diff options
136 files changed, 6184 insertions, 1296 deletions
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 a588ef4bfe..791a7c4a61 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -14,9 +14,9 @@ LOCAL_SRC_FILES := dumpstate.cpp utils.cpp LOCAL_MODULE := dumpstate -LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux +LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase # ZipArchive support, the order matters here to get all symbols. -LOCAL_STATIC_LIBRARIES := libziparchive libz libbase libmincrypt +LOCAL_STATIC_LIBRARIES := libziparchive libz libmincrypt LOCAL_HAL_STATIC_LIBRARIES := libdumpstate LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter LOCAL_INIT_RC := dumpstate.rc diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 5c31d6b66d..e9f9e57358 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -54,14 +54,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; @@ -69,10 +69,12 @@ 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. */ @@ -218,6 +220,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); @@ -506,7 +542,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)); @@ -530,18 +566,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; } @@ -586,6 +643,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); @@ -709,8 +767,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); @@ -924,7 +980,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"); @@ -1062,10 +1118,18 @@ int main(int argc, char *argv[]) { /* 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 */ @@ -1132,6 +1196,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 */ @@ -1205,14 +1270,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()); + } } } @@ -1276,6 +1348,9 @@ int main(int argc, char *argv[]) { // Dumps systrace right away, otherwise it will be filled with unnecessary events. dump_systrace(); + // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed. + dump_raft(); + // Invoking the following dumpsys calls before dump_traces() to try and // keep the system stats as close to its initial state as possible. run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL); @@ -1289,6 +1364,10 @@ 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(); @@ -1388,6 +1467,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), @@ -1396,6 +1476,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"); @@ -1421,9 +1502,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 4769974e0d..5e083cc002 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 09c2e7f74a..fd6413d562 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -38,6 +38,8 @@ #include <sys/prctl.h> #define LOG_TAG "dumpstate" + +#include <android-base/file.h> #include <cutils/debugger.h> #include <cutils/log.h> #include <cutils/properties.h> @@ -1212,6 +1214,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", @@ -1246,52 +1253,38 @@ time_t get_mtime(int fd, time_t default_mtime) { } void dump_emmc_ecsd(const char *ext_csd_path) { - static const size_t EXT_CSD_REV = 192; - static const size_t EXT_PRE_EOL_INFO = 267; - static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268; - static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269; + // List of interesting offsets struct hex { char str[2]; - } buffer[512]; - int fd, ext_csd_rev, ext_pre_eol_info; - ssize_t bytes_read; - static const char *ver_str[] = { - "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" - }; - static const char *eol_str[] = { - "Undefined", - "Normal", - "Warning (consumed 80% of reserve)", - "Urgent (consumed 90% of reserve)" }; + static const size_t EXT_CSD_REV = 192 * sizeof(hex); + static const size_t EXT_PRE_EOL_INFO = 267 * sizeof(hex); + static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268 * sizeof(hex); + static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269 * sizeof(hex); - printf("------ %s Extended CSD ------\n", ext_csd_path); - - fd = TEMP_FAILURE_RETRY(open(ext_csd_path, - O_RDONLY | O_NONBLOCK | O_CLOEXEC)); - if (fd < 0) { - printf("*** %s: %s\n\n", ext_csd_path, strerror(errno)); + std::string buffer; + if (!android::base::ReadFileToString(ext_csd_path, &buffer)) { return; } - bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - close(fd); - if (bytes_read < 0) { - printf("*** %s: %s\n\n", ext_csd_path, strerror(errno)); - return; - } - if (bytes_read < (ssize_t)(EXT_CSD_REV * sizeof(struct hex))) { - printf("*** %s: truncated content %zd\n\n", ext_csd_path, bytes_read); + printf("------ %s Extended CSD ------\n", ext_csd_path); + + if (buffer.length() < (EXT_CSD_REV + sizeof(hex))) { + printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); return; } - ext_csd_rev = 0; - if (sscanf(buffer[EXT_CSD_REV].str, "%02x", &ext_csd_rev) != 1) { - printf("*** %s: EXT_CSD_REV parse error \"%.2s\"\n\n", - ext_csd_path, buffer[EXT_CSD_REV].str); + int ext_csd_rev = 0; + std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex)); + if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) { + printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", + ext_csd_path, sub.c_str()); return; } + static const char *ver_str[] = { + "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" + }; printf("rev 1.%d (MMC %s)\n", ext_csd_rev, (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? @@ -1302,17 +1295,25 @@ void dump_emmc_ecsd(const char *ext_csd_path) { return; } - if (bytes_read < (ssize_t)(EXT_PRE_EOL_INFO * sizeof(struct hex))) { - printf("*** %s: truncated content %zd\n\n", ext_csd_path, bytes_read); + if (buffer.length() < (EXT_PRE_EOL_INFO + sizeof(hex))) { + printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); return; } - ext_pre_eol_info = 0; - if (sscanf(buffer[EXT_PRE_EOL_INFO].str, "%02x", &ext_pre_eol_info) != 1) { - printf("*** %s: PRE_EOL_INFO parse error \"%.2s\"\n\n", - ext_csd_path, buffer[EXT_PRE_EOL_INFO].str); + int ext_pre_eol_info = 0; + sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex)); + if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) { + printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", + ext_csd_path, sub.c_str()); return; } + + static const char *eol_str[] = { + "Undefined", + "Normal", + "Warning (consumed 80% of reserve)", + "Urgent (consumed 90% of reserve)" + }; printf("PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info, eol_str[(ext_pre_eol_info < (int) @@ -1321,7 +1322,7 @@ void dump_emmc_ecsd(const char *ext_csd_path) { for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A; lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B; - ++lifetime) { + lifetime += sizeof(hex)) { int ext_device_life_time_est; static const char *est_str[] = { "Undefined", @@ -1338,21 +1339,24 @@ void dump_emmc_ecsd(const char *ext_csd_path) { "Exceeded the maximum estimated device lifetime", }; - if (bytes_read < (ssize_t)(lifetime * sizeof(struct hex))) { - printf("*** %s: truncated content %zd\n", ext_csd_path, bytes_read); + if (buffer.length() < (lifetime + sizeof(hex))) { + printf("*** %s: truncated content %zu\n", ext_csd_path, buffer.length()); break; } ext_device_life_time_est = 0; - if (sscanf(buffer[lifetime].str, "%02x", &ext_device_life_time_est) != 1) { - printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%.2s\"\n", + sub = buffer.substr(lifetime, sizeof(hex)); + if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) { + printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path, - (unsigned)(lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) + 'A', - buffer[lifetime].str); + (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / + sizeof(hex)) + 'A', + sub.c_str()); continue; } printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n", - (unsigned)(lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) + 'A', + (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / + sizeof(hex)) + 'A', ext_device_life_time_est, est_str[(ext_device_life_time_est < (int) (sizeof(est_str) / sizeof(est_str[0]))) ? diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 7e5bbc578b..d19e98a10e 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 d35099a837..86df596c64 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -96,6 +96,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) @@ -104,9 +115,9 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_SRC_FILES := otapreopt_script.sh -# Let this depend on otapreopt and the chroot tool, so we just have to mention one in a -# configuration. -LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot +# Let this depend on otapreopt, the chroot tool and the slot script, so we just have to mention one +# in a configuration. +LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot include $(BUILD_PREBUILT) diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index 2a9950a4e6..2014e993e3 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <inttypes.h> +#include <regex> #include <stdlib.h> #include <sys/capability.h> #include <sys/file.h> @@ -28,9 +29,9 @@ #include <sys/xattr.h> #include <unistd.h> +#include <android-base/logging.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <cutils/fs.h> #include <cutils/log.h> // TODO: Move everything to base/logging. @@ -43,12 +44,14 @@ #include <globals.h> #include <installd_deps.h> +#include <otapreopt_utils.h> #include <utils.h> #ifndef LOG_TAG #define LOG_TAG "installd" #endif +using android::base::EndsWith; using android::base::StringPrintf; namespace android { @@ -57,6 +60,26 @@ namespace installd { static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; +static constexpr const char* PKG_LIB_POSTFIX = "/lib"; +static constexpr const char* CACHE_DIR_POSTFIX = "/cache"; +static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; + +static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; +static constexpr const char* IDMAP_SUFFIX = "@idmap"; + +// NOTE: keep in sync with StorageManager +static constexpr int FLAG_STORAGE_DE = 1 << 0; +static constexpr int FLAG_STORAGE_CE = 1 << 1; + +// NOTE: keep in sync with Installer +static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; +static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; + +/* dexopt needed flags matching those in dalvik.system.DexFile */ +static constexpr int DEXOPT_DEX2OAT_NEEDED = 1; +static constexpr int DEXOPT_PATCHOAT_NEEDED = 2; +static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3; + #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M typedef int fd_t; @@ -76,30 +99,47 @@ static std::string create_primary_profile(const std::string& profile_dir) { return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME); } +static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, + const char* pkgname, const char* seinfo) { + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) { + PLOG(ERROR) << "Failed to prepare " << path; + return -1; + } + if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) { + PLOG(ERROR) << "Failed to setfilecon " << path; + return -1; + } + return 0; +} + +static int prepare_app_dir(const std::string& parent, const char* name, mode_t target_mode, + uid_t uid, const char* pkgname, const char* seinfo) { + return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid, pkgname, + seinfo); +} + int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version) { uid_t uid = multiuser_get_uid(userid, appid); - int target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; + mode_t target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid, userid, pkgname); - if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) { - PLOG(ERROR) << "Failed to prepare " << path; + if (prepare_app_dir(path, target_mode, uid, pkgname, seinfo) || + prepare_app_dir(path, "cache", 0771, uid, pkgname, seinfo) || + prepare_app_dir(path, "code_cache", 0771, uid, pkgname, seinfo)) { return -1; } - if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) { - PLOG(ERROR) << "Failed to setfilecon " << path; + + // Remember inode numbers of cache directories so that we can clear + // contents while CE storage is locked + if (write_path_inode(path, "cache", kXattrInodeCache) || + write_path_inode(path, "code_cache", kXattrInodeCodeCache)) { return -1; } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid, userid, pkgname); - if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) == -1) { - PLOG(ERROR) << "Failed to prepare " << path; - // TODO: include result once 25796509 is fixed - return 0; - } - if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) { - PLOG(ERROR) << "Failed to setfilecon " << path; + if (prepare_app_dir(path, target_mode, uid, pkgname, seinfo)) { // TODO: include result once 25796509 is fixed return 0; } @@ -243,24 +283,29 @@ int clear_app_profiles(const char* pkgname) { int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode) { - std::string suffix = ""; - bool only_cache = false; - if (flags & FLAG_CLEAR_CACHE_ONLY) { - suffix = CACHE_DIR_POSTFIX; - only_cache = true; - } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { - suffix = CODE_CACHE_DIR_POSTFIX; - only_cache = true; - } - int res = 0; if (flags & FLAG_STORAGE_CE) { - auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode) + suffix; + auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode); + if (flags & FLAG_CLEAR_CACHE_ONLY) { + path = read_path_inode(path, "cache", kXattrInodeCache); + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + path = read_path_inode(path, "code_cache", kXattrInodeCodeCache); + } if (access(path.c_str(), F_OK) == 0) { res |= delete_dir_contents(path); } } if (flags & FLAG_STORAGE_DE) { + std::string suffix = ""; + bool only_cache = false; + if (flags & FLAG_CLEAR_CACHE_ONLY) { + suffix = CACHE_DIR_POSTFIX; + only_cache = true; + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + suffix = CODE_CACHE_DIR_POSTFIX; + only_cache = true; + } + auto path = create_data_user_de_package_path(uuid, userid, pkgname) + suffix; if (access(path.c_str(), F_OK) == 0) { // TODO: include result once 25796509 is fixed @@ -604,14 +649,9 @@ int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, i } int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) { - struct stat buf; - memset(&buf, 0, sizeof(buf)); if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid, userid, pkgname); - if (stat(path.c_str(), &buf) == 0) { - *inode = buf.st_ino; - return 0; - } + return get_path_inode(path, inode); } return -1; } @@ -754,6 +794,16 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ sprintf(image_format_arg, "--image-format=%s", app_image_format); } + char dex2oat_large_app_threshold[kPropertyValueMax]; + bool have_dex2oat_large_app_threshold = + get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0; + char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax]; + if (have_dex2oat_large_app_threshold) { + sprintf(dex2oat_large_app_threshold_arg, + "--very-large-app-threshold=%s", + dex2oat_large_app_threshold); + } + static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; static const char* RUNTIME_ARG = "--runtime-arg"; @@ -854,7 +904,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ + (have_app_image_format ? 1 : 0) + dex2oat_flags_count + (profile_fd == -1 ? 0 : 1) - + (shared_libraries != nullptr ? 4 : 0)]; + + (shared_libraries != nullptr ? 4 : 0) + + (have_dex2oat_large_app_threshold ? 1 : 0)]; int i = 0; argv[i++] = DEX2OAT_BIN; argv[i++] = zip_fd_arg; @@ -897,6 +948,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ if (have_app_image_format) { argv[i++] = image_format_arg; } + if (have_dex2oat_large_app_threshold) { + argv[i++] = dex2oat_large_app_threshold_arg; + } if (dex2oat_flags_count) { i += split(dex2oat_flags, argv + i); } @@ -1313,13 +1367,29 @@ bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) return true; } -static void trim_extension(char* path) { - // Trim the extension. - int pos = strlen(path); - for (; pos >= 0 && path[pos] != '.'; --pos) {} - if (pos >= 0) { - path[pos] = '\0'; // Trim extension +// Translate the given oat path to an art (app image) path. An empty string +// denotes an error. +static std::string create_image_filename(const std::string& oat_path) { + // A standard dalvik-cache entry. Replace ".dex" with ".art." + if (EndsWith(oat_path, ".dex")) { + std::string art_path = oat_path; + art_path.replace(art_path.length() - strlen("dex"), strlen("dex"), "art"); + CHECK(EndsWith(art_path, ".art")); + return art_path; } + + // An odex entry. Not that this may not be an extension, e.g., in the OTA + // case (where the base name will have an extension for the B artifact). + size_t odex_pos = oat_path.rfind(".odex"); + if (odex_pos != std::string::npos) { + std::string art_path = oat_path; + art_path.replace(odex_pos, strlen(".odex"), ".art"); + CHECK_NE(art_path.find(".art"), std::string::npos); + return art_path; + } + + // Don't know how to handle this. + return ""; } static bool add_extension_to_file_name(char* file_name, const char* extension) { @@ -1330,7 +1400,7 @@ static bool add_extension_to_file_name(char* file_name, const char* extension) { return true; } -static int open_output_file(char* file_name, bool recreate, int permissions) { +static int open_output_file(const char* file_name, bool recreate, int permissions) { int flags = O_RDWR | O_CREAT; if (recreate) { if (unlink(file_name) < 0) { @@ -1388,34 +1458,132 @@ bool merge_profiles(uid_t uid, const char *pkgname) { return analyse_profiles(uid, pkgname); } +static const char* parse_null(const char* arg) { + if (strcmp(arg, "!") == 0) { + return nullptr; + } else { + return arg; + } +} + +int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) { + return dexopt(params[0], // apk_path + atoi(params[1]), // uid + params[2], // pkgname + params[3], // instruction_set + atoi(params[4]), // dexopt_needed + params[5], // oat_dir + atoi(params[6]), // dexopt_flags + params[7], // compiler_filter + parse_null(params[8]), // volume_uuid + parse_null(params[9])); // shared_libraries + static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count"); +} + +// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor +// on destruction. It will also run the given cleanup (unless told not to) after closing. +// +// Usage example: +// +// Dex2oatFileWrapper<std::function<void ()>> file(open(...), +// [name]() { +// unlink(name.c_str()); +// }); +// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the +// wrapper if captured as a reference. +// +// if (file.get() == -1) { +// // Error opening... +// } +// +// ... +// if (error) { +// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run +// // and delete the file (after the fd is closed). +// return -1; +// } +// +// (Success case) +// file.SetCleanup(false); +// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run +// // (leaving the file around; after the fd is closed). +// +template <typename Cleanup> +class Dex2oatFileWrapper { + public: + Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) { + } + + Dex2oatFileWrapper(int value, Cleanup cleanup) + : value_(value), cleanup_(cleanup), do_cleanup_(true) {} + + ~Dex2oatFileWrapper() { + reset(-1); + } + + int get() { + return value_; + } + + void SetCleanup(bool cleanup) { + do_cleanup_ = cleanup; + } + + void reset(int new_value) { + if (value_ >= 0) { + close(value_); + } + if (do_cleanup_ && cleanup_ != nullptr) { + cleanup_(); + } + + value_ = new_value; + } + + void reset(int new_value, Cleanup new_cleanup) { + if (value_ >= 0) { + close(value_); + } + if (do_cleanup_ && cleanup_ != nullptr) { + cleanup_(); + } + + value_ = new_value; + cleanup_ = new_cleanup; + } + + private: + int value_; + Cleanup cleanup_; + bool do_cleanup_; +}; + int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries) { - struct utimbuf ut; - struct stat input_stat; - char out_path[PKG_PATH_MAX]; - char swap_file_name[PKG_PATH_MAX]; - char image_path[PKG_PATH_MAX]; - const char *input_file; - char in_odex_path[PKG_PATH_MAX]; - int res; - fd_t input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1; bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0); bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0; bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0; bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0; bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0; + // Don't use profile for vm_safe_mode. b/30688277 + profile_guided = profile_guided && !vm_safe_mode; + CHECK(pkgname != nullptr); CHECK(pkgname[0] != 0); - fd_t reference_profile_fd = -1; // Public apps should not be compiled with profile information ever. Same goes for the special // package '*' used for the system server. + Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd; if (!is_public && pkgname[0] != '*') { // Open reference profile in read only mode as dex2oat does not get write permissions. - reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false); + const std::string pkgname_str(pkgname); + reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false), + [pkgname_str]() { + clear_reference_profile(pkgname_str.c_str()); + }); // Note: it's OK to not find a profile here. } @@ -1423,10 +1591,13 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins LOG_FATAL("dexopt flags contains unknown fields\n"); } + char out_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) { return false; } + const char *input_file; + char in_odex_path[PKG_PATH_MAX]; switch (dexopt_needed) { case DEXOPT_DEX2OAT_NEEDED: input_file = apk_path; @@ -1445,35 +1616,41 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins default: ALOGE("Invalid dexopt needed: %d\n", dexopt_needed); - exit(72); + return 72; } + struct stat input_stat; memset(&input_stat, 0, sizeof(input_stat)); stat(input_file, &input_stat); - input_fd = open(input_file, O_RDONLY, 0); - if (input_fd < 0) { + base::unique_fd input_fd(open(input_file, O_RDONLY, 0)); + if (input_fd.get() < 0) { ALOGE("installd cannot open '%s' for input during dexopt\n", input_file); return -1; } - out_fd = open_output_file(out_path, /*recreate*/true, /*permissions*/0644); - if (out_fd < 0) { + const std::string out_path_str(out_path); + Dex2oatFileWrapper<std::function<void ()>> out_fd( + open_output_file(out_path, /*recreate*/true, /*permissions*/0644), + [out_path_str]() { unlink(out_path_str.c_str()); }); + if (out_fd.get() < 0) { ALOGE("installd cannot open '%s' for output during dexopt\n", out_path); - goto fail; + return -1; } - if (!set_permissions_and_ownership(out_fd, is_public, uid, out_path)) { - goto fail; + if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) { + return -1; } // Create a swap file if necessary. + base::unique_fd swap_fd; if (ShouldUseSwapFileForDexopt()) { // Make sure there really is enough space. + char swap_file_name[PKG_PATH_MAX]; strcpy(swap_file_name, out_path); if (add_extension_to_file_name(swap_file_name, ".swap")) { - swap_fd = open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600); + swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600)); } - if (swap_fd < 0) { + if (swap_fd.get() < 0) { // Could not create swap file. Optimistically go on and hope that we can compile // without it. ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name); @@ -1486,111 +1663,108 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins } // Avoid generating an app image for extract only since it will not contain any classes. - strcpy(image_path, out_path); - trim_extension(image_path); - if (add_extension_to_file_name(image_path, ".art")) { - char app_image_format[kPropertyValueMax]; - bool have_app_image_format = - get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0; - // Use app images only if it is enabled (by a set image format) and we are compiling - // profile-guided (so the app image doesn't conservatively contain all classes). - if (profile_guided && have_app_image_format) { - // Recreate is true since we do not want to modify a mapped image. If the app is already - // running and we modify the image file, it can cause crashes (b/27493510). - image_fd = open_output_file(image_path, /*recreate*/true, /*permissions*/0600); - if (image_fd < 0) { - // Could not create application image file. Go on since we can compile without it. - ALOGE("installd could not create '%s' for image file during dexopt\n", image_path); - } else if (!set_permissions_and_ownership(image_fd, is_public, uid, image_path)) { - image_fd = -1; - } - } - // If we have a valid image file path but no image fd, erase the image file. - if (image_fd < 0) { - if (unlink(image_path) < 0) { - if (errno != ENOENT) { - PLOG(ERROR) << "Couldn't unlink image file " << image_path; - } - } - } + Dex2oatFileWrapper<std::function<void ()>> image_fd; + const std::string image_path = create_image_filename(out_path); + if (!image_path.empty()) { + char app_image_format[kPropertyValueMax]; + bool have_app_image_format = + get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0; + // Use app images only if it is enabled (by a set image format) and we are compiling + // profile-guided (so the app image doesn't conservatively contain all classes). + if (profile_guided && have_app_image_format) { + // Recreate is true since we do not want to modify a mapped image. If the app is + // already running and we modify the image file, it can cause crashes (b/27493510). + image_fd.reset(open_output_file(image_path.c_str(), + true /*recreate*/, + 0600 /*permissions*/), + [image_path]() { unlink(image_path.c_str()); } + ); + if (image_fd.get() < 0) { + // Could not create application image file. Go on since we can compile without + // it. + LOG(ERROR) << "installd could not create '" + << image_path + << "' for image file during dexopt"; + } else if (!set_permissions_and_ownership(image_fd.get(), + is_public, + uid, + image_path.c_str())) { + image_fd.reset(-1); + } + } + // If we have a valid image file path but no image fd, explicitly erase the image file. + if (image_fd.get() < 0) { + if (unlink(image_path.c_str()) < 0) { + if (errno != ENOENT) { + PLOG(ERROR) << "Couldn't unlink image file " << image_path; + } + } + } } ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file); - pid_t pid; - pid = fork(); + pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); SetDex2OatAndPatchOatScheduling(boot_complete); - if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { + if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) { ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); - exit(67); + _exit(67); } if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) { - run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set); + run_patchoat(input_fd.get(), + out_fd.get(), + input_file, + out_path, + pkgname, + instruction_set); } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) { // Pass dex2oat the relative path to the input file. const char *input_file_name = get_location_from_path(input_file); - run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd, - instruction_set, compiler_filter, vm_safe_mode, debuggable, boot_complete, - reference_profile_fd, shared_libraries); + run_dex2oat(input_fd.get(), + out_fd.get(), + image_fd.get(), + input_file_name, + out_path, + swap_fd.get(), + instruction_set, + compiler_filter, + vm_safe_mode, + debuggable, + boot_complete, + reference_profile_fd.get(), + shared_libraries); } else { ALOGE("Invalid dexopt needed: %d\n", dexopt_needed); - exit(73); + _exit(73); } - exit(68); /* only get here on exec failure */ + _exit(68); /* only get here on exec failure */ } else { - res = wait_child(pid); + int res = wait_child(pid); if (res == 0) { ALOGV("DexInv: --- END '%s' (success) ---\n", input_file); } else { ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res); - goto fail; + return -1; } } + struct utimbuf ut; ut.actime = input_stat.st_atime; ut.modtime = input_stat.st_mtime; utime(out_path, &ut); - close(out_fd); - close(input_fd); - if (swap_fd >= 0) { - close(swap_fd); - } - if (reference_profile_fd >= 0) { - close(reference_profile_fd); - } - if (image_fd >= 0) { - close(image_fd); - } - return 0; + // We've been successful, don't delete output. + out_fd.SetCleanup(false); + image_fd.SetCleanup(false); + reference_profile_fd.SetCleanup(false); -fail: - if (out_fd >= 0) { - close(out_fd); - unlink(out_path); - } - if (input_fd >= 0) { - close(input_fd); - } - if (reference_profile_fd >= 0) { - close(reference_profile_fd); - // We failed to compile. Unlink the reference profile. Current profiles are already unlinked - // when profmoan advises compilation. - clear_reference_profile(pkgname); - } - if (swap_fd >= 0) { - close(swap_fd); - } - if (image_fd >= 0) { - close(image_fd); - } - return -1; + return 0; } int mark_boot_complete(const char* instruction_set) @@ -1926,11 +2100,59 @@ static bool unlink_and_rename(const char* from, const char* to) { return true; } +// Move/rename a B artifact (from) to an A artifact (to). +static bool move_ab_path(const std::string& b_path, const std::string& a_path) { + // Check whether B exists. + { + struct stat s; + if (stat(b_path.c_str(), &s) != 0) { + // Silently ignore for now. The service calling this isn't smart enough to understand + // lack of artifacts at the moment. + return false; + } + if (!S_ISREG(s.st_mode)) { + LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file."; + // Try to unlink, but swallow errors. + unlink(b_path.c_str()); + return false; + } + } + + // Rename B to A. + if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) { + // Delete the b_path so we don't try again (or fail earlier). + if (unlink(b_path.c_str()) != 0) { + PLOG(ERROR) << "Could not unlink " << b_path; + } + + return false; + } + + return true; +} + int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) { if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) { LOG(ERROR) << "Cannot move_ab with null input"; return -1; } + + // Get the current slot suffix. No suffix, no A/B. + std::string slot_suffix; + { + char buf[kPropertyValueMax]; + if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) { + return -1; + } + slot_suffix = buf; + + if (!ValidateTargetSlotSuffix(slot_suffix)) { + LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; + return -1; + } + } + + // Validate other inputs. if (validate_apk_path(apk_path) != 0) { LOG(ERROR) << "invalid apk_path " << apk_path; return -1; @@ -1944,37 +2166,37 @@ int move_ab(const char* apk_path, const char* instruction_set, const char* oat_d if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) { return -1; } + const std::string a_image_path = create_image_filename(a_path); - // B path = A path + ".b" - std::string b_path = StringPrintf("%s.b", a_path); + // B path = A path + slot suffix. + const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str()); + const std::string b_image_path = StringPrintf("%s.%s", + a_image_path.c_str(), + slot_suffix.c_str()); - // Check whether B exists. - { - struct stat s; - if (stat(b_path.c_str(), &s) != 0) { - // Silently ignore for now. The service calling this isn't smart enough to understand - // lack of artifacts at the moment. - return -1; - } - if (!S_ISREG(s.st_mode)) { - LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file."; - // Try to unlink, but swallow errors. - unlink(b_path.c_str()); - return -1; - } - } + bool oat_success = move_ab_path(b_path, a_path); + bool success; - // Rename B to A. - if (!unlink_and_rename(b_path.c_str(), a_path)) { - // Delete the b_path so we don't try again (or fail earlier). - if (unlink(b_path.c_str()) != 0) { - PLOG(ERROR) << "Could not unlink " << b_path; + if (oat_success) { + // Note: we can live without an app image. As such, ignore failure to move the image file. + // If we decide to require the app image, or the app image being moved correctly, + // then change accordingly. + constexpr bool kIgnoreAppImageFailure = true; + + bool art_success = true; + if (!a_image_path.empty()) { + art_success = move_ab_path(b_image_path, a_image_path); } - return -1; + success = art_success || kIgnoreAppImageFailure; + } else { + // Cleanup: delete B image, ignore errors. + unlink(b_image_path.c_str()); + + success = false; } - return 0; + return success ? 0 : -1; } } // namespace installd diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h index 7a42c5caf7..e990f1b695 100644 --- a/cmds/installd/commands.h +++ b/cmds/installd/commands.h @@ -28,6 +28,8 @@ namespace android { namespace installd { +static constexpr size_t DEXOPT_PARAM_COUNT = 10U; + int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version); int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags, @@ -56,9 +58,21 @@ bool merge_profiles(uid_t uid, const char *pkgname); bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files); -int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set, - int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, - const char* volume_uuid, const char* shared_libraries); +int dexopt(const char *apk_path, + uid_t uid, + const char *pkgName, + const char *instruction_set, + int dexopt_needed, + const char* oat_dir, + int dexopt_flags, + const char* compiler_filter, + const char* volume_uuid, + const char* shared_libraries); +static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size"); + +// Helper for the above, converting arguments. +int dexopt(const char* const params[DEXOPT_PARAM_COUNT]); + int mark_boot_complete(const char *instruction_set); int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId); int idmap(const char *target_path, const char *overlay_path, uid_t uid); diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp index 6a67e29396..93e1ce54af 100644 --- a/cmds/installd/globals.cpp +++ b/cmds/installd/globals.cpp @@ -30,6 +30,22 @@ namespace android { namespace installd { +static constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA + +static constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA + +static constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under + // ANDROID_DATA + +static constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA + +static constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA + +static constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA + +static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under + // ANDROID_DATA + /* Directory records that are used in execution of commands. */ dir_rec_t android_app_dir; dir_rec_t android_app_ephemeral_dir; @@ -77,7 +93,7 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { // Get the android ephemeral app directory. if (copy_and_append(&android_app_ephemeral_dir, &android_data_dir, EPHEMERAL_APP_SUBDIR) < 0) { - return -1; + return false; } // Get the android app native library directory. @@ -86,7 +102,7 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { } // Get the sd-card ASEC mount point. - if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) { + if (get_path_from_env(&android_asec_dir, ASEC_MOUNTPOINT_ENV_NAME) < 0) { return false; } diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h index 3e523460ad..c90beec49c 100644 --- a/cmds/installd/globals.h +++ b/cmds/installd/globals.h @@ -23,6 +23,11 @@ namespace android { namespace installd { +/* constants */ + +// Name of the environment variable that contains the asec mountpoint. +static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT"; + /* data structures */ struct dir_rec_t { diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 061359ea43..facbc724ec 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -219,7 +219,8 @@ static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSE // We use otapreopt_chroot to get into the chroot. static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot"; -static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { +static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT], + char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { // Time to fork and run otapreopt. // Check that the tool exists. @@ -231,12 +232,14 @@ static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { pid_t pid = fork(); if (pid == 0) { - const char* argv[1 + 9 + 1]; + const char* argv[1 + DEXOPT_PARAM_COUNT + 1]; argv[0] = kOtaPreopt; - for (size_t i = 1; i <= 9; ++i) { - argv[i] = arg[i - 1]; + + for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) { + argv[i + 1] = args[i]; } - argv[10] = nullptr; + + argv[DEXOPT_PARAM_COUNT + 1] = nullptr; execv(argv[0], (char * const *)argv); PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed"; @@ -252,22 +255,30 @@ static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { } } +static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT], + char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { + return dexopt(args); +} + +using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT], + char reply[REPLY_MAX]); + static int do_dexopt(char **arg, char reply[REPLY_MAX]) { + const char* args[DEXOPT_PARAM_COUNT]; + for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) { + CHECK(arg[i] != nullptr); + args[i] = arg[i]; + } + int dexopt_flags = atoi(arg[6]); + DexoptFn dexopt_fn; if ((dexopt_flags & DEXOPT_OTA) != 0) { - return do_ota_dexopt(arg, reply); + dexopt_fn = do_ota_dexopt; + } else { + dexopt_fn = do_regular_dexopt; } - return dexopt(arg[0], // apk_path - atoi(arg[1]), // uid - arg[2], // pkgname - arg[3], // instruction_set - atoi(arg[4]), // dexopt_needed - arg[5], // oat_dir - dexopt_flags, - arg[7], // compiler_filter - parse_null(arg[8]), // volume_uuid - parse_null(arg[9])); // shared_libraries + return dexopt_fn(args, reply); } static int do_merge_profiles(char **arg, char reply[REPLY_MAX]) @@ -546,7 +557,7 @@ done: return 0; } -bool initialize_globals() { +static bool initialize_globals() { const char* data_path = getenv("ANDROID_DATA"); if (data_path == nullptr) { ALOGE("Could not find ANDROID_DATA"); diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h index 8513695ef3..41732cce4f 100644 --- a/cmds/installd/installd_constants.h +++ b/cmds/installd/installd_constants.h @@ -26,51 +26,14 @@ namespace installd { constexpr const char* PRIMARY_USER_PREFIX = "data/"; constexpr const char* SECONDARY_USER_PREFIX = "user/"; -constexpr const char* PKG_DIR_POSTFIX = ""; - -constexpr const char* PKG_LIB_POSTFIX = "/lib"; - -constexpr const char* CACHE_DIR_POSTFIX = "/cache"; -constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; - -constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA -constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA -constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under ANDROID_DATA - -constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA - -constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA - -constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA - -/* other handy constants */ - -constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA - // This is used as a string literal, can't be constants. TODO: std::string... #define DALVIK_CACHE "dalvik-cache" constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex"; constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex"; -constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; -constexpr const char* IDMAP_SUFFIX = "@idmap"; - constexpr size_t PKG_NAME_MAX = 128u; /* largest allowed package name */ constexpr size_t PKG_PATH_MAX = 256u; /* max size of any path we use */ -// NOTE: keep in sync with StorageManager -constexpr int FLAG_STORAGE_DE = 1 << 0; -constexpr int FLAG_STORAGE_CE = 1 << 1; - -// NOTE: keep in sync with Installer -constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; -constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; - -/* dexopt needed flags matching those in dalvik.system.DexFile */ -constexpr int DEXOPT_DEX2OAT_NEEDED = 1; -constexpr int DEXOPT_PATCHOAT_NEEDED = 2; -constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3; - /**************************************************************************** * IMPORTANT: These values are passed from Java code. Keep them in sync with * frameworks/base/services/core/java/com/android/server/pm/Installer.java diff --git a/cmds/installd/installd_deps.h b/cmds/installd/installd_deps.h index 5ff46e694f..509317803e 100644 --- a/cmds/installd/installd_deps.h +++ b/cmds/installd/installd_deps.h @@ -57,9 +57,6 @@ extern bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set); -// Initialize globals. May be implemented with the helper in globals.h. -extern bool initialize_globals(); - } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index ac511ec8dd..5fa972a4de 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -30,6 +30,7 @@ #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <cutils/fs.h> #include <cutils/log.h> #include <cutils/properties.h> @@ -39,7 +40,7 @@ #include <file_parsing.h> #include <globals.h> #include <installd_deps.h> // Need to fill in requirements of commands. -#include <string_helpers.h> +#include <otapreopt_utils.h> #include <system_properties.h> #include <utils.h> @@ -51,16 +52,15 @@ #define TOKEN_MAX 16 /* max number of arguments in buffer */ #define REPLY_MAX 256 /* largest reply allowed */ +using android::base::EndsWith; +using android::base::Join; +using android::base::Split; +using android::base::StartsWith; using android::base::StringPrintf; namespace android { namespace installd { -static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; -static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; -static constexpr const char* kOTARootDirectory = "/system-b"; -static constexpr size_t kISAIndex = 3; - template<typename T> static constexpr T RoundDown(T x, typename std::decay<T>::type n) { return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n); @@ -73,8 +73,6 @@ static constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) { class OTAPreoptService { public: - static constexpr const char* kOTADataDirectory = "/data/ota"; - // Main driver. Performs the following steps. // // 1) Parse options (read system properties etc from B partition). @@ -87,26 +85,31 @@ class OTAPreoptService { // // 5) Run update. int Main(int argc, char** argv) { + if (!ReadArguments(argc, argv)) { + LOG(ERROR) << "Failed reading command line."; + return 1; + } + if (!ReadSystemProperties()) { LOG(ERROR)<< "Failed reading system properties."; - return 1; + return 2; } if (!ReadEnvironment()) { LOG(ERROR) << "Failed reading environment properties."; - return 2; + return 3; } - if (!ReadPackage(argc, argv)) { - LOG(ERROR) << "Failed reading command line file."; - return 3; + if (!CheckAndInitializeInstalldGlobals()) { + LOG(ERROR) << "Failed initializing globals."; + return 4; } PrepareEnvironment(); - if (!PrepareBootImage()) { + if (!PrepareBootImage(/* force */ false)) { LOG(ERROR) << "Failed preparing boot image."; - return 4; + return 5; } int dexopt_retcode = RunPreopt(); @@ -114,7 +117,7 @@ class OTAPreoptService { return dexopt_retcode; } - int GetProperty(const char* key, char* value, const char* default_value) { + int GetProperty(const char* key, char* value, const char* default_value) const { const std::string* prop_value = system_properties_.GetProperty(key); if (prop_value == nullptr) { if (default_value == nullptr) { @@ -131,7 +134,16 @@ class OTAPreoptService { return static_cast<int>(size); } + std::string GetOTADataDirectory() const { + return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str()); + } + + const std::string& GetTargetSlot() const { + return target_slot_; + } + private: + bool ReadSystemProperties() { static constexpr const char* kPropertyFiles[] = { "/default.prop", "/system/build.prop" @@ -173,27 +185,106 @@ private: return false; } - // Check that we found important properties. - constexpr const char* kRequiredProperties[] = { - kBootClassPathPropertyName, kAndroidRootPathPropertyName - }; - for (size_t i = 0; i < arraysize(kRequiredProperties); ++i) { - if (system_properties_.GetProperty(kRequiredProperties[i]) == nullptr) { - return false; - } + if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) { + return false; + } + android_data_ = *system_properties_.GetProperty(kAndroidDataPathPropertyName); + + if (system_properties_.GetProperty(kAndroidRootPathPropertyName) == nullptr) { + return false; + } + android_root_ = *system_properties_.GetProperty(kAndroidRootPathPropertyName); + + if (system_properties_.GetProperty(kBootClassPathPropertyName) == nullptr) { + return false; + } + boot_classpath_ = *system_properties_.GetProperty(kBootClassPathPropertyName); + + if (system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) == nullptr) { + return false; } + asec_mountpoint_ = *system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME); return true; } - bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) { + const std::string& GetAndroidData() const { + return android_data_; + } + + const std::string& GetAndroidRoot() const { + return android_root_; + } + + const std::string GetOtaDirectoryPrefix() const { + return GetAndroidData() + "/ota"; + } + + bool CheckAndInitializeInstalldGlobals() { + // init_globals_from_data_and_root requires "ASEC_MOUNTPOINT" in the environment. We + // do not use any datapath that includes this, but we'll still have to set it. + CHECK(system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) != nullptr); + int result = setenv(ASEC_MOUNTPOINT_ENV_NAME, asec_mountpoint_.c_str(), 0); + if (result != 0) { + LOG(ERROR) << "Could not set ASEC_MOUNTPOINT environment variable"; + return false; + } + + if (!init_globals_from_data_and_root(GetAndroidData().c_str(), GetAndroidRoot().c_str())) { + LOG(ERROR) << "Could not initialize globals; exiting."; + return false; + } + + // This is different from the normal installd. We only do the base + // directory, the rest will be created on demand when each app is compiled. + if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) { + LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix(); + return false; + } + + return true; + } + + bool ReadArguments(int argc ATTRIBUTE_UNUSED, char** argv) { + // Expected command line: + // target-slot dexopt {DEXOPT_PARAMETERS} + // The DEXOPT_PARAMETERS are passed on to dexopt(), so we expect DEXOPT_PARAM_COUNT + // of them. We store them in package_parameters_ (size checks are done when + // parsing the special parameters and when copying into package_parameters_. + + static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_), + "Unexpected dexopt param count"); + + const char* target_slot_arg = argv[1]; + if (target_slot_arg == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + // Sanitize value. Only allow (a-zA-Z0-9_)+. + target_slot_ = target_slot_arg; + if (!ValidateTargetSlotSuffix(target_slot_)) { + LOG(ERROR) << "Target slot suffix not legal: " << target_slot_; + return false; + } + + // Check for "dexopt" next. + if (argv[2] == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + if (std::string("dexopt").compare(argv[2]) != 0) { + LOG(ERROR) << "Second parameter not dexopt: " << argv[2]; + return false; + } + + // Copy the rest into package_parameters_, but be careful about over- and underflow. size_t index = 0; - while (index < ARRAY_SIZE(package_parameters_) && - argv[index + 1] != nullptr) { - package_parameters_[index] = argv[index + 1]; + while (index < DEXOPT_PARAM_COUNT && + argv[index + 3] != nullptr) { + package_parameters_[index] = argv[index + 3]; index++; } - if (index != ARRAY_SIZE(package_parameters_)) { + if (index != ARRAY_SIZE(package_parameters_) || argv[index + 3] != nullptr) { LOG(ERROR) << "Wrong number of parameters"; return false; } @@ -202,15 +293,9 @@ private: } void PrepareEnvironment() { - CHECK(system_properties_.GetProperty(kBootClassPathPropertyName) != nullptr); - const std::string& boot_cp = - *system_properties_.GetProperty(kBootClassPathPropertyName); - environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_cp.c_str())); - environ_.push_back(StringPrintf("ANDROID_DATA=%s", kOTADataDirectory)); - CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr); - const std::string& android_root = - *system_properties_.GetProperty(kAndroidRootPathPropertyName); - environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root.c_str())); + environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str())); + environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str())); + environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str())); for (const std::string& e : environ_) { putenv(const_cast<char*>(e.c_str())); @@ -219,7 +304,7 @@ private: // Ensure that we have the right boot image. The first time any app is // compiled, we'll try to generate it. - bool PrepareBootImage() { + bool PrepareBootImage(bool force) const { if (package_parameters_[kISAIndex] == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; @@ -227,44 +312,114 @@ private: const char* isa = package_parameters_[kISAIndex]; // Check whether the file exists where expected. - std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE; + std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE; std::string isa_path = dalvik_cache + "/" + isa; std::string art_path = isa_path + "/system@framework@boot.art"; std::string oat_path = isa_path + "/system@framework@boot.oat"; - if (access(art_path.c_str(), F_OK) == 0 && - access(oat_path.c_str(), F_OK) == 0) { - // Files exist, assume everything is alright. - return true; + bool cleared = false; + if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) { + // Files exist, assume everything is alright if not forced. Otherwise clean up. + if (!force) { + return true; + } + ClearDirectory(isa_path); + cleared = true; } + // Reset umask in otapreopt, so that we control the the access for the files we create. + umask(0); + // Create the directories, if necessary. if (access(dalvik_cache.c_str(), F_OK) != 0) { - if (mkdir(dalvik_cache.c_str(), 0711) != 0) { - PLOG(ERROR) << "Could not create dalvik-cache dir"; + if (!CreatePath(dalvik_cache)) { + PLOG(ERROR) << "Could not create dalvik-cache dir " << dalvik_cache; return false; } } if (access(isa_path.c_str(), F_OK) != 0) { - if (mkdir(isa_path.c_str(), 0711) != 0) { + if (!CreatePath(isa_path)) { PLOG(ERROR) << "Could not create dalvik-cache isa dir"; return false; } } // Prepare to create. - // TODO: Delete files, just for a blank slate. - const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName); + if (!cleared) { + ClearDirectory(isa_path); + } std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { return PatchoatBootImage(art_path, isa); } else { // No preopted boot image. Try to compile. - return Dex2oatBootImage(boot_cp, art_path, oat_path, isa); + return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa); } } - bool PatchoatBootImage(const std::string& art_path, const char* isa) { + static bool CreatePath(const std::string& path) { + // Create the given path. Use string processing instead of dirname, as dirname's need for + // a writable char buffer is painful. + + // First, try to use the full path. + if (mkdir(path.c_str(), 0711) == 0) { + return true; + } + if (errno != ENOENT) { + PLOG(ERROR) << "Could not create path " << path; + return false; + } + + // Now find the parent and try that first. + size_t last_slash = path.find_last_of('/'); + if (last_slash == std::string::npos || last_slash == 0) { + PLOG(ERROR) << "Could not create " << path; + return false; + } + + if (!CreatePath(path.substr(0, last_slash))) { + return false; + } + + if (mkdir(path.c_str(), 0711) == 0) { + return true; + } + PLOG(ERROR) << "Could not create " << path; + return false; + } + + static void ClearDirectory(const std::string& dir) { + DIR* c_dir = opendir(dir.c_str()); + if (c_dir == nullptr) { + PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents"; + return; + } + + for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) { + const char* name = de->d_name; + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + // We only want to delete regular files and symbolic links. + std::string file = StringPrintf("%s/%s", dir.c_str(), name); + if (de->d_type != DT_REG && de->d_type != DT_LNK) { + LOG(WARNING) << "Unexpected file " + << file + << " of type " + << std::hex + << de->d_type + << " encountered."; + } else { + // Try to unlink the file. + if (unlink(file.c_str()) != 0) { + PLOG(ERROR) << "Unable to unlink " << file; + } + } + } + CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; + } + + bool PatchoatBootImage(const std::string& art_path, const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; @@ -290,12 +445,12 @@ private: bool Dex2oatBootImage(const std::string& boot_cp, const std::string& art_path, const std::string& oat_path, - const char* isa) { + const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; cmd.push_back("/system/bin/dex2oat"); cmd.push_back(StringPrintf("--image=%s", art_path.c_str())); - for (const std::string& boot_part : Split(boot_cp, ':')) { + for (const std::string& boot_part : Split(boot_cp, ":")) { cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str())); } cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str())); @@ -324,7 +479,7 @@ private: const std::string* extra_opts = system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags"); if (extra_opts != nullptr) { - std::vector<std::string> extra_vals = Split(*extra_opts, ' '); + std::vector<std::string> extra_vals = Split(*extra_opts, " "); cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end()); } // TODO: Should we lower this? It's usually set close to max, because @@ -356,18 +511,81 @@ private: return (strcmp(arg, "!") == 0) ? nullptr : arg; } + bool ShouldSkipPreopt() const { + // There's one thing we have to be careful about: we may/will be asked to compile an app + // living in the system image. This may be a valid request - if the app wasn't compiled, + // e.g., if the system image wasn't large enough to include preopted files. However, the + // data we have is from the old system, so the driver (the OTA service) can't actually + // know. Thus, we will get requests for apps that have preopted components. To avoid + // duplication (we'd generate files that are not used and are *not* cleaned up), do two + // simple checks: + // + // 1) Does the apk_path start with the value of ANDROID_ROOT? (~in the system image) + // (For simplicity, assume the value of ANDROID_ROOT does not contain a symlink.) + // + // 2) If you replace the name in the apk_path with "oat," does the path exist? + // (=have a subdirectory for preopted files) + // + // If the answer to both is yes, skip the dexopt. + // + // Note: while one may think it's OK to call dexopt and it will fail (because APKs should + // be stripped), that's not true for APKs signed outside the build system (so the + // jar content must be exactly the same). + + // (This is ugly as it's the only thing where we need to understand the contents + // of package_parameters_, but it beats postponing the decision or using the call- + // backs to do weird things.) + constexpr size_t kApkPathIndex = 0; + CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex); + CHECK(package_parameters_[kApkPathIndex] != nullptr); + if (StartsWith(package_parameters_[kApkPathIndex], android_root_.c_str())) { + const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/'); + if (last_slash != nullptr) { + std::string path(package_parameters_[kApkPathIndex], + last_slash - package_parameters_[kApkPathIndex] + 1); + CHECK(EndsWith(path, "/")); + path = path + "oat"; + if (access(path.c_str(), F_OK) == 0) { + return true; + } + } + } + + // Another issue is unavailability of files in the new system. If the partition + // layout changes, otapreopt_chroot may not know about this. Then files from that + // partition will not be available and fail to build. This is problematic, as + // this tool will wipe the OTA artifact cache and try again (for robustness after + // a failed OTA with remaining cache artifacts). + if (access(package_parameters_[kApkPathIndex], F_OK) != 0) { + LOG(WARNING) << "Skipping preopt of non-existing package " + << package_parameters_[kApkPathIndex]; + return true; + } + + return false; + } + int RunPreopt() { - int ret = dexopt(package_parameters_[0], // apk_path - atoi(package_parameters_[1]), // uid - package_parameters_[2], // pkgname - package_parameters_[3], // instruction_set - atoi(package_parameters_[4]), // dexopt_needed - package_parameters_[5], // oat_dir - atoi(package_parameters_[6]), // dexopt_flags - package_parameters_[7], // compiler_filter - ParseNull(package_parameters_[8]), // volume_uuid - ParseNull(package_parameters_[9])); // shared_libraries - return ret; + if (ShouldSkipPreopt()) { + return 0; + } + + int dexopt_result = dexopt(package_parameters_); + if (dexopt_result == 0) { + return 0; + } + + // If the dexopt failed, we may have a stale boot image from a previous OTA run. + // Try to delete and retry. + + if (!PrepareBootImage(/* force */ true)) { + LOG(ERROR) << "Forced boot image creating failed. Original error return was " + << dexopt_result; + return dexopt_result; + } + + LOG(WARNING) << "Original dexopt failed, re-trying after boot image was regenerated."; + return dexopt(package_parameters_); } //////////////////////////////////// @@ -375,8 +593,8 @@ private: //////////////////////////////////// // Wrapper on fork/execv to run a command in a subprocess. - bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { - const std::string command_line(Join(arg_vector, ' ')); + static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { + const std::string command_line = Join(arg_vector, ' '); CHECK_GE(arg_vector.size(), 1U) << command_line; @@ -465,9 +683,8 @@ private: void AddCompilerOptionFromSystemProperty(const char* system_property, const char* prefix, bool runtime, - std::vector<std::string>& out) { - const std::string* value = - system_properties_.GetProperty(system_property); + std::vector<std::string>& out) const { + const std::string* value = system_properties_.GetProperty(system_property); if (value != nullptr) { if (runtime) { out.push_back("--runtime-arg"); @@ -480,11 +697,25 @@ private: } } + static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; + static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; + static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; + // The index of the instruction-set string inside the package parameters. Needed for + // some special-casing that requires knowledge of the instruction-set. + static constexpr size_t kISAIndex = 3; + // Stores the system properties read out of the B partition. We need to use these properties // to compile, instead of the A properties we could get from init/get_property. SystemProperties system_properties_; - const char* package_parameters_[10]; + // Some select properties that are always needed. + std::string target_slot_; + std::string android_root_; + std::string android_data_; + std::string boot_classpath_; + std::string asec_mountpoint_; + + const char* package_parameters_[DEXOPT_PARAM_COUNT]; // Store environment values we need to set. std::vector<std::string> environ_; @@ -497,7 +728,6 @@ OTAPreoptService gOps; //////////////////////// int get_property(const char *key, char *value, const char *default_value) { - // TODO: Replace with system-properties map. return gOps.GetProperty(key, value, default_value); } @@ -505,9 +735,8 @@ int get_property(const char *key, char *value, const char *default_value) { bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set) { - // TODO: Insert B directory. - char *file_name_start; - char *file_name_end; + const char *file_name_start; + const char *file_name_end; file_name_start = strrchr(apk_path, '/'); if (file_name_start == nullptr) { @@ -526,8 +755,13 @@ bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, std::string file_name(file_name_start, file_name_len); // <apk_parent_dir>/oat/<isa>/<file_name>.odex.b - snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.b", oat_dir, instruction_set, - file_name.c_str()); + snprintf(path, + PKG_PATH_MAX, + "%s/%s/%s.odex.%s", + oat_dir, + instruction_set, + file_name.c_str(), + gOps.GetTargetSlot().c_str()); return true; } @@ -539,11 +773,6 @@ bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, */ bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { - if (StringPrintf("%soat/%s/odex.b", apk_path, instruction_set).length() + 1 > PKG_PATH_MAX) { - ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path); - return false; - } - const char *path_end = strrchr(apk_path, '/'); if (path_end == nullptr) { ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); @@ -559,11 +788,15 @@ bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, } std::string name_component(name_begin, extension_start - name_begin); - std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b", + std::string new_path = StringPrintf("%s/oat/%s/%s.odex.%s", path_component.c_str(), instruction_set, - name_component.c_str()); - CHECK_LT(new_path.length(), PKG_PATH_MAX); + name_component.c_str(), + gOps.GetTargetSlot().c_str()); + if (new_path.length() >= PKG_PATH_MAX) { + LOG(ERROR) << "apk_path of " << apk_path << " is too long: " << new_path; + return false; + } strcpy(path, new_path.c_str()); return true; } @@ -586,7 +819,7 @@ bool create_cache_path(char path[PKG_PATH_MAX], std::replace(from_src.begin(), from_src.end(), '/', '@'); std::string assembled_path = StringPrintf("%s/%s/%s/%s%s", - OTAPreoptService::kOTADataDirectory, + gOps.GetOTADataDirectory().c_str(), DALVIK_CACHE, instruction_set, from_src.c_str(), @@ -600,27 +833,6 @@ bool create_cache_path(char path[PKG_PATH_MAX], return true; } -bool initialize_globals() { - const char* data_path = getenv("ANDROID_DATA"); - if (data_path == nullptr) { - ALOGE("Could not find ANDROID_DATA"); - return false; - } - return init_globals_from_data_and_root(data_path, kOTARootDirectory); -} - -static bool initialize_directories() { - // This is different from the normal installd. We only do the base - // directory, the rest will be created on demand when each app is compiled. - mode_t old_umask = umask(0); - LOG(INFO) << "Old umask: " << old_umask; - if (access(OTAPreoptService::kOTADataDirectory, R_OK) < 0) { - ALOGE("Could not access %s\n", OTAPreoptService::kOTADataDirectory); - return false; - } - return true; -} - static int log_callback(int type, const char *fmt, ...) { va_list ap; int priority; @@ -648,8 +860,6 @@ static int otapreopt_main(const int argc, char *argv[]) { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(argv); - ALOGI("otapreopt firing up\n"); - if (argc < 2) { ALOGE("Expecting parameters"); exit(1); @@ -659,16 +869,6 @@ static int otapreopt_main(const int argc, char *argv[]) { cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); - if (!initialize_globals()) { - ALOGE("Could not initialize globals; exiting.\n"); - exit(1); - } - - if (!initialize_directories()) { - ALOGE("Could not create directories; exiting.\n"); - exit(1); - } - if (selinux_enabled && selinux_status_open(true) < 0) { ALOGE("Could not open selinux status; exiting.\n"); exit(1); diff --git a/cmds/installd/otapreopt.rc b/cmds/installd/otapreopt.rc new file mode 100644 index 0000000000..059ae752e7 --- /dev/null +++ b/cmds/installd/otapreopt.rc @@ -0,0 +1,8 @@ +# When /data is available, look for A/B artifacts for the current slot and move them +# into the dalvik-cache (relabeling them). +on post-fs-data + exec - root -- /system/bin/otapreopt_slot + # The dalvik-cache was not moved itself, so as to restrict the rights of otapreopt_slot. + # But now the relabeling is annoying as there is no force option available here. So + # explicitly list all the ISAs we know. + restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/mips /data/dalvik-cache/mips64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64 diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index f7f69a90f4..5ea89e6842 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -14,14 +14,20 @@ ** limitations under the License. */ +#include <fcntl.h> #include <linux/unistd.h> #include <sys/mount.h> #include <sys/wait.h> +#include <sstream> + #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> +#include <commands.h> +#include <otapreopt_utils.h> + #ifndef LOG_TAG #define LOG_TAG "otapreopt" #endif @@ -31,7 +37,37 @@ using android::base::StringPrintf; namespace android { namespace installd { +static void CloseDescriptor(int fd) { + if (fd >= 0) { + int result = close(fd); + UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor + // that we do *not* want. + } +} + +static void CloseDescriptor(const char* descriptor_string) { + int fd = -1; + std::istringstream stream(descriptor_string); + stream >> fd; + if (!stream.fail()) { + CloseDescriptor(fd); + } +} + +// Entry for otapreopt_chroot. Expected parameters are: +// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] +// The file descriptor denoted by status-fd will be closed. The rest of the parameters will +// be passed on to otapreopt in the chroot. static int otapreopt_chroot(const int argc, char **arg) { + // Close all file descriptors. They are coming from the caller, we do not want to pass them + // on across our fork/exec into a different domain. + // 1) Default descriptors. + CloseDescriptor(STDIN_FILENO); + CloseDescriptor(STDOUT_FILENO); + CloseDescriptor(STDERR_FILENO); + // 2) The status channel. + CloseDescriptor(arg[1]); + // We need to run the otapreopt tool from the postinstall partition. As such, set up a // mount namespace and change root. @@ -59,6 +95,28 @@ static int otapreopt_chroot(const int argc, char **arg) { } } + // Try to mount the vendor partition. update_engine doesn't do this for us, but we + // want it for vendor APKs. + // Notes: + // 1) We pretty much guess a name here and hope to find the partition by name. + // It is just as complicated and brittle to scan /proc/mounts. But this requires + // validating the target-slot so as not to try to mount some totally random path. + // 2) We're in a mount namespace here, so when we die, this will be cleaned up. + // 3) Ignore errors. Printing anything at this stage will open a file descriptor + // for logging. + if (!ValidateTargetSlotSuffix(arg[2])) { + LOG(ERROR) << "Target slot suffix not legal: " << arg[2]; + exit(207); + } + std::string vendor_partition = StringPrintf("/dev/block/bootdevice/by-name/vendor%s", + arg[2]); + int vendor_result = mount(vendor_partition.c_str(), + "/postinstall/vendor", + "ext4", + MS_RDONLY, + /* data */ nullptr); + UNUSED(vendor_result); + // Chdir into /postinstall. if (chdir("/postinstall") != 0) { PLOG(ERROR) << "Unable to chdir into /postinstall."; @@ -78,13 +136,42 @@ static int otapreopt_chroot(const int argc, char **arg) { // Now go on and run otapreopt. - const char* argv[1 + 9 + 1]; - CHECK_EQ(argc, 10); + // Incoming: cmd + status-fd + target-slot + "dexopt" + dexopt-params + null + // Outgoing: cmd + target-slot + "dexopt" + dexopt-params + null + constexpr size_t kInArguments = 1 // Binary name. + + 1 // status file descriptor. + + 1 // target-slot. + + 1 // "dexopt." + + DEXOPT_PARAM_COUNT // dexopt parameters. + + 1; // null termination. + constexpr size_t kOutArguments = 1 // Binary name. + + 1 // target-slot. + + 1 // "dexopt." + + DEXOPT_PARAM_COUNT // dexopt parameters. + + 1; // null termination. + const char* argv[kOutArguments]; + if (static_cast<size_t>(argc) != kInArguments - 1 /* null termination */) { + LOG(ERROR) << "Unexpected argument size " + << argc + << " vs " + << (kInArguments - 1); + for (size_t i = 0; i < static_cast<size_t>(argc); ++i) { + if (arg[i] == nullptr) { + LOG(ERROR) << "(null)"; + } else { + LOG(ERROR) << "\"" << arg[i] << "\""; + } + } + exit(206); + } argv[0] = "/system/bin/otapreopt"; - for (size_t i = 1; i <= 9; ++i) { - argv[i] = arg[i]; + + // The first parameter is the status file descriptor, skip. + + for (size_t i = 1; i <= kOutArguments - 2 /* cmd + null */; ++i) { + argv[i] = arg[i + 1]; } - argv[10] = nullptr; + argv[kOutArguments - 1] = nullptr; execv(argv[0], (char * const *)argv); PLOG(ERROR) << "execv(OTAPREOPT) failed."; diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index a31734a651..f950276090 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -18,24 +18,62 @@ # This script will run as a postinstall step to drive otapreopt. +TARGET_SLOT="$1" +STATUS_FD="$2" + # Maximum number of packages/steps. MAXIMUM_PACKAGES=1000 -PREPARE=$(cmd otadexopt prepare) -if [ "$PREPARE" != "Success" ] ; then - echo "Failed to prepare." +# First ensure the system is booted. This is to work around issues when cmd would +# infinitely loop trying to get a service manager (which will never come up in that +# mode). b/30797145 +BOOT_PROPERTY_NAME="dev.bootcomplete" + +BOOT_COMPLETE=$(getprop $BOOT_PROPERTY_NAME) +if [ "$BOOT_COMPLETE" != "1" ] ; then + echo "Error: boot-complete not detected." + # We must return 0 to not block sideload. + exit 0 +fi + + +# Compute target slot suffix. +# TODO: Once bootctl is not restricted, we should query from there. Or get this from +# update_engine as a parameter. +if [ "$TARGET_SLOT" = "0" ] ; then + TARGET_SLOT_SUFFIX="_a" +elif [ "$TARGET_SLOT" = "1" ] ; then + TARGET_SLOT_SUFFIX="_b" +else + echo "Unknown target slot $TARGET_SLOT" exit 1 fi + +PREPARE=$(cmd otadexopt prepare) +# Note: Ignore preparation failures. Step and done will fail and exit this. +# This is necessary to support suspends - the OTA service will keep +# the state around for us. + +PROGRESS=$(cmd otadexopt progress) +print -u${STATUS_FD} "global_progress $PROGRESS" + i=0 while ((i<MAXIMUM_PACKAGES)) ; do - cmd otadexopt step + DEXOPT_PARAMS=$(cmd otadexopt next) + + /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&- + + PROGRESS=$(cmd otadexopt progress) + print -u${STATUS_FD} "global_progress $PROGRESS" + DONE=$(cmd otadexopt done) - if [ "$DONE" = "OTA complete." ] ; then - break + if [ "$DONE" = "OTA incomplete." ] ; then + sleep 1 + i=$((i+1)) + continue fi - sleep 1 - i=$((i+1)) + break done DONE=$(cmd otadexopt done) @@ -45,6 +83,7 @@ else echo "Complete or error." fi +print -u${STATUS_FD} "global_progress 1.0" cmd otadexopt cleanup exit 0 diff --git a/cmds/installd/otapreopt_slot.sh b/cmds/installd/otapreopt_slot.sh new file mode 100644 index 0000000000..b5786e9a28 --- /dev/null +++ b/cmds/installd/otapreopt_slot.sh @@ -0,0 +1,39 @@ +#!/system/bin/sh + +# +# 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. +# + +# This script will move artifacts for the currently active slot. + +SLOT_SUFFIX=$(getprop ro.boot.slot_suffix) +if test -n "$SLOT_SUFFIX" ; then + if test -d /data/ota/$SLOT_SUFFIX/dalvik-cache ; then + log -p i -t otapreopt_slot "Moving A/B artifacts for slot ${SLOT_SUFFIX}." + OLD_SIZE=$(du -h -s /data/dalvik-cache) + rm -rf /data/dalvik-cache/* + NEW_SIZE=$(du -h -s /data/ota/$SLOT_SUFFIX/dalvik-cache) + mv /data/ota/$SLOT_SUFFIX/dalvik-cache/* /data/dalvik-cache/ + rmdir /data/ota/$SLOT_SUFFIX/dalvik-cache + rmdir /data/ota/$SLOT_SUFFIX + log -p i -t otapreopt_slot "Moved ${NEW_SIZE} over ${OLD_SIZE}" + else + log -p i -t otapreopt_slot "No A/B artifacts found for slot ${SLOT_SUFFIX}." + fi + exit 0 +else + log -p w -t otapreopt_slot "Slot property empty." + exit 1 +fi diff --git a/cmds/installd/otapreopt_utils.h b/cmds/installd/otapreopt_utils.h new file mode 100644 index 0000000000..436e554944 --- /dev/null +++ b/cmds/installd/otapreopt_utils.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OTAPREOPT_UTILS_H_ +#define OTAPREOPT_UTILS_H_ + +#include <regex> + +namespace android { +namespace installd { + +static inline bool ValidateTargetSlotSuffix(const std::string& input) { + std::regex slot_suffix_regex("[a-zA-Z0-9_]+"); + std::smatch slot_suffix_match; + return std::regex_match(input, slot_suffix_match, slot_suffix_regex); +} + +} // namespace installd +} // namespace android + +#endif // OTAPREOPT_UTILS_H_ diff --git a/cmds/installd/string_helpers.h b/cmds/installd/string_helpers.h deleted file mode 100644 index e8fcdef9cf..0000000000 --- a/cmds/installd/string_helpers.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_OTAPREOPT_STRING_HELPERS_H_ -#define ART_OTAPREOPT_STRING_HELPERS_H_ - -#include <sstream> -#include <string> - -#include <android-base/macros.h> - -namespace android { -namespace installd { - -static inline bool StringStartsWith(const std::string& target, - const char* prefix) { - return target.compare(0, strlen(prefix), prefix) == 0; -} - -// Split the input according to the separator character. Doesn't honor quotation. -static inline std::vector<std::string> Split(const std::string& in, const char separator) { - if (in.empty()) { - return std::vector<std::string>(); - } - - std::vector<std::string> ret; - std::stringstream strstr(in); - std::string token; - - while (std::getline(strstr, token, separator)) { - ret.push_back(token); - } - - return ret; -} - -template <typename StringT> -static inline std::string Join(const std::vector<StringT>& strings, char separator) { - if (strings.empty()) { - return ""; - } - - std::string result(strings[0]); - for (size_t i = 1; i < strings.size(); ++i) { - result += separator; - result += strings[i]; - } - return result; -} - -} // namespace installd -} // namespace android - -#endif // ART_OTAPREOPT_STRING_HELPERS_H_ diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index c83899360b..674f760e52 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -21,6 +21,7 @@ #include <stdlib.h> #include <sys/stat.h> #include <sys/wait.h> +#include <sys/xattr.h> #if defined(__APPLE__) #include <sys/mount.h> @@ -39,7 +40,9 @@ #ifndef LOG_TAG #define LOG_TAG "installd" #endif + #define CACHE_NOISY(x) //x +#define DEBUG_XATTRS 0 using android::base::StringPrintf; @@ -105,10 +108,12 @@ std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t u while ((ent = readdir(dir))) { if (ent->d_ino == ce_data_inode) { auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name); +#if DEBUG_XATTRS if (resolved != fallback) { LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode << " instead of " << fallback; } +#endif closedir(dir); return resolved; } @@ -551,7 +556,7 @@ static void* _cache_malloc(cache_t* cache, size_t len) if (res == NULL) { return NULL; } - CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %d", res, len)); + CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %zu", res, len)); // Link it into our list of blocks, not disrupting the current one. if (cache->memBlocks == NULL) { *(void**)res = NULL; @@ -576,7 +581,7 @@ static void* _cache_malloc(cache_t* cache, size_t len) cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE; nextPos = res + len; } - CACHE_NOISY(ALOGI("cache_malloc: ret %p size %d, block=%p, nextPos=%p", + CACHE_NOISY(ALOGI("cache_malloc: ret %p size %zu, block=%p, nextPos=%p", res, len, cache->memBlocks, nextPos)); cache->curMemBlockAvail = nextPos; return res; @@ -654,7 +659,7 @@ static cache_file_t* _add_cache_file_t(cache_t* cache, cache_dir_t* dir, time_t cache->availFiles = newAvail; cache->files = newFiles; } - CACHE_NOISY(ALOGI("Setting file %p at position %d in array %p", file, + CACHE_NOISY(ALOGI("Setting file %p at position %zd in array %p", file, cache->numFiles, cache->files)); cache->files[cache->numFiles] = file; cache->numFiles++; @@ -779,6 +784,99 @@ static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char * return 0; } +int get_path_inode(const std::string& path, ino_t *inode) { + struct stat buf; + memset(&buf, 0, sizeof(buf)); + if (stat(path.c_str(), &buf) != 0) { + PLOG(WARNING) << "Failed to stat " << path; + return -1; + } else { + *inode = buf.st_ino; + return 0; + } +} + +/** + * Write the inode of a specific child file into the given xattr on the + * parent directory. This allows you to find the child later, even if its + * name is encrypted. + */ +int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr) { + ino_t inode = 0; + uint64_t inode_raw = 0; + auto path = StringPrintf("%s/%s", parent.c_str(), name); + + if (get_path_inode(path, &inode) != 0) { + // Path probably doesn't exist yet; ignore + return 0; + } + + // Check to see if already set correctly + if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) { + if (inode_raw == inode) { + // Already set correctly; skip writing + return 0; + } else { + PLOG(WARNING) << "Mismatched inode value; found " << inode + << " on disk but marked value was " << inode_raw << "; overwriting"; + } + } + + inode_raw = inode; + if (setxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw), 0) != 0 && errno != EOPNOTSUPP) { + PLOG(ERROR) << "Failed to write xattr " << inode_xattr << " at " << parent; + return -1; + } else { + return 0; + } +} + +/** + * Read the inode of a specific child file from the given xattr on the + * parent directory. Returns a currently valid path for that child, which + * might have an encrypted name. + */ +std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr) { + ino_t inode = 0; + uint64_t inode_raw = 0; + auto fallback = StringPrintf("%s/%s", parent.c_str(), name); + + // Lookup the inode value written earlier + if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) { + inode = inode_raw; + } + + // For testing purposes, rely on the inode when defined; this could be + // optimized to use access() in the future. + if (inode != 0) { + DIR* dir = opendir(parent.c_str()); + if (dir == nullptr) { + PLOG(ERROR) << "Failed to opendir " << parent; + return fallback; + } + + struct dirent* ent; + while ((ent = readdir(dir))) { + if (ent->d_ino == inode) { + auto resolved = StringPrintf("%s/%s", parent.c_str(), ent->d_name); +#if DEBUG_XATTRS + if (resolved != fallback) { + LOG(DEBUG) << "Resolved path " << resolved << " for inode " << inode + << " instead of " << fallback; + } +#endif + closedir(dir); + return resolved; + } + } + LOG(WARNING) << "Failed to resolve inode " << inode << "; using " << fallback; + closedir(dir); + return fallback; + } else { + return fallback; + } +} + void add_cache_files(cache_t* cache, const std::string& data_path) { DIR *d; struct dirent *de; @@ -796,7 +894,6 @@ void add_cache_files(cache_t* cache, const std::string& data_path) { if (de->d_type == DT_DIR) { DIR* subdir; const char *name = de->d_name; - char* pathpos; /* always skip "." and ".." */ if (name[0] == '.') { @@ -804,16 +901,9 @@ void add_cache_files(cache_t* cache, const std::string& data_path) { if ((name[1] == '.') && (name[2] == 0)) continue; } - strcpy(dirname, basepath); - pathpos = dirname + strlen(dirname); - if ((*(pathpos-1)) != '/') { - *pathpos = '/'; - pathpos++; - *pathpos = 0; - } - - // TODO: also try searching using xattr when CE is locked - snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/cache", name); + auto parent = StringPrintf("%s/%s", basepath, name); + auto resolved = read_path_inode(parent, "cache", kXattrInodeCache); + strcpy(dirname, resolved.c_str()); CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname)); subdir = opendir(dirname); @@ -931,16 +1021,16 @@ void finish_cache_collection(cache_t* cache) { CACHE_NOISY(size_t i;) - CACHE_NOISY(ALOGI("clear_cache_files: %d dirs, %d files\n", cache->numDirs, cache->numFiles)); + CACHE_NOISY(ALOGI("clear_cache_files: %zu dirs, %zu files\n", cache->numDirs, cache->numFiles)); CACHE_NOISY( for (i=0; i<cache->numDirs; i++) { cache_dir_t* dir = cache->dirs[i]; - ALOGI("dir #%d: %p %s parent=%p\n", i, dir, dir->name, dir->parent); + ALOGI("dir #%zu: %p %s parent=%p\n", i, dir, dir->name, dir->parent); }) CACHE_NOISY( for (i=0; i<cache->numFiles; i++) { cache_file_t* file = cache->files[i]; - ALOGI("file #%d: %p %s time=%d dir=%p\n", i, file, file->name, + ALOGI("file #%zu: %p %s time=%d dir=%p\n", i, file, file->name, (int)file->modTime, file->dir); }) void* block = cache->memBlocks; diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 60df356019..8123e9b28d 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -62,6 +62,9 @@ typedef struct { int8_t* curMemBlockEnd; } cache_t; +constexpr const char* kXattrInodeCache = "user.inode_cache"; +constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache"; + int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, const char *postfix, @@ -118,6 +121,11 @@ int64_t data_disk_free(const std::string& data_path); cache_t* start_cache_collection(); +int get_path_inode(const std::string& path, ino_t *inode); + +int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr); +std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr); + void add_cache_files(cache_t* cache, const std::string& data_path); void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size); diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml index 4b7a706e47..4ff00b5b3a 100644 --- a/data/etc/wearable_core_hardware.xml +++ b/data/etc/wearable_core_hardware.xml @@ -34,7 +34,6 @@ <!-- device administration --> <feature name="android.software.device_admin" /> - <feature name="android.software.managed_users" /> <!-- devices with GPS must include device/google/clockwork/gps.xml --> <!-- devices with an autofocus camera and/or flash must include either diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 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 09300a20c9..fe4b1fa830 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 3b681ee0da..8ec0546c7e 100644 --- a/include/gui/BufferQueueConsumer.h +++ b/include/gui/BufferQueueConsumer.h @@ -136,6 +136,10 @@ 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; diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h index 35c917044a..82bc121af3 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> @@ -327,6 +328,8 @@ private: // The slot of the last queued buffer int mLastQueuedSlot; + OccupancyTracker mOccupancyTracker; + const uint64_t mUniqueId; }; // class BufferQueueCore diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index 48c3396ce4..838632c26d 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -170,9 +170,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 +183,10 @@ 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; diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h index ba864784d2..0490c3cc5b 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -85,6 +85,10 @@ 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(); 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/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h index 69fe51ef93..62e3877259 100644 --- a/include/gui/GraphicBufferAlloc.h +++ b/include/gui/GraphicBufferAlloc.h @@ -35,7 +35,7 @@ public: virtual ~GraphicBufferAlloc(); virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t usage, - status_t* error); + std::string requestorName, status_t* error) override; }; diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h index 3f39799564..1efcf3cfb5 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/IGraphicBufferAlloc.h b/include/gui/IGraphicBufferAlloc.h index f3c46ec2ed..600cf27c4c 100644 --- a/include/gui/IGraphicBufferAlloc.h +++ b/include/gui/IGraphicBufferAlloc.h @@ -21,14 +21,15 @@ #include <sys/types.h> #include <binder/IInterface.h> +#include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> #include <utils/RefBase.h> +#include <string> + namespace android { // ---------------------------------------------------------------------------- -class GraphicBuffer; - class IGraphicBufferAlloc : public IInterface { public: @@ -37,7 +38,13 @@ public: /* Create a new GraphicBuffer for the client to use. */ virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h, - PixelFormat format, uint32_t usage, status_t* error) = 0; + PixelFormat format, uint32_t usage, std::string requestorName, + status_t* error) = 0; + + sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h, + PixelFormat format, uint32_t usage, status_t* error) { + return createGraphicBuffer(w, h, format, usage, "<Unknown>", error); + } }; // ---------------------------------------------------------------------------- diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h index f03d1ce4c2..3b10d78b58 100644 --- a/include/gui/IGraphicBufferConsumer.h +++ b/include/gui/IGraphicBufferConsumer.h @@ -27,6 +27,7 @@ #include <binder/IInterface.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> +#include <gui/OccupancyTracker.h> #include <EGL/egl.h> #include <EGL/eglext.h> @@ -265,6 +266,12 @@ 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. diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 336ca70746..c62bc5899c 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 { // ---------------------------------------------------------------------------- @@ -359,24 +361,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, @@ -521,9 +528,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 +573,14 @@ public: 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/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h index af26721b35..74a4123bb6 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 bb79bd02b8..c27a741632 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..1d15e7f029 --- /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 bcce14a42e..8177ec66af 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -134,6 +134,12 @@ 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: @@ -185,13 +191,13 @@ 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); @@ -217,6 +223,7 @@ public: 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, @@ -363,7 +370,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 312e02f1dc..b8ee331736 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,13 +150,16 @@ 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); 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 b7012eb32b..0bd14ea3a1 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 99a7622838..88dd585d74 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/Gralloc1.h b/include/ui/Gralloc1.h new file mode 100644 index 0000000000..cf8c173eee --- /dev/null +++ b/include/ui/Gralloc1.h @@ -0,0 +1,238 @@ +/* + * 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_UI_GRALLOC1_H +#define ANDROID_UI_GRALLOC1_H + +#define GRALLOC1_LOG_TAG "Gralloc1" + +#include <ui/Gralloc1On0Adapter.h> + +#include <unordered_set> + +namespace std { + template <> + struct hash<gralloc1_capability_t> { + size_t operator()(gralloc1_capability_t capability) const { + return std::hash<int32_t>()(static_cast<int32_t>(capability)); + } + }; +} + +namespace android { + +class Fence; +class GraphicBuffer; + +namespace Gralloc1 { + +class Device; + +class Descriptor { +public: + Descriptor(Device& device, gralloc1_buffer_descriptor_t deviceId) + : mShimDevice(device), + mDeviceId(deviceId), + mWidth(0), + mHeight(0), + mFormat(static_cast<android_pixel_format_t>(0)), + mProducerUsage(GRALLOC1_PRODUCER_USAGE_NONE), + mConsumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {} + + ~Descriptor(); + + gralloc1_buffer_descriptor_t getDeviceId() const { return mDeviceId; } + + gralloc1_error_t setDimensions(uint32_t width, uint32_t height); + gralloc1_error_t setFormat(android_pixel_format_t format); + gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage); + gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage); + +private: + Device& mShimDevice; + const gralloc1_buffer_descriptor_t mDeviceId; + + uint32_t mWidth; + uint32_t mHeight; + android_pixel_format_t mFormat; + gralloc1_producer_usage_t mProducerUsage; + gralloc1_consumer_usage_t mConsumerUsage; + +}; // Descriptor + +class Device { + friend class Gralloc1::Descriptor; + +public: + Device(gralloc1_device_t* device); + + bool hasCapability(gralloc1_capability_t capability) const; + + std::string dump(); + + std::shared_ptr<Descriptor> createDescriptor(); + + gralloc1_error_t getStride(buffer_handle_t buffer, uint32_t* outStride); + + gralloc1_error_t allocate( + const std::vector<std::shared_ptr<const Descriptor>>& descriptors, + std::vector<buffer_handle_t>* outBuffers); + gralloc1_error_t allocate( + const std::shared_ptr<const Descriptor>& descriptor, + gralloc1_backing_store_t id, buffer_handle_t* outBuffer); + + gralloc1_error_t retain(buffer_handle_t buffer); + gralloc1_error_t retain(const GraphicBuffer* buffer); + + gralloc1_error_t release(buffer_handle_t buffer); + + gralloc1_error_t getNumFlexPlanes(buffer_handle_t buffer, + uint32_t* outNumPlanes); + + gralloc1_error_t lock(buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, void** outData, + const sp<Fence>& acquireFence); + gralloc1_error_t lockFlex(buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, + struct android_flex_layout* outData, const sp<Fence>& acquireFence); + gralloc1_error_t lockYCbCr(buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, struct android_ycbcr* outData, + const sp<Fence>& acquireFence); + + gralloc1_error_t unlock(buffer_handle_t buffer, sp<Fence>* outFence); + +private: + std::unordered_set<gralloc1_capability_t> loadCapabilities(); + + bool loadFunctions(); + + template <typename LockType, typename OutType> + gralloc1_error_t lockHelper(LockType pfn, buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, OutType* outData, + const sp<Fence>& acquireFence) { + int32_t intError = pfn(mDevice, buffer, + static_cast<uint64_t>(producerUsage), + static_cast<uint64_t>(consumerUsage), accessRegion, outData, + acquireFence->dup()); + return static_cast<gralloc1_error_t>(intError); + } + + gralloc1_device_t* const mDevice; + + const std::unordered_set<gralloc1_capability_t> mCapabilities; + + template <typename PFN, gralloc1_function_descriptor_t descriptor> + struct FunctionLoader { + FunctionLoader() : pfn(nullptr) {} + + bool load(gralloc1_device_t* device, bool errorIfNull) { + gralloc1_function_pointer_t rawPointer = + device->getFunction(device, descriptor); + pfn = reinterpret_cast<PFN>(rawPointer); + if (errorIfNull && !rawPointer) { + ALOG(LOG_ERROR, GRALLOC1_LOG_TAG, + "Failed to load function pointer %d", descriptor); + } + return rawPointer != nullptr; + } + + template <typename ...Args> + typename std::result_of<PFN(Args...)>::type operator()(Args... args) { + return pfn(args...); + } + + PFN pfn; + }; + + // Function pointers + struct Functions { + FunctionLoader<GRALLOC1_PFN_DUMP, GRALLOC1_FUNCTION_DUMP> dump; + FunctionLoader<GRALLOC1_PFN_CREATE_DESCRIPTOR, + GRALLOC1_FUNCTION_CREATE_DESCRIPTOR> createDescriptor; + FunctionLoader<GRALLOC1_PFN_DESTROY_DESCRIPTOR, + GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR> destroyDescriptor; + FunctionLoader<GRALLOC1_PFN_SET_CONSUMER_USAGE, + GRALLOC1_FUNCTION_SET_CONSUMER_USAGE> setConsumerUsage; + FunctionLoader<GRALLOC1_PFN_SET_DIMENSIONS, + GRALLOC1_FUNCTION_SET_DIMENSIONS> setDimensions; + FunctionLoader<GRALLOC1_PFN_SET_FORMAT, + GRALLOC1_FUNCTION_SET_FORMAT> setFormat; + FunctionLoader<GRALLOC1_PFN_SET_PRODUCER_USAGE, + GRALLOC1_FUNCTION_SET_PRODUCER_USAGE> setProducerUsage; + FunctionLoader<GRALLOC1_PFN_GET_BACKING_STORE, + GRALLOC1_FUNCTION_GET_BACKING_STORE> getBackingStore; + FunctionLoader<GRALLOC1_PFN_GET_CONSUMER_USAGE, + GRALLOC1_FUNCTION_GET_CONSUMER_USAGE> getConsumerUsage; + FunctionLoader<GRALLOC1_PFN_GET_DIMENSIONS, + GRALLOC1_FUNCTION_GET_DIMENSIONS> getDimensions; + FunctionLoader<GRALLOC1_PFN_GET_FORMAT, + GRALLOC1_FUNCTION_GET_FORMAT> getFormat; + FunctionLoader<GRALLOC1_PFN_GET_PRODUCER_USAGE, + GRALLOC1_FUNCTION_GET_PRODUCER_USAGE> getProducerUsage; + FunctionLoader<GRALLOC1_PFN_GET_STRIDE, + GRALLOC1_FUNCTION_GET_STRIDE> getStride; + FunctionLoader<GRALLOC1_PFN_ALLOCATE, + GRALLOC1_FUNCTION_ALLOCATE> allocate; + FunctionLoader<GRALLOC1_PFN_RETAIN, + GRALLOC1_FUNCTION_RETAIN> retain; + FunctionLoader<GRALLOC1_PFN_RELEASE, + GRALLOC1_FUNCTION_RELEASE> release; + FunctionLoader<GRALLOC1_PFN_GET_NUM_FLEX_PLANES, + GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES> getNumFlexPlanes; + FunctionLoader<GRALLOC1_PFN_LOCK, + GRALLOC1_FUNCTION_LOCK> lock; + FunctionLoader<GRALLOC1_PFN_LOCK_FLEX, + GRALLOC1_FUNCTION_LOCK_FLEX> lockFlex; + FunctionLoader<GRALLOC1_PFN_LOCK_YCBCR, + GRALLOC1_FUNCTION_LOCK_YCBCR> lockYCbCr; + FunctionLoader<GRALLOC1_PFN_UNLOCK, + GRALLOC1_FUNCTION_UNLOCK> unlock; + + // Adapter-only functions + FunctionLoader<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER, + GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER> retainGraphicBuffer; + FunctionLoader<GRALLOC1_PFN_ALLOCATE_WITH_ID, + GRALLOC1_FUNCTION_ALLOCATE_WITH_ID> allocateWithId; + } mFunctions; + +}; // class android::Gralloc1::Device + +class Loader +{ +public: + Loader(); + ~Loader(); + + std::unique_ptr<Device> getDevice(); + +private: + static std::unique_ptr<Gralloc1On0Adapter> mAdapter; + std::unique_ptr<Device> mDevice; +}; + +} // namespace android::Gralloc1 + +} // namespace android + +#endif diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h new file mode 100644 index 0000000000..97c9a89d32 --- /dev/null +++ b/include/ui/Gralloc1On0Adapter.h @@ -0,0 +1,481 @@ +/* + * 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_UI_GRALLOC_1_ON_0_ADAPTER_H +#define ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H + +#include <ui/Fence.h> +#include <ui/GraphicBuffer.h> + +#include <hardware/gralloc1.h> + +#include <mutex> +#include <string> +#include <unordered_map> +#include <vector> + +struct gralloc_module_t; + +// This is not an "official" capability (i.e., it is not found in gralloc1.h), +// but we will use it to detect that we are running through the adapter, which +// is capable of collaborating with GraphicBuffer such that queries on a +// buffer_handle_t succeed +static const auto GRALLOC1_CAPABILITY_ON_ADAPTER = + static_cast<gralloc1_capability_t>(GRALLOC1_LAST_CAPABILITY + 1); + +static const auto GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER = + static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 1); +static const auto GRALLOC1_FUNCTION_ALLOCATE_WITH_ID = + static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 2); +static const auto GRALLOC1_FUNCTION_LOCK_YCBCR = + static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 3); +static const auto GRALLOC1_LAST_ADAPTER_FUNCTION = GRALLOC1_FUNCTION_LOCK_YCBCR; + +typedef gralloc1_error_t (*GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER)( + gralloc1_device_t* device, const android::GraphicBuffer* buffer); +typedef gralloc1_error_t (*GRALLOC1_PFN_ALLOCATE_WITH_ID)( + gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptor, + gralloc1_backing_store_t id, buffer_handle_t* outBuffer); +typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_LOCK_YCBCR)( + gralloc1_device_t* device, buffer_handle_t buffer, + uint64_t /*gralloc1_producer_usage_t*/ producerUsage, + uint64_t /*gralloc1_consumer_usage_t*/ consumerUsage, + const gralloc1_rect_t* accessRegion, struct android_ycbcr* outYCbCr, + int32_t acquireFence); + +namespace android { + +class Gralloc1On0Adapter : public gralloc1_device_t +{ +public: + Gralloc1On0Adapter(const hw_module_t* module); + ~Gralloc1On0Adapter(); + + gralloc1_device_t* getDevice() { + return static_cast<gralloc1_device_t*>(this); + } + +private: + static inline Gralloc1On0Adapter* getAdapter(gralloc1_device_t* device) { + return static_cast<Gralloc1On0Adapter*>(device); + } + + // getCapabilities + + void doGetCapabilities(uint32_t* outCount, + int32_t* /*gralloc1_capability_t*/ outCapabilities); + static void getCapabilitiesHook(gralloc1_device_t* device, + uint32_t* outCount, + int32_t* /*gralloc1_capability_t*/ outCapabilities) { + getAdapter(device)->doGetCapabilities(outCount, outCapabilities); + }; + + // getFunction + + gralloc1_function_pointer_t doGetFunction( + int32_t /*gralloc1_function_descriptor_t*/ descriptor); + static gralloc1_function_pointer_t getFunctionHook( + gralloc1_device_t* device, + int32_t /*gralloc1_function_descriptor_t*/ descriptor) { + return getAdapter(device)->doGetFunction(descriptor); + } + + // dump + + void dump(uint32_t* outSize, char* outBuffer); + static void dumpHook(gralloc1_device_t* device, uint32_t* outSize, + char* outBuffer) { + return getAdapter(device)->dump(outSize, outBuffer); + } + std::string mCachedDump; + + // Buffer descriptor lifecycle functions + + class Descriptor; + + gralloc1_error_t createDescriptor( + gralloc1_buffer_descriptor_t* outDescriptor); + static int32_t createDescriptorHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t* outDescriptor) { + auto error = getAdapter(device)->createDescriptor(outDescriptor); + return static_cast<int32_t>(error); + } + + gralloc1_error_t destroyDescriptor(gralloc1_buffer_descriptor_t descriptor); + static int32_t destroyDescriptorHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptor) { + auto error = getAdapter(device)->destroyDescriptor(descriptor); + return static_cast<int32_t>(error); + } + + // Buffer descriptor modification functions + + struct Descriptor : public std::enable_shared_from_this<Descriptor> { + Descriptor(Gralloc1On0Adapter* adapter, + gralloc1_buffer_descriptor_t id) + : adapter(adapter), + id(id), + width(0), + height(0), + format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), + producerUsage(GRALLOC1_PRODUCER_USAGE_NONE), + consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {} + + gralloc1_error_t setDimensions(uint32_t w, uint32_t h) { + width = w; + height = h; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t setFormat(int32_t f) { + format = f; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) { + producerUsage = usage; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage) { + consumerUsage = usage; + return GRALLOC1_ERROR_NONE; + } + + Gralloc1On0Adapter* const adapter; + const gralloc1_buffer_descriptor_t id; + + uint32_t width; + uint32_t height; + int32_t format; + gralloc1_producer_usage_t producerUsage; + gralloc1_consumer_usage_t consumerUsage; + }; + + template <typename ...Args> + static int32_t callDescriptorFunction(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptorId, + gralloc1_error_t (Descriptor::*member)(Args...), Args... args) { + auto descriptor = getAdapter(device)->getDescriptor(descriptorId); + if (!descriptor) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_DESCRIPTOR); + } + auto error = ((*descriptor).*member)(std::forward<Args>(args)...); + return static_cast<int32_t>(error); + } + + static int32_t setConsumerUsageHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) { + auto usage = static_cast<gralloc1_consumer_usage_t>(intUsage); + return callDescriptorFunction(device, descriptorId, + &Descriptor::setConsumerUsage, usage); + } + + static int32_t setDimensionsHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptorId, uint32_t width, + uint32_t height) { + return callDescriptorFunction(device, descriptorId, + &Descriptor::setDimensions, width, height); + } + + static int32_t setFormatHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptorId, int32_t format) { + return callDescriptorFunction(device, descriptorId, + &Descriptor::setFormat, format); + } + + static int32_t setProducerUsageHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) { + auto usage = static_cast<gralloc1_producer_usage_t>(intUsage); + return callDescriptorFunction(device, descriptorId, + &Descriptor::setProducerUsage, usage); + } + + // Buffer handle query functions + + class Buffer { + public: + Buffer(buffer_handle_t handle, gralloc1_backing_store_t store, + const Descriptor& descriptor, uint32_t stride, + bool wasAllocated); + + buffer_handle_t getHandle() const { return mHandle; } + + void retain() { ++mReferenceCount; } + + // Returns true if the reference count has dropped to 0, indicating that + // the buffer needs to be released + bool release() { return --mReferenceCount == 0; } + + bool wasAllocated() const { return mWasAllocated; } + + gralloc1_error_t getBackingStore( + gralloc1_backing_store_t* outStore) const { + *outStore = mStore; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t getConsumerUsage( + gralloc1_consumer_usage_t* outUsage) const { + *outUsage = mDescriptor.consumerUsage; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t getDimensions(uint32_t* outWidth, + uint32_t* outHeight) const { + *outWidth = mDescriptor.width; + *outHeight = mDescriptor.height; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t getFormat(int32_t* outFormat) const { + *outFormat = mDescriptor.format; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t getNumFlexPlanes(uint32_t* outNumPlanes) const { + // TODO: This is conservative, and we could do better by examining + // the format, but it won't hurt anything for now + *outNumPlanes = 4; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t getProducerUsage( + gralloc1_producer_usage_t* outUsage) const { + *outUsage = mDescriptor.producerUsage; + return GRALLOC1_ERROR_NONE; + } + + gralloc1_error_t getStride(uint32_t* outStride) const { + *outStride = mStride; + return GRALLOC1_ERROR_NONE; + } + + private: + + const buffer_handle_t mHandle; + size_t mReferenceCount; + + // Since we're adapting to gralloc0, there will always be a 1:1 + // correspondence between buffer handles and backing stores, and the + // backing store ID will be the same as the GraphicBuffer unique ID + const gralloc1_backing_store_t mStore; + + const Descriptor mDescriptor; + const uint32_t mStride; + + // Whether this buffer allocated in this process (as opposed to just + // being retained here), which determines whether to free or unregister + // the buffer when this Buffer is released + const bool mWasAllocated; + }; + + template <typename ...Args> + static int32_t callBufferFunction(gralloc1_device_t* device, + buffer_handle_t bufferHandle, + gralloc1_error_t (Buffer::*member)(Args...) const, Args... args) { + auto buffer = getAdapter(device)->getBuffer(bufferHandle); + if (!buffer) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE); + } + auto error = ((*buffer).*member)(std::forward<Args>(args)...); + return static_cast<int32_t>(error); + } + + template <typename MF, MF memFunc, typename ...Args> + static int32_t bufferHook(gralloc1_device_t* device, + buffer_handle_t bufferHandle, Args... args) { + return Gralloc1On0Adapter::callBufferFunction(device, bufferHandle, + memFunc, std::forward<Args>(args)...); + } + + static int32_t getConsumerUsageHook(gralloc1_device_t* device, + buffer_handle_t bufferHandle, uint64_t* outUsage) { + auto usage = GRALLOC1_CONSUMER_USAGE_NONE; + auto error = callBufferFunction(device, bufferHandle, + &Buffer::getConsumerUsage, &usage); + if (error != GRALLOC1_ERROR_NONE) { + *outUsage = static_cast<uint64_t>(usage); + } + return error; + } + + static int32_t getProducerUsageHook(gralloc1_device_t* device, + buffer_handle_t bufferHandle, uint64_t* outUsage) { + auto usage = GRALLOC1_PRODUCER_USAGE_NONE; + auto error = callBufferFunction(device, bufferHandle, + &Buffer::getProducerUsage, &usage); + if (error != GRALLOC1_ERROR_NONE) { + *outUsage = static_cast<uint64_t>(usage); + } + return error; + } + + // Buffer management functions + + // We don't provide GRALLOC1_FUNCTION_ALLOCATE, since this should always be + // called through GRALLOC1_FUNCTION_ALLOCATE_WITH_ID + gralloc1_error_t allocate( + const std::shared_ptr<Descriptor>& descriptor, + gralloc1_backing_store_t id, + buffer_handle_t* outBufferHandle); + static gralloc1_error_t allocateWithIdHook(gralloc1_device_t* device, + gralloc1_buffer_descriptor_t descriptors, + gralloc1_backing_store_t id, buffer_handle_t* outBuffer); + + gralloc1_error_t retain(const std::shared_ptr<Buffer>& buffer); + gralloc1_error_t release(const std::shared_ptr<Buffer>& buffer); + + // Member function pointer 'member' will either be retain or release + template <gralloc1_error_t (Gralloc1On0Adapter::*member)( + const std::shared_ptr<Buffer>& buffer)> + static int32_t managementHook(gralloc1_device_t* device, + buffer_handle_t bufferHandle) { + auto adapter = getAdapter(device); + + auto buffer = adapter->getBuffer(bufferHandle); + if (!buffer) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE); + } + + auto error = ((*adapter).*member)(buffer); + return static_cast<int32_t>(error); + } + + gralloc1_error_t retain(const GraphicBuffer* buffer); + static gralloc1_error_t retainGraphicBufferHook(gralloc1_device_t* device, + const GraphicBuffer* buffer) { + auto adapter = getAdapter(device); + return adapter->retain(buffer); + } + + // Buffer access functions + + gralloc1_error_t lock(const std::shared_ptr<Buffer>& buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t& accessRegion, void** outData, + const sp<Fence>& acquireFence); + gralloc1_error_t lockFlex(const std::shared_ptr<Buffer>& buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t& accessRegion, + struct android_flex_layout* outFlex, + const sp<Fence>& acquireFence); + gralloc1_error_t lockYCbCr(const std::shared_ptr<Buffer>& buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t& accessRegion, + struct android_ycbcr* outFlex, + const sp<Fence>& acquireFence); + + template <typename OUT, gralloc1_error_t (Gralloc1On0Adapter::*member)( + const std::shared_ptr<Buffer>&, gralloc1_producer_usage_t, + gralloc1_consumer_usage_t, const gralloc1_rect_t&, OUT*, + const sp<Fence>&)> + static int32_t lockHook(gralloc1_device_t* device, + buffer_handle_t bufferHandle, + uint64_t /*gralloc1_producer_usage_t*/ uintProducerUsage, + uint64_t /*gralloc1_consumer_usage_t*/ uintConsumerUsage, + const gralloc1_rect_t* accessRegion, OUT* outData, + int32_t acquireFenceFd) { + auto adapter = getAdapter(device); + + // Exactly one of producer and consumer usage must be *_USAGE_NONE, + // but we can't check this until the upper levels of the framework + // correctly distinguish between producer and consumer usage + /* + bool hasProducerUsage = + uintProducerUsage != GRALLOC1_PRODUCER_USAGE_NONE; + bool hasConsumerUsage = + uintConsumerUsage != GRALLOC1_CONSUMER_USAGE_NONE; + if (hasProducerUsage && hasConsumerUsage || + !hasProducerUsage && !hasConsumerUsage) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE); + } + */ + + auto producerUsage = + static_cast<gralloc1_producer_usage_t>(uintProducerUsage); + auto consumerUsage = + static_cast<gralloc1_consumer_usage_t>(uintConsumerUsage); + + if (!outData) { + const auto producerCpuUsage = GRALLOC1_PRODUCER_USAGE_CPU_READ | + GRALLOC1_PRODUCER_USAGE_CPU_WRITE; + if (producerUsage & producerCpuUsage != 0) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE); + } + if (consumerUsage & GRALLOC1_CONSUMER_USAGE_CPU_READ != 0) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE); + } + } + + auto buffer = adapter->getBuffer(bufferHandle); + if (!buffer) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE); + } + + if (!accessRegion) { + ALOGE("accessRegion is null"); + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE); + } + + sp<Fence> acquireFence{new Fence(acquireFenceFd)}; + auto error = ((*adapter).*member)(buffer, producerUsage, consumerUsage, + *accessRegion, outData, acquireFence); + return static_cast<int32_t>(error); + } + + gralloc1_error_t unlock(const std::shared_ptr<Buffer>& buffer, + sp<Fence>* outReleaseFence); + static int32_t unlockHook(gralloc1_device_t* device, + buffer_handle_t bufferHandle, int32_t* outReleaseFenceFd) { + auto adapter = getAdapter(device); + + auto buffer = adapter->getBuffer(bufferHandle); + if (!buffer) { + return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE); + } + + sp<Fence> releaseFence = Fence::NO_FENCE; + auto error = adapter->unlock(buffer, &releaseFence); + if (error == GRALLOC1_ERROR_NONE) { + *outReleaseFenceFd = releaseFence->dup(); + } + return static_cast<int32_t>(error); + } + + // Adapter internals + const gralloc_module_t* mModule; + uint8_t mMinorVersion; + alloc_device_t* mDevice; + + std::shared_ptr<Descriptor> getDescriptor( + gralloc1_buffer_descriptor_t descriptorId); + std::shared_ptr<Buffer> getBuffer(buffer_handle_t bufferHandle); + + static std::atomic<gralloc1_buffer_descriptor_t> sNextBufferDescriptorId; + std::mutex mDescriptorMutex; + std::unordered_map<gralloc1_buffer_descriptor_t, + std::shared_ptr<Descriptor>> mDescriptors; + std::mutex mBufferMutex; + std::unordered_map<buffer_handle_t, std::shared_ptr<Buffer>> mBuffers; +}; + +} // namespace android + +#endif diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index 3da720ff37..3e127a12a5 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -26,6 +26,7 @@ #include <utils/Flattenable.h> #include <utils/RefBase.h> +#include <string> struct ANativeWindowBuffer; @@ -73,7 +74,7 @@ public: // creates w * h buffer GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, - uint32_t inUsage); + uint32_t inUsage, std::string requestorName = "<Unknown>"); // create a buffer from an existing handle GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, @@ -159,7 +160,7 @@ private: const GraphicBuffer& operator = (const GraphicBuffer& rhs) const; status_t initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, - uint32_t inUsage); + uint32_t inUsage, std::string requestorName); void free_handle(); diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h index 5443f09a10..28d0238339 100644 --- a/include/ui/GraphicBufferAllocator.h +++ b/include/ui/GraphicBufferAllocator.h @@ -27,42 +27,41 @@ #include <utils/threads.h> #include <utils/Singleton.h> +#include <ui/Gralloc1.h> #include <ui/PixelFormat.h> -#include <hardware/gralloc.h> - - namespace android { -// --------------------------------------------------------------------------- +class Gralloc1Loader; class String8; class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> { public: enum { - USAGE_SW_READ_NEVER = GRALLOC_USAGE_SW_READ_NEVER, - USAGE_SW_READ_RARELY = GRALLOC_USAGE_SW_READ_RARELY, - USAGE_SW_READ_OFTEN = GRALLOC_USAGE_SW_READ_OFTEN, - USAGE_SW_READ_MASK = GRALLOC_USAGE_SW_READ_MASK, + USAGE_SW_READ_NEVER = GRALLOC1_CONSUMER_USAGE_CPU_READ_NEVER, + USAGE_SW_READ_RARELY = GRALLOC1_CONSUMER_USAGE_CPU_READ, + USAGE_SW_READ_OFTEN = GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN, + USAGE_SW_READ_MASK = GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN, - USAGE_SW_WRITE_NEVER = GRALLOC_USAGE_SW_WRITE_NEVER, - USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY, - USAGE_SW_WRITE_OFTEN = GRALLOC_USAGE_SW_WRITE_OFTEN, - USAGE_SW_WRITE_MASK = GRALLOC_USAGE_SW_WRITE_MASK, + USAGE_SW_WRITE_NEVER = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_NEVER, + USAGE_SW_WRITE_RARELY = GRALLOC1_PRODUCER_USAGE_CPU_WRITE, + USAGE_SW_WRITE_OFTEN = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, + USAGE_SW_WRITE_MASK = GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK, - USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE, - USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, - USAGE_HW_2D = GRALLOC_USAGE_HW_2D, - USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK + USAGE_HW_TEXTURE = GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE, + USAGE_HW_RENDER = GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET, + USAGE_HW_2D = 0x00000400, // Deprecated + USAGE_HW_MASK = 0x00071F00, // Deprecated }; static inline GraphicBufferAllocator& get() { return getInstance(); } - status_t alloc(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage, - buffer_handle_t* handle, uint32_t* stride); + status_t allocate(uint32_t w, uint32_t h, PixelFormat format, + uint32_t usage, buffer_handle_t* handle, uint32_t* stride, + uint64_t graphicBufferId, std::string requestorName); status_t free(buffer_handle_t handle); @@ -77,6 +76,7 @@ private: PixelFormat format; uint32_t usage; size_t size; + std::string requestorName; }; static Mutex sLock; @@ -86,7 +86,8 @@ private: GraphicBufferAllocator(); ~GraphicBufferAllocator(); - alloc_device_t *mAllocDev; + std::unique_ptr<Gralloc1::Loader> mLoader; + std::unique_ptr<Gralloc1::Device> mDevice; }; // --------------------------------------------------------------------------- diff --git a/include/ui/GraphicBufferMapper.h b/include/ui/GraphicBufferMapper.h index 6099548aaf..a25809c124 100644 --- a/include/ui/GraphicBufferMapper.h +++ b/include/ui/GraphicBufferMapper.h @@ -20,12 +20,9 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Singleton.h> - -#include <hardware/gralloc.h> +#include <ui/Gralloc1.h> - -struct gralloc_module_t; +#include <utils/Singleton.h> namespace android { @@ -39,6 +36,7 @@ public: static inline GraphicBufferMapper& get() { return getInstance(); } status_t registerBuffer(buffer_handle_t handle); + status_t registerBuffer(const GraphicBuffer* buffer); status_t unregisterBuffer(buffer_handle_t handle); @@ -59,13 +57,13 @@ public: status_t unlockAsync(buffer_handle_t handle, int *fenceFd); - // dumps information about the mapping of this handle - void dump(buffer_handle_t handle); - private: friend class Singleton<GraphicBufferMapper>; + GraphicBufferMapper(); - gralloc_module_t const *mAllocMod; + + std::unique_ptr<Gralloc1::Loader> mLoader; + std::unique_ptr<Gralloc1::Device> mDevice; }; // --------------------------------------------------------------------------- diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 635020e718..46feb1cc36 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -64,6 +64,7 @@ LOCAL_SRC_FILES := \ 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 96f7829233..73d2042983 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -260,6 +260,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, mCore->mDequeueCondition.broadcast(); ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); + mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); VALIDATE_CONSISTENCY(); } @@ -718,6 +719,13 @@ 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(); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 09e7cb6a8e..f8f3dc9fc4 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -496,7 +496,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, status_t error; BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( - width, height, format, usage, &error)); + width, height, format, usage, + {mConsumerName.string(), mConsumerName.size()}, &error)); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); @@ -509,11 +510,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; } @@ -896,9 +901,11 @@ 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(), mCore->mQueue.size()); + mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); // Take a ticket for the callback functions callbackTicket = mNextCallbackTicket++; @@ -1102,7 +1109,8 @@ 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())); + static_cast<uint32_t>(mCore->mQueue.size()), + mCore->mFrameCounter + 1); // Set up a death notification so that we can disconnect // automatically if the remote producer dies @@ -1255,7 +1263,8 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, for (size_t i = 0; i < newBufferCount; ++i) { status_t result = NO_ERROR; sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( - allocWidth, allocHeight, allocFormat, allocUsage, &result)); + allocWidth, allocHeight, allocFormat, allocUsage, + {mConsumerName.string(), mConsumerName.size()}, &result)); if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" " %u, usage %u)", width, height, format, usage); @@ -1338,14 +1347,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); @@ -1414,6 +1415,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 diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index c00526dd9c..a1bdf4a4a1 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -235,6 +235,16 @@ 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) { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 553b65ce28..aa0db4506c 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -345,7 +345,8 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer( kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY); + GraphicBuffer::USAGE_SW_WRITE_RARELY, + "[GLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); uint32_t stride = buffer->getStride(); diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp index 9643402dfa..e6150f4926 100644 --- a/libs/gui/GraphicBufferAlloc.cpp +++ b/libs/gui/GraphicBufferAlloc.cpp @@ -32,9 +32,10 @@ GraphicBufferAlloc::~GraphicBufferAlloc() { } sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width, - uint32_t height, PixelFormat format, uint32_t usage, status_t* error) { - sp<GraphicBuffer> graphicBuffer( - new GraphicBuffer(width, height, format, usage)); + uint32_t height, PixelFormat format, uint32_t usage, + std::string requestorName, status_t* error) { + sp<GraphicBuffer> graphicBuffer(new GraphicBuffer( + width, height, format, usage, std::move(requestorName))); status_t err = graphicBuffer->initCheck(); *error = err; if (err != 0 || graphicBuffer->handle == 0) { diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp index cab7dc3d5a..9a06011ce0 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/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp index 3009989964..7b3b7c1dc5 100644 --- a/libs/gui/IGraphicBufferAlloc.cpp +++ b/libs/gui/IGraphicBufferAlloc.cpp @@ -46,13 +46,19 @@ public: virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t usage, - status_t* error) { + std::string requestorName, status_t* error) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor()); data.writeUint32(width); data.writeUint32(height); data.writeInt32(static_cast<int32_t>(format)); data.writeUint32(usage); + if (requestorName.empty()) { + requestorName += "[PID "; + requestorName += std::to_string(getpid()); + requestorName += ']'; + } + data.writeUtf8AsUtf16(requestorName); remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply); sp<GraphicBuffer> graphicBuffer; status_t result = reply.readInt32(); @@ -101,9 +107,11 @@ status_t BnGraphicBufferAlloc::onTransact( uint32_t height = data.readUint32(); PixelFormat format = static_cast<PixelFormat>(data.readInt32()); uint32_t usage = data.readUint32(); - status_t error; - sp<GraphicBuffer> result = - createGraphicBuffer(width, height, format, usage, &error); + status_t error = NO_ERROR; + std::string requestorName; + data.readUtf8FromUtf16(&requestorName); + sp<GraphicBuffer> result = createGraphicBuffer(width, height, + format, usage, requestorName, &error); reply->writeInt32(error); if (result != 0) { reply->write(*result); diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index af6800b16f..c8eff00e3f 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -51,6 +51,7 @@ enum { SET_CONSUMER_USAGE_BITS, SET_TRANSFORM_HINT, GET_SIDEBAND_STREAM, + GET_OCCUPANCY_HISTORY, DISCARD_FREE_BUFFERS, DUMP, }; @@ -261,6 +262,31 @@ 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()); @@ -425,6 +451,25 @@ 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(); diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 7d7d09d144..fbd704d03d 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -50,12 +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_UNIQUE_ID, + GET_FRAME_TIMESTAMPS, + GET_UNIQUE_ID }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -133,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; @@ -171,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; @@ -333,18 +346,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( @@ -420,6 +421,42 @@ public: 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()); @@ -524,9 +561,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; @@ -547,8 +586,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; } @@ -622,12 +663,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(); @@ -679,6 +714,30 @@ 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; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index a8b4fa8226..f0b0ada270 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 2ecb9083ff..dd5b169dba 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 bcfe714bc9..dbf811462d 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) { @@ -1272,8 +1342,7 @@ 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; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 92ae41eec8..3df5f74fc9 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,7 +167,7 @@ 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, @@ -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; } @@ -612,6 +614,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,9 +709,9 @@ 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); } // ---------------------------------------------------------------------------- @@ -763,6 +773,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 721667cdc2..8a9eeeec21 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,6 +854,142 @@ 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); 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/ui/Android.mk b/libs/ui/Android.mk index ee6c093bcf..e690ede195 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -17,7 +17,7 @@ include $(CLEAR_VARS) LOCAL_CLANG := true LOCAL_CPPFLAGS := -std=c++1y -Weverything -Werror -LOCAL_SANITIZE := integer +# LOCAL_SANITIZE := integer # The static constructors and destructors in this library have not been noted to # introduce significant overheads @@ -37,6 +37,8 @@ LOCAL_CPPFLAGS += -Wno-padded LOCAL_SRC_FILES := \ Fence.cpp \ FrameStats.cpp \ + Gralloc1.cpp \ + Gralloc1On0Adapter.cpp \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp new file mode 100644 index 0000000000..4c73ce4535 --- /dev/null +++ b/libs/ui/Gralloc1.cpp @@ -0,0 +1,402 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 + +#include <ui/Gralloc1.h> + +#include <vector> + +#undef LOG_TAG +#define LOG_TAG GRALLOC1_LOG_TAG + +namespace android { + +namespace Gralloc1 { + +Descriptor::~Descriptor() +{ + int32_t intError = mShimDevice.mFunctions.destroyDescriptor( + mShimDevice.mDevice, mDeviceId); + auto error = static_cast<gralloc1_error_t>(intError); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("destroyDescriptor failed: %d", intError); + } +} + +gralloc1_error_t Descriptor::setDimensions(uint32_t width, uint32_t height) +{ + int32_t intError = mShimDevice.mFunctions.setDimensions(mShimDevice.mDevice, + mDeviceId, width, height); + auto error = static_cast<gralloc1_error_t>(intError); + if (error != GRALLOC1_ERROR_NONE) { + return error; + } + mWidth = width; + mHeight = height; + return error; +} + +template <typename ApiType> +struct Setter { + typedef int32_t (*Type)(gralloc1_device_t*, gralloc1_buffer_descriptor_t, + ApiType); +}; + +template <typename ApiType, typename ValueType> +static inline gralloc1_error_t setHelper( + typename Setter<ApiType>::Type setter, gralloc1_device_t* device, + gralloc1_buffer_descriptor_t id, ValueType newValue, + ValueType* cacheVariable) +{ + int32_t intError = setter(device, id, static_cast<ApiType>(newValue)); + auto error = static_cast<gralloc1_error_t>(intError); + if (error != GRALLOC1_ERROR_NONE) { + return error; + } + *cacheVariable = newValue; + return error; +} + +gralloc1_error_t Descriptor::setFormat(android_pixel_format_t format) +{ + return setHelper<int32_t>(mShimDevice.mFunctions.setFormat.pfn, + mShimDevice.mDevice, mDeviceId, format, &mFormat); +} + +gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage) +{ + return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn, + mShimDevice.mDevice, mDeviceId, usage, &mProducerUsage); +} + +gralloc1_error_t Descriptor::setConsumerUsage(gralloc1_consumer_usage_t usage) +{ + return setHelper<uint64_t>(mShimDevice.mFunctions.setConsumerUsage.pfn, + mShimDevice.mDevice, mDeviceId, usage, &mConsumerUsage); +} + +Device::Device(gralloc1_device_t* device) + : mDevice(device), + mCapabilities(loadCapabilities()), + mFunctions() +{ + if (!loadFunctions()) { + ALOGE("Failed to load a required function, aborting"); + abort(); + } +} + +bool Device::hasCapability(gralloc1_capability_t capability) const +{ + return mCapabilities.count(capability) > 0; +} + +std::string Device::dump() +{ + uint32_t length = 0; + mFunctions.dump(mDevice, &length, nullptr); + + std::vector<char> output; + output.resize(length); + mFunctions.dump(mDevice, &length, output.data()); + + return std::string(output.cbegin(), output.cend()); +} + +std::shared_ptr<Descriptor> Device::createDescriptor() +{ + gralloc1_buffer_descriptor_t descriptorId; + int32_t intError = mFunctions.createDescriptor(mDevice, &descriptorId); + auto error = static_cast<gralloc1_error_t>(intError); + if (error != GRALLOC1_ERROR_NONE) { + return nullptr; + } + auto descriptor = std::make_shared<Descriptor>(*this, descriptorId); + return descriptor; +} + +gralloc1_error_t Device::getStride(buffer_handle_t buffer, uint32_t* outStride) +{ + int32_t intError = mFunctions.getStride(mDevice, buffer, outStride); + return static_cast<gralloc1_error_t>(intError); +} + +static inline bool allocationSucceded(gralloc1_error_t error) +{ + return error == GRALLOC1_ERROR_NONE || error == GRALLOC1_ERROR_NOT_SHARED; +} + +gralloc1_error_t Device::allocate( + const std::vector<std::shared_ptr<const Descriptor>>& descriptors, + std::vector<buffer_handle_t>* outBuffers) +{ + if (mFunctions.allocate.pfn == nullptr) { + // Allocation is not supported on this device + return GRALLOC1_ERROR_UNSUPPORTED; + } + + std::vector<gralloc1_buffer_descriptor_t> deviceIds; + for (const auto& descriptor : descriptors) { + deviceIds.emplace_back(descriptor->getDeviceId()); + } + + std::vector<buffer_handle_t> buffers(descriptors.size()); + int32_t intError = mFunctions.allocate(mDevice, + static_cast<uint32_t>(descriptors.size()), deviceIds.data(), + buffers.data()); + auto error = static_cast<gralloc1_error_t>(intError); + if (allocationSucceded(error)) { + *outBuffers = std::move(buffers); + } + + return error; +} + +gralloc1_error_t Device::allocate( + const std::shared_ptr<const Descriptor>& descriptor, + gralloc1_backing_store_t id, buffer_handle_t* outBuffer) +{ + gralloc1_error_t error = GRALLOC1_ERROR_NONE; + + if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { + buffer_handle_t buffer = nullptr; + int32_t intError = mFunctions.allocateWithId(mDevice, + descriptor->getDeviceId(), id, &buffer); + error = static_cast<gralloc1_error_t>(intError); + if (allocationSucceded(error)) { + *outBuffer = buffer; + } + } else { + std::vector<std::shared_ptr<const Descriptor>> descriptors; + descriptors.emplace_back(descriptor); + std::vector<buffer_handle_t> buffers; + error = allocate(descriptors, &buffers); + if (allocationSucceded(error)) { + *outBuffer = buffers[0]; + } + } + + return error; +} + +gralloc1_error_t Device::retain(buffer_handle_t buffer) +{ + int32_t intError = mFunctions.retain(mDevice, buffer); + return static_cast<gralloc1_error_t>(intError); +} + +gralloc1_error_t Device::retain(const GraphicBuffer* buffer) +{ + if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { + return mFunctions.retainGraphicBuffer(mDevice, buffer); + } else { + return retain(buffer->getNativeBuffer()->handle); + } +} + +gralloc1_error_t Device::release(buffer_handle_t buffer) +{ + int32_t intError = mFunctions.release(mDevice, buffer); + return static_cast<gralloc1_error_t>(intError); +} + +gralloc1_error_t Device::getNumFlexPlanes(buffer_handle_t buffer, + uint32_t* outNumPlanes) +{ + uint32_t numPlanes = 0; + int32_t intError = mFunctions.getNumFlexPlanes(mDevice, buffer, &numPlanes); + auto error = static_cast<gralloc1_error_t>(intError); + if (error == GRALLOC1_ERROR_NONE) { + *outNumPlanes = numPlanes; + } + return error; +} + +gralloc1_error_t Device::lock(buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, void** outData, + const sp<Fence>& acquireFence) +{ + ALOGV("Calling lock(%p)", buffer); + return lockHelper(mFunctions.lock.pfn, buffer, producerUsage, + consumerUsage, accessRegion, outData, acquireFence); +} + +gralloc1_error_t Device::lockFlex(buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, + struct android_flex_layout* outData, + const sp<Fence>& acquireFence) +{ + ALOGV("Calling lockFlex(%p)", buffer); + return lockHelper(mFunctions.lockFlex.pfn, buffer, producerUsage, + consumerUsage, accessRegion, outData, acquireFence); +} + +gralloc1_error_t Device::lockYCbCr(buffer_handle_t buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t* accessRegion, + struct android_ycbcr* outData, + const sp<Fence>& acquireFence) +{ + ALOGV("Calling lockYCbCr(%p)", buffer); + return lockHelper(mFunctions.lockYCbCr.pfn, buffer, producerUsage, + consumerUsage, accessRegion, outData, acquireFence); +} + +gralloc1_error_t Device::unlock(buffer_handle_t buffer, sp<Fence>* outFence) +{ + int32_t fenceFd = -1; + int32_t intError = mFunctions.unlock(mDevice, buffer, &fenceFd); + auto error = static_cast<gralloc1_error_t>(intError); + if (error == GRALLOC1_ERROR_NONE) { + *outFence = new Fence(fenceFd); + } + return error; +} + +std::unordered_set<gralloc1_capability_t> Device::loadCapabilities() +{ + std::vector<int32_t> intCapabilities; + uint32_t numCapabilities = 0; + mDevice->getCapabilities(mDevice, &numCapabilities, nullptr); + + intCapabilities.resize(numCapabilities); + mDevice->getCapabilities(mDevice, &numCapabilities, intCapabilities.data()); + + std::unordered_set<gralloc1_capability_t> capabilities; + for (const auto intCapability : intCapabilities) { + capabilities.emplace(static_cast<gralloc1_capability_t>(intCapability)); + } + return capabilities; +} + +bool Device::loadFunctions() +{ + // Functions which must always be present + if (!mFunctions.dump.load(mDevice, true)) { + return false; + } + if (!mFunctions.createDescriptor.load(mDevice, true)) { + return false; + } + if (!mFunctions.destroyDescriptor.load(mDevice, true)) { + return false; + } + if (!mFunctions.setConsumerUsage.load(mDevice, true)) { + return false; + } + if (!mFunctions.setDimensions.load(mDevice, true)) { + return false; + } + if (!mFunctions.setFormat.load(mDevice, true)) { + return false; + } + if (!mFunctions.setProducerUsage.load(mDevice, true)) { + return false; + } + if (!mFunctions.getBackingStore.load(mDevice, true)) { + return false; + } + if (!mFunctions.getConsumerUsage.load(mDevice, true)) { + return false; + } + if (!mFunctions.getDimensions.load(mDevice, true)) { + return false; + } + if (!mFunctions.getFormat.load(mDevice, true)) { + return false; + } + if (!mFunctions.getProducerUsage.load(mDevice, true)) { + return false; + } + if (!mFunctions.getStride.load(mDevice, true)) { + return false; + } + if (!mFunctions.retain.load(mDevice, true)) { + return false; + } + if (!mFunctions.release.load(mDevice, true)) { + return false; + } + if (!mFunctions.getNumFlexPlanes.load(mDevice, true)) { + return false; + } + if (!mFunctions.lock.load(mDevice, true)) { + return false; + } + if (!mFunctions.lockFlex.load(mDevice, true)) { + return false; + } + if (!mFunctions.unlock.load(mDevice, true)) { + return false; + } + + if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { + // These should always be present on the adapter + if (!mFunctions.retainGraphicBuffer.load(mDevice, true)) { + return false; + } + if (!mFunctions.lockYCbCr.load(mDevice, true)) { + return false; + } + + // allocateWithId may not be present if we're only able to map in this + // process + mFunctions.allocateWithId.load(mDevice, false); + } else { + // allocate may not be present if we're only able to map in this process + mFunctions.allocate.load(mDevice, false); + } + + return true; +} + +std::unique_ptr<Gralloc1On0Adapter> Loader::mAdapter = nullptr; + +Loader::Loader() + : mDevice(nullptr) +{ + hw_module_t const* module; + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + uint8_t majorVersion = (module->module_api_version >> 8) & 0xFF; + uint8_t minorVersion = module->module_api_version & 0xFF; + gralloc1_device_t* device = nullptr; + if (majorVersion == 1) { + gralloc1_open(module, &device); + } else { + if (!mAdapter) { + mAdapter = std::make_unique<Gralloc1On0Adapter>(module); + } + device = mAdapter->getDevice(); + } + mDevice = std::make_unique<Gralloc1::Device>(device); +} + +Loader::~Loader() {} + +std::unique_ptr<Device> Loader::getDevice() +{ + return std::move(mDevice); +} + +} // namespace android::Gralloc1 + +} // namespace android diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp new file mode 100644 index 0000000000..d5b88deb9a --- /dev/null +++ b/libs/ui/Gralloc1On0Adapter.cpp @@ -0,0 +1,478 @@ +/* + * 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 "Gralloc1On0Adapter" +//#define LOG_NDEBUG 0 + +#include <hardware/gralloc.h> + +#include <ui/Gralloc1On0Adapter.h> + +#include <utils/Log.h> + +#include <inttypes.h> + +template <typename PFN, typename T> +static gralloc1_function_pointer_t asFP(T function) +{ + static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer"); + return reinterpret_cast<gralloc1_function_pointer_t>(function); +} + +namespace android { + +Gralloc1On0Adapter::Gralloc1On0Adapter(const hw_module_t* module) + : mModule(reinterpret_cast<const gralloc_module_t*>(module)), + mMinorVersion(mModule->common.module_api_version & 0xFF), + mDevice(nullptr) +{ + ALOGV("Constructing"); + getCapabilities = getCapabilitiesHook; + getFunction = getFunctionHook; + int error = ::gralloc_open(&(mModule->common), &mDevice); + if (error) { + ALOGE("Failed to open gralloc0 module: %d", error); + } + ALOGV("Opened gralloc0 device %p", mDevice); +} + +Gralloc1On0Adapter::~Gralloc1On0Adapter() +{ + ALOGV("Destructing"); + if (mDevice) { + ALOGV("Closing gralloc0 device %p", mDevice); + ::gralloc_close(mDevice); + } +} + +void Gralloc1On0Adapter::doGetCapabilities(uint32_t* outCount, + int32_t* outCapabilities) +{ + if (outCapabilities == nullptr) { + *outCount = 1; + return; + } + if (*outCount >= 1) { + *outCapabilities = GRALLOC1_CAPABILITY_ON_ADAPTER; + *outCount = 1; + } +} + +gralloc1_function_pointer_t Gralloc1On0Adapter::doGetFunction( + int32_t intDescriptor) +{ + constexpr auto lastDescriptor = + static_cast<int32_t>(GRALLOC1_LAST_ADAPTER_FUNCTION); + if (intDescriptor < 0 || intDescriptor > lastDescriptor) { + ALOGE("Invalid function descriptor"); + return nullptr; + } + + auto descriptor = + static_cast<gralloc1_function_descriptor_t>(intDescriptor); + switch (descriptor) { + case GRALLOC1_FUNCTION_DUMP: + return asFP<GRALLOC1_PFN_DUMP>(dumpHook); + case GRALLOC1_FUNCTION_CREATE_DESCRIPTOR: + return asFP<GRALLOC1_PFN_CREATE_DESCRIPTOR>(createDescriptorHook); + case GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR: + return asFP<GRALLOC1_PFN_DESTROY_DESCRIPTOR>(destroyDescriptorHook); + case GRALLOC1_FUNCTION_SET_CONSUMER_USAGE: + return asFP<GRALLOC1_PFN_SET_CONSUMER_USAGE>(setConsumerUsageHook); + case GRALLOC1_FUNCTION_SET_DIMENSIONS: + return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook); + case GRALLOC1_FUNCTION_SET_FORMAT: + return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook); + case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE: + return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook); + case GRALLOC1_FUNCTION_GET_BACKING_STORE: + return asFP<GRALLOC1_PFN_GET_BACKING_STORE>( + bufferHook<decltype(&Buffer::getBackingStore), + &Buffer::getBackingStore, gralloc1_backing_store_t*>); + case GRALLOC1_FUNCTION_GET_CONSUMER_USAGE: + return asFP<GRALLOC1_PFN_GET_CONSUMER_USAGE>(getConsumerUsageHook); + case GRALLOC1_FUNCTION_GET_DIMENSIONS: + return asFP<GRALLOC1_PFN_GET_DIMENSIONS>( + bufferHook<decltype(&Buffer::getDimensions), + &Buffer::getDimensions, uint32_t*, uint32_t*>); + case GRALLOC1_FUNCTION_GET_FORMAT: + return asFP<GRALLOC1_PFN_GET_FORMAT>( + bufferHook<decltype(&Buffer::getFormat), + &Buffer::getFormat, int32_t*>); + case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE: + return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook); + case GRALLOC1_FUNCTION_GET_STRIDE: + return asFP<GRALLOC1_PFN_GET_STRIDE>( + bufferHook<decltype(&Buffer::getStride), + &Buffer::getStride, uint32_t*>); + case GRALLOC1_FUNCTION_ALLOCATE: + // Not provided, since we'll use ALLOCATE_WITH_ID + return nullptr; + case GRALLOC1_FUNCTION_ALLOCATE_WITH_ID: + if (mDevice != nullptr) { + return asFP<GRALLOC1_PFN_ALLOCATE_WITH_ID>(allocateWithIdHook); + } else { + return nullptr; + } + case GRALLOC1_FUNCTION_RETAIN: + return asFP<GRALLOC1_PFN_RETAIN>( + managementHook<&Gralloc1On0Adapter::retain>); + case GRALLOC1_FUNCTION_RELEASE: + return asFP<GRALLOC1_PFN_RELEASE>( + managementHook<&Gralloc1On0Adapter::release>); + case GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER: + return asFP<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER>( + retainGraphicBufferHook); + case GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES: + return asFP<GRALLOC1_PFN_GET_NUM_FLEX_PLANES>( + bufferHook<decltype(&Buffer::getNumFlexPlanes), + &Buffer::getNumFlexPlanes, uint32_t*>); + case GRALLOC1_FUNCTION_LOCK: + return asFP<GRALLOC1_PFN_LOCK>( + lockHook<void*, &Gralloc1On0Adapter::lock>); + case GRALLOC1_FUNCTION_LOCK_FLEX: + return asFP<GRALLOC1_PFN_LOCK_FLEX>( + lockHook<struct android_flex_layout, + &Gralloc1On0Adapter::lockFlex>); + case GRALLOC1_FUNCTION_LOCK_YCBCR: + return asFP<GRALLOC1_PFN_LOCK_YCBCR>( + lockHook<struct android_ycbcr, + &Gralloc1On0Adapter::lockYCbCr>); + case GRALLOC1_FUNCTION_UNLOCK: + return asFP<GRALLOC1_PFN_UNLOCK>(unlockHook); + case GRALLOC1_FUNCTION_INVALID: + ALOGE("Invalid function descriptor"); + return nullptr; + } + + ALOGE("Unknown function descriptor: %d", intDescriptor); + return nullptr; +} + +void Gralloc1On0Adapter::dump(uint32_t* outSize, char* outBuffer) +{ + ALOGV("dump(%u (%p), %p", outSize ? *outSize : 0, outSize, outBuffer); + + if (!mDevice->dump) { + // dump is optional on gralloc0 implementations + *outSize = 0; + return; + } + + if (!outBuffer) { + constexpr int32_t BUFFER_LENGTH = 4096; + char buffer[BUFFER_LENGTH] = {}; + mDevice->dump(mDevice, buffer, BUFFER_LENGTH); + buffer[BUFFER_LENGTH - 1] = 0; // Ensure the buffer is null-terminated + size_t actualLength = std::strlen(buffer); + mCachedDump.resize(actualLength); + std::copy_n(buffer, actualLength, mCachedDump.begin()); + *outSize = static_cast<uint32_t>(actualLength); + } else { + *outSize = std::min(*outSize, + static_cast<uint32_t>(mCachedDump.size())); + outBuffer = std::copy_n(mCachedDump.cbegin(), *outSize, outBuffer); + } +} + +gralloc1_error_t Gralloc1On0Adapter::createDescriptor( + gralloc1_buffer_descriptor_t* outDescriptor) +{ + auto descriptorId = sNextBufferDescriptorId++; + std::lock_guard<std::mutex> lock(mDescriptorMutex); + mDescriptors.emplace(descriptorId, + std::make_shared<Descriptor>(this, descriptorId)); + + ALOGV("Created descriptor %" PRIu64, descriptorId); + + *outDescriptor = descriptorId; + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::destroyDescriptor( + gralloc1_buffer_descriptor_t descriptor) +{ + ALOGV("Destroying descriptor %" PRIu64, descriptor); + + std::lock_guard<std::mutex> lock(mDescriptorMutex); + if (mDescriptors.count(descriptor) == 0) { + return GRALLOC1_ERROR_BAD_DESCRIPTOR; + } + + mDescriptors.erase(descriptor); + return GRALLOC1_ERROR_NONE; +} + +Gralloc1On0Adapter::Buffer::Buffer(buffer_handle_t handle, + gralloc1_backing_store_t store, const Descriptor& descriptor, + uint32_t stride, bool wasAllocated) + : mHandle(handle), + mReferenceCount(1), + mStore(store), + mDescriptor(descriptor), + mStride(stride), + mWasAllocated(wasAllocated) {} + +gralloc1_error_t Gralloc1On0Adapter::allocate( + const std::shared_ptr<Descriptor>& descriptor, + gralloc1_backing_store_t store, + buffer_handle_t* outBufferHandle) +{ + ALOGV("allocate(%" PRIu64 ", %#" PRIx64 ")", descriptor->id, store); + + // If this function is being called, it's because we handed out its function + // pointer, which only occurs when mDevice has been loaded successfully and + // we are permitted to allocate + + int usage = static_cast<int>(descriptor->producerUsage) | + static_cast<int>(descriptor->consumerUsage); + buffer_handle_t handle = nullptr; + int stride = 0; + ALOGV("Calling alloc(%p, %u, %u, %i, %u)", mDevice, descriptor->width, + descriptor->height, descriptor->format, usage); + auto error = mDevice->alloc(mDevice, + static_cast<int>(descriptor->width), + static_cast<int>(descriptor->height), descriptor->format, + usage, &handle, &stride); + if (error != 0) { + ALOGE("gralloc0 allocation failed: %d (%s)", error, + strerror(-error)); + return GRALLOC1_ERROR_NO_RESOURCES; + } + + *outBufferHandle = handle; + auto buffer = std::make_shared<Buffer>(handle, store, *descriptor, stride, + true); + + std::lock_guard<std::mutex> lock(mBufferMutex); + mBuffers.emplace(handle, std::move(buffer)); + + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::allocateWithIdHook( + gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptorId, + gralloc1_backing_store_t store, buffer_handle_t* outBuffer) +{ + auto adapter = getAdapter(device); + + auto descriptor = adapter->getDescriptor(descriptorId); + if (!descriptor) { + return GRALLOC1_ERROR_BAD_DESCRIPTOR; + } + + buffer_handle_t bufferHandle = nullptr; + auto error = adapter->allocate(descriptor, store, &bufferHandle); + if (error != GRALLOC1_ERROR_NONE) { + return error; + } + + *outBuffer = bufferHandle; + return error; +} + +gralloc1_error_t Gralloc1On0Adapter::retain( + const std::shared_ptr<Buffer>& buffer) +{ + buffer->retain(); + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::release( + const std::shared_ptr<Buffer>& buffer) +{ + if (!buffer->release()) { + return GRALLOC1_ERROR_NONE; + } + + buffer_handle_t handle = buffer->getHandle(); + if (buffer->wasAllocated()) { + ALOGV("Calling free(%p)", handle); + int result = mDevice->free(mDevice, handle); + if (result != 0) { + ALOGE("gralloc0 free failed: %d", result); + } + } else { + ALOGV("Calling unregisterBuffer(%p)", handle); + int result = mModule->unregisterBuffer(mModule, handle); + if (result != 0) { + ALOGE("gralloc0 unregister failed: %d", result); + } + } + + std::lock_guard<std::mutex> lock(mBufferMutex); + mBuffers.erase(handle); + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::retain( + const android::GraphicBuffer* graphicBuffer) +{ + ALOGV("retainGraphicBuffer(%p, %#" PRIx64 ")", + graphicBuffer->getNativeBuffer()->handle, graphicBuffer->getId()); + + buffer_handle_t handle = graphicBuffer->getNativeBuffer()->handle; + std::lock_guard<std::mutex> lock(mBufferMutex); + if (mBuffers.count(handle) != 0) { + mBuffers[handle]->retain(); + return GRALLOC1_ERROR_NONE; + } + + ALOGV("Calling registerBuffer(%p)", handle); + int result = mModule->registerBuffer(mModule, handle); + if (result != 0) { + ALOGE("gralloc0 register failed: %d", result); + return GRALLOC1_ERROR_NO_RESOURCES; + } + + Descriptor descriptor{this, sNextBufferDescriptorId++}; + descriptor.setDimensions(graphicBuffer->getWidth(), + graphicBuffer->getHeight()); + descriptor.setFormat(graphicBuffer->getPixelFormat()); + descriptor.setProducerUsage( + static_cast<gralloc1_producer_usage_t>(graphicBuffer->getUsage())); + descriptor.setConsumerUsage( + static_cast<gralloc1_consumer_usage_t>(graphicBuffer->getUsage())); + auto buffer = std::make_shared<Buffer>(handle, + static_cast<gralloc1_backing_store_t>(graphicBuffer->getId()), + descriptor, graphicBuffer->getStride(), false); + mBuffers.emplace(handle, std::move(buffer)); + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::lock( + const std::shared_ptr<Buffer>& buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t& accessRegion, void** outData, + const sp<Fence>& acquireFence) +{ + if (mMinorVersion >= 3) { + int result = mModule->lockAsync(mModule, buffer->getHandle(), + static_cast<int32_t>(producerUsage | consumerUsage), + accessRegion.left, accessRegion.top, accessRegion.width, + accessRegion.height, outData, acquireFence->dup()); + if (result != 0) { + return GRALLOC1_ERROR_UNSUPPORTED; + } + } else { + acquireFence->waitForever("Gralloc1On0Adapter::lock"); + int result = mModule->lock(mModule, buffer->getHandle(), + static_cast<int32_t>(producerUsage | consumerUsage), + accessRegion.left, accessRegion.top, accessRegion.width, + accessRegion.height, outData); + ALOGV("gralloc0 lock returned %d", result); + if (result != 0) { + return GRALLOC1_ERROR_UNSUPPORTED; + } + } + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::lockFlex( + const std::shared_ptr<Buffer>& /*buffer*/, + gralloc1_producer_usage_t /*producerUsage*/, + gralloc1_consumer_usage_t /*consumerUsage*/, + const gralloc1_rect_t& /*accessRegion*/, + struct android_flex_layout* /*outData*/, + const sp<Fence>& /*acquireFence*/) +{ + // TODO + return GRALLOC1_ERROR_UNSUPPORTED; +} + +gralloc1_error_t Gralloc1On0Adapter::lockYCbCr( + const std::shared_ptr<Buffer>& buffer, + gralloc1_producer_usage_t producerUsage, + gralloc1_consumer_usage_t consumerUsage, + const gralloc1_rect_t& accessRegion, struct android_ycbcr* outData, + const sp<Fence>& acquireFence) +{ + if (mMinorVersion >= 3 && mModule->lockAsync_ycbcr) { + int result = mModule->lockAsync_ycbcr(mModule, buffer->getHandle(), + static_cast<int>(producerUsage | consumerUsage), + accessRegion.left, accessRegion.top, accessRegion.width, + accessRegion.height, outData, acquireFence->dup()); + if (result != 0) { + return GRALLOC1_ERROR_UNSUPPORTED; + } + } else if (mModule->lock_ycbcr) { + acquireFence->waitForever("Gralloc1On0Adapter::lockYCbCr"); + int result = mModule->lock_ycbcr(mModule, buffer->getHandle(), + static_cast<int>(producerUsage | consumerUsage), + accessRegion.left, accessRegion.top, accessRegion.width, + accessRegion.height, outData); + ALOGV("gralloc0 lockYCbCr returned %d", result); + if (result != 0) { + return GRALLOC1_ERROR_UNSUPPORTED; + } + } else { + return GRALLOC1_ERROR_UNSUPPORTED; + } + + return GRALLOC1_ERROR_NONE; +} + +gralloc1_error_t Gralloc1On0Adapter::unlock( + const std::shared_ptr<Buffer>& buffer, + sp<Fence>* outReleaseFence) +{ + if (mMinorVersion >= 3) { + int fenceFd = -1; + int result = mModule->unlockAsync(mModule, buffer->getHandle(), + &fenceFd); + if (result != 0) { + close(fenceFd); + ALOGE("gralloc0 unlockAsync failed: %d", result); + } else { + *outReleaseFence = new Fence(fenceFd); + } + } else { + int result = mModule->unlock(mModule, buffer->getHandle()); + if (result != 0) { + ALOGE("gralloc0 unlock failed: %d", result); + } + } + return GRALLOC1_ERROR_NONE; +} + +std::shared_ptr<Gralloc1On0Adapter::Descriptor> +Gralloc1On0Adapter::getDescriptor(gralloc1_buffer_descriptor_t descriptorId) +{ + std::lock_guard<std::mutex> lock(mDescriptorMutex); + if (mDescriptors.count(descriptorId) == 0) { + return nullptr; + } + + return mDescriptors[descriptorId]; +} + +std::shared_ptr<Gralloc1On0Adapter::Buffer> Gralloc1On0Adapter::getBuffer( + buffer_handle_t bufferHandle) +{ + std::lock_guard<std::mutex> lock(mBufferMutex); + if (mBuffers.count(bufferHandle) == 0) { + return nullptr; + } + + return mBuffers[bufferHandle]; +} + +std::atomic<gralloc1_buffer_descriptor_t> + Gralloc1On0Adapter::sNextBufferDescriptorId(1); + +} // namespace android diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 4fe0946989..97b948d979 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -54,7 +54,7 @@ GraphicBuffer::GraphicBuffer() } GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inUsage) + PixelFormat inFormat, uint32_t inUsage, std::string requestorName) : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) { @@ -64,7 +64,8 @@ GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, format = usage = 0; handle = NULL; - mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage); + mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage, + std::move(requestorName)); } GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, @@ -151,7 +152,7 @@ status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, allocator.free(handle); handle = 0; } - return initSize(inWidth, inHeight, inFormat, inUsage); + return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]"); } bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight, @@ -165,12 +166,12 @@ bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight, } status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inUsage) + PixelFormat inFormat, uint32_t inUsage, std::string requestorName) { GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); uint32_t outStride = 0; - status_t err = allocator.alloc(inWidth, inHeight, inFormat, inUsage, - &handle, &outStride); + status_t err = allocator.allocate(inWidth, inHeight, inFormat, inUsage, + &handle, &outStride, mId, std::move(requestorName)); if (err == NO_ERROR) { width = static_cast<int>(inWidth); height = static_cast<int>(inHeight); @@ -390,7 +391,7 @@ status_t GraphicBuffer::unflatten( mOwner = ownHandle; if (handle != 0) { - status_t err = mBufferMapper.registerBuffer(handle); + status_t err = mBufferMapper.registerBuffer(this); if (err != NO_ERROR) { width = height = stride = format = usage = 0; handle = NULL; diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 9b265af2b4..edfff4d6a5 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -25,6 +25,7 @@ #include <utils/Trace.h> #include <ui/GraphicBufferAllocator.h> +#include <ui/Gralloc1On0Adapter.h> namespace android { // --------------------------------------------------------------------------- @@ -36,20 +37,10 @@ KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList; GraphicBufferAllocator::GraphicBufferAllocator() - : mAllocDev(0) -{ - hw_module_t const* module; - int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); - ALOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); - if (err == 0) { - gralloc_open(module, &mAllocDev); - } -} + : mLoader(std::make_unique<Gralloc1::Loader>()), + mDevice(mLoader->getDevice()) {} -GraphicBufferAllocator::~GraphicBufferAllocator() -{ - gralloc_close(mAllocDev); -} +GraphicBufferAllocator::~GraphicBufferAllocator() {} void GraphicBufferAllocator::dump(String8& result) const { @@ -64,23 +55,23 @@ void GraphicBufferAllocator::dump(String8& result) const for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); if (rec.size) { - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x\n", + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n", list.keyAt(i), rec.size/1024.0f, - rec.width, rec.stride, rec.height, rec.format, rec.usage); + rec.width, rec.stride, rec.height, rec.format, rec.usage, + rec.requestorName.c_str()); } else { - snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %8X | 0x%08x\n", + snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %8X | 0x%08x | %s\n", list.keyAt(i), - rec.width, rec.stride, rec.height, rec.format, rec.usage); + rec.width, rec.stride, rec.height, rec.format, rec.usage, + rec.requestorName.c_str()); } result.append(buffer); total += rec.size; } snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0f); result.append(buffer); - if (mAllocDev->common.version >= 1 && mAllocDev->dump) { - mAllocDev->dump(mAllocDev, buffer, SIZE); - result.append(buffer); - } + std::string deviceDump = mDevice->dump(); + result.append(deviceDump.c_str(), deviceDump.size()); } void GraphicBufferAllocator::dumpToSystemLog() @@ -90,9 +81,9 @@ void GraphicBufferAllocator::dumpToSystemLog() ALOGD("%s", s.string()); } -status_t GraphicBufferAllocator::alloc(uint32_t width, uint32_t height, +status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t usage, buffer_handle_t* handle, - uint32_t* stride) + uint32_t* stride, uint64_t graphicBufferId, std::string requestorName) { ATRACE_CALL(); @@ -101,22 +92,46 @@ status_t GraphicBufferAllocator::alloc(uint32_t width, uint32_t height, if (!width || !height) width = height = 1; - // we have a h/w allocator and h/w buffer is requested - status_t err; - // Filter out any usage bits that should not be passed to the gralloc module usage &= GRALLOC_USAGE_ALLOC_MASK; - int outStride = 0; - err = mAllocDev->alloc(mAllocDev, static_cast<int>(width), - static_cast<int>(height), format, static_cast<int>(usage), handle, - &outStride); - *stride = static_cast<uint32_t>(outStride); + auto descriptor = mDevice->createDescriptor(); + auto error = descriptor->setDimensions(width, height); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error); + return BAD_VALUE; + } + error = descriptor->setFormat(static_cast<android_pixel_format_t>(format)); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("Failed to set format to %d: %d", format, error); + return BAD_VALUE; + } + error = descriptor->setProducerUsage( + static_cast<gralloc1_producer_usage_t>(usage)); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("Failed to set producer usage to %u: %d", usage, error); + return BAD_VALUE; + } + error = descriptor->setConsumerUsage( + static_cast<gralloc1_consumer_usage_t>(usage)); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("Failed to set consumer usage to %u: %d", usage, error); + return BAD_VALUE; + } - ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", - width, height, format, usage, err, strerror(-err)); + error = mDevice->allocate(descriptor, graphicBufferId, handle); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d", + width, height, format, usage, error); + return NO_MEMORY; + } - if (err == NO_ERROR) { + error = mDevice->getStride(*handle, stride); + if (error != GRALLOC1_ERROR_NONE) { + ALOGW("Failed to get stride from buffer: %d", error); + } + + if (error == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); uint32_t bpp = bytesPerPixel(format); @@ -127,27 +142,27 @@ status_t GraphicBufferAllocator::alloc(uint32_t width, uint32_t height, rec.format = format; rec.usage = usage; rec.size = static_cast<size_t>(height * (*stride) * bpp); + rec.requestorName = std::move(requestorName); list.add(*handle, rec); } - return err; + return NO_ERROR; } status_t GraphicBufferAllocator::free(buffer_handle_t handle) { ATRACE_CALL(); - status_t err; - - err = mAllocDev->free(mAllocDev, handle); - ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); - if (err == NO_ERROR) { - Mutex::Autolock _l(sLock); - KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); - list.removeItem(handle); + auto error = mDevice->release(handle); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("Failed to free buffer: %d", error); } - return err; + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + list.removeItem(handle); + + return NO_ERROR; } // --------------------------------------------------------------------------- diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index 90a1c1110d..481d43ce42 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "GraphicBufferMapper" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 #include <stdint.h> #include <errno.h> @@ -31,11 +32,11 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include <ui/Gralloc1On0Adapter.h> #include <ui/GraphicBufferMapper.h> #include <ui/Rect.h> -#include <hardware/gralloc.h> - +#include <system/graphics.h> namespace android { // --------------------------------------------------------------------------- @@ -43,151 +44,247 @@ namespace android { ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) GraphicBufferMapper::GraphicBufferMapper() - : mAllocMod(0) -{ - hw_module_t const* module; - int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); - ALOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); - if (err == 0) { - mAllocMod = reinterpret_cast<gralloc_module_t const *>(module); - } -} + : mLoader(std::make_unique<Gralloc1::Loader>()), + mDevice(mLoader->getDevice()) {} + + status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) { ATRACE_CALL(); - status_t err; - err = mAllocMod->registerBuffer(mAllocMod, handle); + gralloc1_error_t error = mDevice->retain(handle); + ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d", + handle, error); - ALOGW_IF(err, "registerBuffer(%p) failed %d (%s)", - handle, err, strerror(-err)); - return err; + return error; } -status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle) +status_t GraphicBufferMapper::registerBuffer(const GraphicBuffer* buffer) { ATRACE_CALL(); - status_t err; - err = mAllocMod->unregisterBuffer(mAllocMod, handle); + gralloc1_error_t error = mDevice->retain(buffer); + ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d", + buffer->getNativeBuffer()->handle, error); - ALOGW_IF(err, "unregisterBuffer(%p) failed %d (%s)", - handle, err, strerror(-err)); - return err; + return error; } -status_t GraphicBufferMapper::lock(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr) +status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle) { ATRACE_CALL(); - status_t err; - err = mAllocMod->lock(mAllocMod, handle, static_cast<int>(usage), - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr); + gralloc1_error_t error = mDevice->release(handle); + ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d", + handle, error); - ALOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err)); - return err; + return error; } -status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr) -{ - ATRACE_CALL(); - status_t err; - - if (mAllocMod->lock_ycbcr == NULL) { - return -EINVAL; // do not log failure - } +static inline gralloc1_rect_t asGralloc1Rect(const Rect& rect) { + gralloc1_rect_t outRect{}; + outRect.left = rect.left; + outRect.top = rect.top; + outRect.width = rect.width(); + outRect.height = rect.height(); + return outRect; +} - err = mAllocMod->lock_ycbcr(mAllocMod, handle, static_cast<int>(usage), - bounds.left, bounds.top, bounds.width(), bounds.height(), - ycbcr); +status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, + const Rect& bounds, void** vaddr) +{ + return lockAsync(handle, usage, bounds, vaddr, -1); +} - ALOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err)); - return err; +status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, + const Rect& bounds, android_ycbcr *ycbcr) +{ + return lockAsyncYCbCr(handle, usage, bounds, ycbcr, -1); } status_t GraphicBufferMapper::unlock(buffer_handle_t handle) { - ATRACE_CALL(); - status_t err; - - err = mAllocMod->unlock(mAllocMod, handle); - - ALOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); - return err; + int32_t fenceFd = -1; + status_t error = unlockAsync(handle, &fenceFd); + if (error == NO_ERROR) { + sync_wait(fenceFd, -1); + close(fenceFd); + } + return error; } status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd) { ATRACE_CALL(); - status_t err; - - if (mAllocMod->common.module_api_version >= GRALLOC_MODULE_API_VERSION_0_3) { - err = mAllocMod->lockAsync(mAllocMod, handle, static_cast<int>(usage), - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr, fenceFd); - } else { - if (fenceFd >= 0) { - sync_wait(fenceFd, -1); - close(fenceFd); - } - err = mAllocMod->lock(mAllocMod, handle, static_cast<int>(usage), - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr); + + gralloc1_rect_t accessRegion = asGralloc1Rect(bounds); + sp<Fence> fence = new Fence(fenceFd); + gralloc1_error_t error = mDevice->lock(handle, + static_cast<gralloc1_producer_usage_t>(usage), + static_cast<gralloc1_consumer_usage_t>(usage), + &accessRegion, vaddr, fence); + ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle, + error); + + return error; +} + +static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) { + if (plane.bits_per_component != 8) { + ALOGV("Invalid number of bits per component: %d", + plane.bits_per_component); + return false; + } + if (plane.bits_used != 8) { + ALOGV("Invalid number of bits used: %d", plane.bits_used); + return false; + } + + bool hasValidIncrement = plane.h_increment == 1 || + (plane.component != FLEX_COMPONENT_Y && plane.h_increment == 2); + hasValidIncrement = hasValidIncrement && plane.v_increment > 0; + if (!hasValidIncrement) { + ALOGV("Invalid increment: h %d v %d", plane.h_increment, + plane.v_increment); + return false; } - ALOGW_IF(err, "lockAsync(...) failed %d (%s)", err, strerror(-err)); - return err; + return true; } status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd) { ATRACE_CALL(); - status_t err; - - if (mAllocMod->common.module_api_version >= GRALLOC_MODULE_API_VERSION_0_3 - && mAllocMod->lockAsync_ycbcr != NULL) { - err = mAllocMod->lockAsync_ycbcr(mAllocMod, handle, - static_cast<int>(usage), bounds.left, bounds.top, - bounds.width(), bounds.height(), ycbcr, fenceFd); - } else if (mAllocMod->lock_ycbcr != NULL) { - if (fenceFd >= 0) { - sync_wait(fenceFd, -1); - close(fenceFd); - } - err = mAllocMod->lock_ycbcr(mAllocMod, handle, static_cast<int>(usage), - bounds.left, bounds.top, bounds.width(), bounds.height(), - ycbcr); - } else { - if (fenceFd >= 0) { - close(fenceFd); + + gralloc1_rect_t accessRegion = asGralloc1Rect(bounds); + sp<Fence> fence = new Fence(fenceFd); + + if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) { + gralloc1_error_t error = mDevice->lockYCbCr(handle, + static_cast<gralloc1_producer_usage_t>(usage), + static_cast<gralloc1_consumer_usage_t>(usage), + &accessRegion, ycbcr, fence); + ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d", + handle, error); + return error; + } + + uint32_t numPlanes = 0; + gralloc1_error_t error = mDevice->getNumFlexPlanes(handle, &numPlanes); + if (error != GRALLOC1_ERROR_NONE) { + ALOGV("Failed to retrieve number of flex planes: %d", error); + return error; + } + if (numPlanes < 3) { + ALOGV("Not enough planes for YCbCr (%u found)", numPlanes); + return GRALLOC1_ERROR_UNSUPPORTED; + } + + std::vector<android_flex_plane_t> planes(numPlanes); + android_flex_layout_t flexLayout{}; + flexLayout.num_planes = numPlanes; + flexLayout.planes = planes.data(); + + error = mDevice->lockFlex(handle, + static_cast<gralloc1_producer_usage_t>(usage), + static_cast<gralloc1_consumer_usage_t>(usage), + &accessRegion, &flexLayout, fence); + if (error != GRALLOC1_ERROR_NONE) { + ALOGW("lockFlex(%p, ...) failed: %d", handle, error); + return error; + } + if (flexLayout.format != FLEX_FORMAT_YCbCr) { + ALOGV("Unable to convert flex-format buffer to YCbCr"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + + // Find planes + auto yPlane = planes.cend(); + auto cbPlane = planes.cend(); + auto crPlane = planes.cend(); + for (auto planeIter = planes.cbegin(); planeIter != planes.cend(); + ++planeIter) { + if (planeIter->component == FLEX_COMPONENT_Y) { + yPlane = planeIter; + } else if (planeIter->component == FLEX_COMPONENT_Cb) { + cbPlane = planeIter; + } else if (planeIter->component == FLEX_COMPONENT_Cr) { + crPlane = planeIter; } - return -EINVAL; // do not log failure } + if (yPlane == planes.cend()) { + ALOGV("Unable to find Y plane"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + if (cbPlane == planes.cend()) { + ALOGV("Unable to find Cb plane"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + if (crPlane == planes.cend()) { + ALOGV("Unable to find Cr plane"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + + // Validate planes + if (!isValidYCbCrPlane(*yPlane)) { + ALOGV("Y plane is invalid"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + if (!isValidYCbCrPlane(*cbPlane)) { + ALOGV("Cb plane is invalid"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + if (!isValidYCbCrPlane(*crPlane)) { + ALOGV("Cr plane is invalid"); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + if (cbPlane->v_increment != crPlane->v_increment) { + ALOGV("Cb and Cr planes have different step (%d vs. %d)", + cbPlane->v_increment, crPlane->v_increment); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + if (cbPlane->h_increment != crPlane->h_increment) { + ALOGV("Cb and Cr planes have different stride (%d vs. %d)", + cbPlane->h_increment, crPlane->h_increment); + unlock(handle); + return GRALLOC1_ERROR_UNSUPPORTED; + } + + // Pack plane data into android_ycbcr struct + ycbcr->y = yPlane->top_left; + ycbcr->cb = cbPlane->top_left; + ycbcr->cr = crPlane->top_left; + ycbcr->ystride = static_cast<size_t>(yPlane->v_increment); + ycbcr->cstride = static_cast<size_t>(cbPlane->v_increment); + ycbcr->chroma_step = static_cast<size_t>(cbPlane->h_increment); - ALOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err)); - return err; + return error; } status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) { ATRACE_CALL(); - status_t err; - if (mAllocMod->common.module_api_version >= GRALLOC_MODULE_API_VERSION_0_3) { - err = mAllocMod->unlockAsync(mAllocMod, handle, fenceFd); - } else { - *fenceFd = -1; - err = mAllocMod->unlock(mAllocMod, handle); + sp<Fence> fence = Fence::NO_FENCE; + gralloc1_error_t error = mDevice->unlock(handle, &fence); + if (error != GRALLOC1_ERROR_NONE) { + ALOGE("unlock(%p) failed: %d", handle, error); + return error; } - ALOGW_IF(err, "unlockAsync(...) failed %d (%s)", err, strerror(-err)); - return err; + *fenceFd = fence->dup(); + return error; } // --------------------------------------------------------------------------- diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index bef5f02aa3..2e186985e6 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -621,6 +621,24 @@ typedef EGLAPI EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDRO #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/libagl/context.h b/opengl/libagl/context.h index d23f43568e..18ef7d5716 100644 --- a/opengl/libagl/context.h +++ b/opengl/libagl/context.h @@ -26,7 +26,8 @@ #endif #include <private/pixelflinger/ggl_context.h> -#include <hardware/gralloc.h> + +#include <system/window.h> #include <GLES/gl.h> #include <GLES/glext.h> @@ -615,7 +616,7 @@ struct ogles_context_t { culling_t cull; lighting_t lighting; user_clip_planes_t clipPlanes; - compute_iterators_t lerp; __attribute__((aligned(32))); + compute_iterators_t lerp __attribute__((aligned(32))); vertex_t current; vec4_t currentColorClamped; vec3_t currentNormal; diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 92139e9735..c1efd1cae6 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -32,6 +32,7 @@ #include <utils/threads.h> #include <ui/ANativeObjectBase.h> #include <ui/Fence.h> +#include <ui/GraphicBufferMapper.h> #include <EGL/egl.h> #include <EGL/eglext.h> @@ -242,7 +243,6 @@ private: ANativeWindow* nativeWindow; ANativeWindowBuffer* buffer; ANativeWindowBuffer* previousBuffer; - gralloc_module_t const* module; int width; int height; void* bits; @@ -341,16 +341,12 @@ egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat, ANativeWindow* window) - : egl_surface_t(dpy, config, depthFormat), - nativeWindow(window), buffer(0), previousBuffer(0), module(0), - bits(NULL) + : egl_surface_t(dpy, config, depthFormat), + nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL) { - hw_module_t const* pModule; - hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule); - module = reinterpret_cast<gralloc_module_t const*>(pModule); pixelFormatTable = gglGetPixelFormatTable(); - + // keep a reference on the window nativeWindow->common.incRef(&nativeWindow->common); nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width); @@ -440,22 +436,16 @@ void egl_window_surface_v2_t::disconnect() status_t egl_window_surface_v2_t::lock( ANativeWindowBuffer* buf, int usage, void** vaddr) { - int err; - - err = module->lock(module, buf->handle, - usage, 0, 0, buf->width, buf->height, vaddr); - - return err; + auto& mapper = GraphicBufferMapper::get(); + return mapper.lock(buf->handle, usage, + android::Rect(buf->width, buf->height), vaddr); } status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf) { if (!buf) return BAD_VALUE; - int err = NO_ERROR; - - err = module->unlock(module, buf->handle); - - return err; + auto& mapper = GraphicBufferMapper::get(); + return mapper.unlock(buf->handle); } void egl_window_surface_v2_t::copyBlt( diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp index 479bf7ee07..e7fe9d71a3 100644 --- a/opengl/libagl/light.cpp +++ b/opengl/libagl/light.cpp @@ -229,7 +229,7 @@ static inline void validate_light_mvi(ogles_context_t* c) #endif vnorm3(l.normalizedObjPosition.v, l.objPosition.v); } - const vec4_t eyeViewer = { 0, 0, 0x10000, 0 }; + const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}}; #if OBJECT_SPACE_LIGHTING c->transforms.mvui.point3(&c->transforms.mvui, &c->lighting.objViewer, &eyeViewer); diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp index cdeccb3f78..034c857fa7 100644 --- a/opengl/libagl/matrix.cpp +++ b/opengl/libagl/matrix.cpp @@ -253,13 +253,13 @@ void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rh { GLfloat const* const m = lhs.m; for (int i=0 ; i<4 ; i++) { - register const float rhs_i0 = rhs.m[ I(i,0) ]; - register float ri0 = m[ I(0,0) ] * rhs_i0; - register float ri1 = m[ I(0,1) ] * rhs_i0; - register float ri2 = m[ I(0,2) ] * rhs_i0; - register float ri3 = m[ I(0,3) ] * rhs_i0; + const float rhs_i0 = rhs.m[ I(i,0) ]; + float ri0 = m[ I(0,0) ] * rhs_i0; + float ri1 = m[ I(0,1) ] * rhs_i0; + float ri2 = m[ I(0,2) ] * rhs_i0; + float ri3 = m[ I(0,3) ] * rhs_i0; for (int j=1 ; j<4 ; j++) { - register const float rhs_ij = rhs.m[ I(i,j) ]; + const float rhs_ij = rhs.m[ I(i,j) ]; ri0 += m[ I(j,0) ] * rhs_ij; ri1 += m[ I(j,1) ] * rhs_ij; ri2 += m[ I(j,2) ] * rhs_ij; diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index 9aa1c4f867..3fe5ed0630 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -25,6 +25,9 @@ #include <ETC1/etc1.h> +#include <ui/GraphicBufferMapper.h> +#include <ui/Rect.h> + namespace android { // ---------------------------------------------------------------------------- @@ -128,17 +131,11 @@ void ogles_lock_textures(ogles_context_t* c) ANativeWindowBuffer* native_buffer = u.texture->buffer; if (native_buffer) { c->rasterizer.procs.activeTexture(c, i); - hw_module_t const* pModule; - if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule)) - continue; - - gralloc_module_t const* module = - reinterpret_cast<gralloc_module_t const*>(pModule); + auto& mapper = GraphicBufferMapper::get(); void* vaddr; - int err = module->lock(module, native_buffer->handle, - GRALLOC_USAGE_SW_READ_OFTEN, - 0, 0, native_buffer->width, native_buffer->height, + mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN, + Rect(native_buffer->width, native_buffer->height), &vaddr); u.texture->setImageBits(vaddr); @@ -156,14 +153,10 @@ void ogles_unlock_textures(ogles_context_t* c) ANativeWindowBuffer* native_buffer = u.texture->buffer; if (native_buffer) { c->rasterizer.procs.activeTexture(c, i); - hw_module_t const* pModule; - if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule)) - continue; - gralloc_module_t const* module = - reinterpret_cast<gralloc_module_t const*>(pModule); + auto& mapper = GraphicBufferMapper::get(); + mapper.unlock(native_buffer->handle); - module->unlock(module, native_buffer->handle); u.texture->setImageBits(NULL); c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); } @@ -405,7 +398,7 @@ int createTextureSurface(ogles_context_t* c, return 0; } -static size_t dataSizePalette4(int numLevels, int width, int height, int format) +static GLsizei dataSizePalette4(int numLevels, int width, int height, int format) { int indexBits = 8; int entrySize = 0; diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index e793852caa..03abc4974c 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -54,6 +54,8 @@ using namespace android; // Implementation is incomplete and untested. #define ENABLE_EGL_KHR_GL_COLORSPACE 0 +#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0 + // ---------------------------------------------------------------------------- namespace android { @@ -84,6 +86,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 }, }; /* @@ -1196,7 +1207,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(), @@ -1205,6 +1216,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); @@ -1854,7 +1872,9 @@ EGLClientBuffer eglCreateNativeClientBufferANDROID(const EGLint *attrib_list) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); } - GraphicBuffer* gBuffer = new GraphicBuffer(width, height, format, usage); + GraphicBuffer* gBuffer = new GraphicBuffer(width, height, format, usage, + std::string("[eglCreateNativeClientBufferANDROID pid ") + + std::to_string(getpid()) + ']'); const status_t err = gBuffer->initCheck(); if (err != NO_ERROR) { ALOGE("Unable to create native buffer { w=%d, h=%d, f=%d, u=%#x }: %#x", @@ -1935,3 +1955,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_object.cpp b/opengl/libs/EGL/egl_object.cpp index 90f27d1c7d..cfecf77021 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) { if (win) { getDisplay()->onWindowSurfaceCreated(); diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index 8f3b9cb2b5..97eda4ce79 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 374a5de7bc..b9be675686 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/SensorService.cpp b/services/sensorservice/SensorService.cpp index fb83effd25..a24740be74 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()); @@ -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(); } } } @@ -435,15 +449,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; diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index c8de62190f..1e1ea5ab4a 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; diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index fb6307ee59..dc5e97b324 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -44,10 +44,8 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -#LOCAL_CFLAGS += -DENABLE_FENCE_TRACKING -USE_HWC2 := false -ifeq ($(USE_HWC2),true) +ifeq ($(TARGET_USES_HWC2),true) LOCAL_CFLAGS += -DUSE_HWC2 LOCAL_SRC_FILES += \ SurfaceFlinger.cpp \ @@ -148,6 +146,10 @@ ifneq ($(ENABLE_CPUSETS),) LOCAL_CFLAGS += -DENABLE_CPUSETS endif +ifeq ($(TARGET_USES_HWC2),true) + LOCAL_CFLAGS += -DUSE_HWC2 +endif + LOCAL_SRC_FILES := \ main_surfaceflinger.cpp diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 2a025b8a93..e14a59b46d 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -43,10 +43,7 @@ Client::~Client() { const size_t count = mLayers.size(); for (size_t i=0 ; i<count ; i++) { - sp<Layer> layer(mLayers.valueAt(i).promote()); - if (layer != 0) { - mFlinger->removeLayer(layer); - } + mFlinger->removeLayer(mLayers.valueAt(i)); } } @@ -173,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 b6d738125a..12db50568f 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..c67feb30aa 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 = 1; + 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 ed8cc08ff7..4fe3cfd03e 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -149,7 +149,12 @@ Device::~Device() } for (auto element : mDisplays) { - auto display = element.second; + auto display = element.second.lock(); + if (!display) { + ALOGE("~Device: Found a display (%" PRId64 " that has already been" + " destroyed", element.first); + continue; + } DisplayType displayType = HWC2::DisplayType::Invalid; auto error = display->getType(&displayType); @@ -208,6 +213,10 @@ Error Device::createVirtualDisplay(uint32_t width, uint32_t height, ALOGI("Created virtual display"); *format = static_cast<android_pixel_format_t>(intFormat); *outDisplay = getDisplayById(displayId); + if (!*outDisplay) { + ALOGE("Failed to get display by id"); + return Error::BadDisplay; + } (*outDisplay)->setVirtual(); return Error::None; } @@ -289,7 +298,10 @@ void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp) std::shared_ptr<Display> Device::getDisplayById(hwc2_display_t id) { if (mDisplays.count(id) != 0) { - return mDisplays.at(id); + auto strongDisplay = mDisplays[id].lock(); + ALOGE_IF(!strongDisplay, "Display %" PRId64 " is in mDisplays but is no" + " longer alive", id); + return strongDisplay; } auto display = std::make_shared<Display>(*this, id); @@ -305,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 @@ -430,6 +445,7 @@ void Device::destroyVirtualDisplay(hwc2_display_t display) auto error = static_cast<Error>(intError); ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:" " %s (%d)", display, to_string(error).c_str(), intError); + mDisplays.erase(display); } // Display methods @@ -565,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, @@ -583,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; } @@ -789,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); @@ -810,6 +829,7 @@ Error Display::setOutputBuffer(const sp<GraphicBuffer>& buffer, auto handle = buffer->getNativeBuffer()->handle; int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle, fenceFd); + close(fenceFd); return static_cast<Error>(intError); } diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 8ab61e9ced..fb04af877e 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; }; @@ -88,7 +89,8 @@ public: // Other Device methods // This will create a Display if one is not found, but it will not be marked - // as connected + // as connected. This Display may be null if the display has been torn down + // but has not been removed from the map yet. std::shared_ptr<Display> getDisplayById(hwc2_display_t id); bool hasCapability(HWC2::Capability capability) const; @@ -180,8 +182,8 @@ private: HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion; HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder; - std::vector<Capability> mCapabilities; - std::unordered_map<hwc2_display_t, std::shared_ptr<Display>> mDisplays; + std::unordered_set<Capability> mCapabilities; + std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays; HotplugCallback mHotplug; std::vector<std::pair<std::shared_ptr<Display>, Connection>> @@ -280,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; @@ -305,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..8bcee39b9d 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; } } } @@ -2333,7 +2343,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 dc7c355617..bdacc737c6 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 26297943b5..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); @@ -422,6 +467,10 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { Mutex::Autolock _l(mDisplayLock); auto displayId = displayDevice.getHwcDisplayId(); + if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { + ALOGV("Skipping HWComposer prepare for non-HWC display"); + return NO_ERROR; + } if (!isValidDisplay(displayId)) { return BAD_INDEX; } @@ -515,6 +564,11 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { } bool HWComposer::hasDeviceComposition(int32_t displayId) const { + if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { + // Displays without a corresponding HWC display are never composed by + // the device + return false; + } if (!isValidDisplay(displayId)) { ALOGE("hasDeviceComposition: Invalid display %d", displayId); return false; @@ -523,6 +577,11 @@ bool HWComposer::hasDeviceComposition(int32_t displayId) const { } bool HWComposer::hasClientComposition(int32_t displayId) const { + if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { + // Displays without a corresponding HWC display are always composed by + // the client + return true; + } if (!isValidDisplay(displayId)) { ALOGE("hasClientComposition: Invalid display %d", displayId); return true; @@ -671,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 c0baa49f85..61bb0bd8d9 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; @@ -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; @@ -620,8 +619,9 @@ status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const { 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 5b82355dcd..bf9b39c50d 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -122,7 +122,6 @@ private: 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; 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 02477239b7..785df1a288 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); @@ -556,7 +559,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 @@ -718,18 +721,33 @@ void Layer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { return; } - // Client or SolidColor layers - if (mActiveBuffer == nullptr || mActiveBuffer->handle == nullptr || - mHwcLayers[hwcId].forceClientComposition) { - // TODO: This also includes solid color layers, but no API exists to - // setup a solid color layer yet + // Client layers + if (mHwcLayers[hwcId].forceClientComposition || + (mActiveBuffer != nullptr && mActiveBuffer->handle == nullptr)) { ALOGV("[%s] Requesting Client composition", mName.string()); setCompositionType(hwcId, HWC2::Composition::Client); - error = hwcLayer->setBuffer(nullptr, Fence::NO_FENCE); + return; + } + + // SolidColor layers + if (mActiveBuffer == nullptr) { + setCompositionType(hwcId, HWC2::Composition::SolidColor); + + // For now, we only support black for DimLayer + error = hwcLayer->setColor({0, 0, 0, 255}); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + // Clear out the transform, because it doesn't make sense absent a + // source buffer + error = hwcLayer->setTransform(HWC2::Transform::None); if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set null buffer: %s (%d)", mName.string(), + ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(), static_cast<int32_t>(error)); } + return; } @@ -1060,8 +1078,13 @@ void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, } HWC2::Composition Layer::getCompositionType(int32_t hwcId) const { + if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) { + // If we're querying the composition type for a display that does not + // have a HWC counterpart, then it will always be Client + return HWC2::Composition::Client; + } if (mHwcLayers.count(hwcId) == 0) { - ALOGE("getCompositionType called without a valid HWC layer"); + ALOGE("getCompositionType called with an invalid HWC layer"); return HWC2::Composition::Invalid; } return mHwcLayers.at(hwcId).compositionType; @@ -1095,6 +1118,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()) { @@ -1104,6 +1141,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 @@ -1271,9 +1331,14 @@ void Layer::pushPendingState() { // If this transaction is waiting on the receipt of a frame, generate a sync // point and send it to the remote layer. if (mCurrentState.handle != nullptr) { - sp<Handle> handle = static_cast<Handle*>(mCurrentState.handle.get()); - sp<Layer> handleLayer = handle->owner.promote(); - if (handleLayer == nullptr) { + sp<IBinder> strongBinder = mCurrentState.handle.promote(); + sp<Handle> handle = nullptr; + sp<Layer> handleLayer = nullptr; + if (strongBinder != nullptr) { + handle = static_cast<Handle*>(strongBinder.get()); + handleLayer = handle->owner.promote(); + } + if (strongBinder == nullptr || handleLayer == nullptr) { ALOGE("[%s] Unable to promote Layer handle", mName.string()); // If we can't promote the layer we are intended to wait on, // then it is expired or otherwise invalid. Allow this transaction @@ -1361,9 +1426,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(); } } @@ -1573,11 +1639,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; @@ -1674,7 +1744,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); @@ -1706,6 +1777,7 @@ void Layer::onPostComposition() { mFrameTracker.advanceFrame(); mFrameLatencyNeeded = false; } + return frameLatencyNeeded; } #ifdef USE_HWC2 @@ -1754,6 +1826,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); @@ -1766,16 +1845,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, @@ -1867,13 +1949,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 @@ -2016,7 +2105,6 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) if (bufWidth != uint32_t(oldActiveBuffer->width) || bufHeight != uint32_t(oldActiveBuffer->height)) { recomputeVisibleRegions = true; - mFreezePositionUpdates = false; } } @@ -2180,6 +2268,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 ba7184f8e7..65339530b4 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,11 +122,13 @@ public: bool modified; Rect crop; + Rect requestedCrop; + Rect finalCrop; // If set, defers this state update until the Layer identified by handle // receives a frame with the given frameNumber - sp<IBinder> handle; + wp<IBinder> handle; uint64_t frameNumber; // the transparentRegion hint is a bit special, it's latched only @@ -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 @@ -406,6 +411,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 +465,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 +513,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); 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 fd33d5cd3e..36cfa3718a 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -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); } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index 71b6b5915d..f64fe51ef5 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -57,7 +57,6 @@ public: 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; 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 4e97321dbf..3091ec6e03 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 = 1; + 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 @@ -688,6 +698,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) { @@ -706,6 +717,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); @@ -904,6 +1010,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; @@ -939,32 +1054,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 @@ -972,8 +1077,6 @@ void SurfaceFlinger::handleMessageRefresh() { layer->releasePendingBuffer(); } mLayersWithQueuedFrames.clear(); - - previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0); } void SurfaceFlinger::doDebugFlashRegions() @@ -1009,7 +1112,12 @@ void SurfaceFlinger::doDebugFlashRegions() } for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - status_t result = mDisplays[displayId]->prepareFrame(*mHwc); + auto& displayDevice = mDisplays[displayId]; + if (!displayDevice->isDisplayOn()) { + continue; + } + + status_t result = displayDevice->prepareFrame(*mHwc); ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:" " %d (%s)", displayId, result, strerror(-result)); } @@ -1033,11 +1141,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"); @@ -1045,7 +1149,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); @@ -1065,10 +1173,8 @@ void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/) } } -#ifdef ENABLE_FENCE_TRACKING mFenceTracker.addFrame(refreshStartTime, presentFence, hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence()); -#endif if (mAnimCompositionPending) { mAnimCompositionPending = false; @@ -1213,8 +1319,7 @@ void SurfaceFlinger::setUpHWComposer() { } layer->setGeometry(displayDevice); - if (mDebugDisableHWC || mDebugRegion || mDaltonize || - mHasColorMatrix) { + if (mDebugDisableHWC || mDebugRegion) { layer->forceClientComposition(hwcId); } } @@ -1222,6 +1327,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]; @@ -1229,13 +1337,25 @@ 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) { - status_t result = mDisplays[displayId]->prepareFrame(*mHwc); + auto& displayDevice = mDisplays[displayId]; + if (!displayDevice->isDisplayOn()) { + continue; + } + + status_t result = displayDevice->prepareFrame(*mHwc); ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:" " %d (%s)", displayId, result, strerror(-result)); } @@ -1273,6 +1393,9 @@ void SurfaceFlinger::postFramebuffer() for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { auto& displayDevice = mDisplays[displayId]; + if (!displayDevice->isDisplayOn()) { + continue; + } const auto hwcId = displayDevice->getHwcDisplayId(); if (hwcId >= 0) { mHwc->commit(hwcId); @@ -1463,26 +1586,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 @@ -1646,6 +1771,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(); @@ -1905,18 +2032,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); @@ -1931,6 +2047,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"); @@ -2048,6 +2173,10 @@ bool SurfaceFlinger::doComposeSurfaces( } } + if (applyColorMatrix) { + getRenderEngine().setupColorTransform(oldColorMatrix); + } + // disable scissor at the end of the frame mRenderEngine->disableScissor(); return true; @@ -2080,8 +2209,14 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, return NO_ERROR; } -status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) { +status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) { Mutex::Autolock _l(mStateLock); + sp<Layer> layer = weakLayer.promote(); + if (layer == nullptr) { + // The layer has already been removed, carry on + return NO_ERROR; + } + ssize_t index = mCurrentState.layersSortedByZ.remove(layer); if (index >= 0) { mLayersPendingRemoval.push(layer); @@ -2250,10 +2385,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; } } @@ -2290,7 +2425,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) { @@ -2421,14 +2556,7 @@ status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer) { // called by ~LayerCleaner() when all references to the IBinder (handle) // are gone - status_t err = NO_ERROR; - sp<Layer> l(layer.promote()); - if (l != NULL) { - err = removeLayer(l); - ALOGE_IF(err<0 && err != NAME_NOT_FOUND, - "error removing layer=%p (%s)", l.get(), strerror(-err)); - } - return err; + return removeLayer(layer); } // --------------------------------------------------------------------------- @@ -2489,6 +2617,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 @@ -2499,7 +2628,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 @@ -2606,14 +2747,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) { @@ -2734,6 +2873,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 { @@ -2787,6 +2979,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, dumpStaticScreenStats(result); result.append("\n"); + dumpBufferingStats(result); + /* * Dump the visible layer list */ @@ -2869,8 +3063,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); @@ -3025,16 +3218,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; @@ -3042,15 +3243,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(); @@ -3081,6 +3281,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; @@ -3423,6 +3628,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); @@ -3559,6 +3772,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 633e956d24..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(); @@ -311,7 +324,7 @@ private: status_t onLayerDestroyed(const wp<Layer>& layer); // remove a layer from SurfaceFlinger immediately - status_t removeLayer(const sp<Layer>& layer); + status_t removeLayer(const wp<Layer>& layer); // add a layer to SurfaceFlinger status_t addClientLayer(const sp<Client>& client, @@ -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 c71b3bcbfd..e0e4c61e69 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 8f6fd660e4..98943d247c 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 = 1; + 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(); @@ -2096,8 +2125,14 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, return NO_ERROR; } -status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) { +status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) { Mutex::Autolock _l(mStateLock); + sp<Layer> layer = weakLayer.promote(); + if (layer == nullptr) { + // The layer has already been removed, carry on + return NO_ERROR; + } + ssize_t index = mCurrentState.layersSortedByZ.remove(layer); if (index >= 0) { mLayersPendingRemoval.push(layer); @@ -2266,10 +2301,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; } } @@ -2306,7 +2341,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) { @@ -2437,14 +2472,7 @@ status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer) { // called by ~LayerCleaner() when all references to the IBinder (handle) // are gone - status_t err = NO_ERROR; - sp<Layer> l(layer.promote()); - if (l != NULL) { - err = removeLayer(l); - ALOGE_IF(err<0 && err != NAME_NOT_FOUND, - "error removing layer=%p (%s)", l.get(), strerror(-err)); - } - return err; + return removeLayer(layer); } // --------------------------------------------------------------------------- @@ -2505,6 +2533,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 +2544,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 +2663,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 +2789,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 +2892,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 +3132,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 +3194,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 +3651,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 |