Merge "libsigchain: intercept bsd_signal on lp32" am: 1c59b4b4e8 am: 759d63a2f4
am: fb4a57b5c4
Change-Id: Ieb930794c7a91c6392e349a02cbebcbfffe61aa4
diff --git a/cmds/bugreportz/.clang-format b/cmds/bugreportz/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /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 14ba225..880bc75 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 312dceb..7bed284 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -15,89 +15,35 @@
*/
#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");
+#include "bugreportz.h"
+
+static constexpr char PROGRESS_PREFIX[] = "PROGRESS:";
+
+static void write_line(const std::string& line, bool show_progress) {
+ if (line.empty()) return;
+
+ // When not invoked with the -p option, it must skip PROGRESS lines otherwise it
+ // will break adb (which is expecting either OK or FAIL).
+ if (!show_progress && android::base::StartsWith(line, PROGRESS_PREFIX)) return;
+
+ android::base::WriteStringToFd(line, STDOUT_FILENO);
}
-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.
-
- // 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));
- }
-
+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 +55,18 @@
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 0000000..304e4b3
--- /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 0000000..dfef17f
--- /dev/null
+++ b/cmds/bugreportz/bugreportz_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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("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("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(
+ "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 0000000..a3ae1ff
--- /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 85aafce..2bc277e 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -3,6 +3,11 @@
`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, `bugreportz -p` generates progress
+lines in the following format:
+
+- `PROGRESS:<progress>/<total>`, 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/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 517b8a9..3c6d67a 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -54,14 +54,14 @@
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 @@
#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_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 @@
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 @@
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;
}
@@ -587,6 +644,7 @@
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);
@@ -712,8 +770,6 @@
run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
- run_command("RAFT LOGS", 600, SU_PATH, "root", "logcompressor", "-r", RAFT_DIR, NULL);
-
/* show the traces we collected in main(), if that was done */
if (dump_traces_path != NULL) {
dump_file("VM TRACES JUST NOW", dump_traces_path);
@@ -927,7 +983,7 @@
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");
@@ -1066,10 +1122,18 @@
/* 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 */
@@ -1136,6 +1200,7 @@
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 */
@@ -1208,7 +1273,7 @@
add_text_zip_entry("version.txt", version);
}
- if (do_update_progress) {
+ if (do_update_progress && do_broadcast) {
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
"--es", "android.intent.extra.NAME", suffix,
@@ -1280,6 +1345,9 @@
// Dumps systrace right away, otherwise it will be filled with unnecessary events.
dump_systrace();
+ // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+ dump_raft();
+
// Invoking the following dumpsys calls before dump_traces() to try and
// keep the system stats as close to its initial state as possible.
run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
@@ -1293,6 +1361,10 @@
add_dir(RECOVERY_DIR, true);
add_dir(RECOVERY_DATA_DIR, true);
add_dir(LOGPERSIST_DATA_DIR, false);
+ if (!is_user_build()) {
+ add_dir(PROFILE_DATA_DIR_CUR, true);
+ add_dir(PROFILE_DATA_DIR_REF, true);
+ }
add_mountinfo();
dump_iptables();
@@ -1425,9 +1497,9 @@
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 4769974..5e083cc 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -79,7 +79,7 @@
* 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 1f56d21..3448e91 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -38,3 +38,11 @@
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 b33d7ab..a4e9c05 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -1214,6 +1214,11 @@
fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
}
+ if (control_socket_fd >= 0) {
+ dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
+ fsync(control_socket_fd);
+ }
+
int status = property_set(key, value);
if (status) {
MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 7e5bbc5..d19e98a 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 @@
});
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 @@
} 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 d35099a..86df596 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -96,6 +96,17 @@
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_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 e6680ed..2c37ea8 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 @@
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 @@
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_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_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 @@
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 @@
+ (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 @@
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 @@
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 @@
return true;
}
-static int open_output_file(char* file_name, bool recreate, int permissions) {
+static int open_output_file(const char* file_name, bool recreate, int permissions) {
int flags = O_RDWR | O_CREAT;
if (recreate) {
if (unlink(file_name) < 0) {
@@ -1388,19 +1458,110 @@
return analyse_profiles(uid, pkgname);
}
+static const char* parse_null(const char* arg) {
+ if (strcmp(arg, "!") == 0) {
+ return nullptr;
+ } else {
+ return arg;
+ }
+}
+
+int dexopt(const char* 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;
@@ -1410,12 +1571,16 @@
CHECK(pkgname != nullptr);
CHECK(pkgname[0] != 0);
- fd_t reference_profile_fd = -1;
// Public apps should not be compiled with profile information ever. Same goes for the special
// package '*' used for the system server.
+ Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
if (!is_public && pkgname[0] != '*') {
// Open reference profile in read only mode as dex2oat does not get write permissions.
- reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
+ const std::string pkgname_str(pkgname);
+ reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
+ [pkgname_str]() {
+ clear_reference_profile(pkgname_str.c_str());
+ });
// Note: it's OK to not find a profile here.
}
@@ -1423,10 +1588,13 @@
LOG_FATAL("dexopt flags contains unknown fields\n");
}
+ char out_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
return false;
}
+ const char *input_file;
+ char in_odex_path[PKG_PATH_MAX];
switch (dexopt_needed) {
case DEXOPT_DEX2OAT_NEEDED:
input_file = apk_path;
@@ -1445,35 +1613,41 @@
default:
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- exit(72);
+ return 72;
}
+ struct stat input_stat;
memset(&input_stat, 0, sizeof(input_stat));
stat(input_file, &input_stat);
- input_fd = open(input_file, O_RDONLY, 0);
- if (input_fd < 0) {
+ base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
+ if (input_fd.get() < 0) {
ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
return -1;
}
- out_fd = open_output_file(out_path, /*recreate*/true, /*permissions*/0644);
- if (out_fd < 0) {
+ const std::string out_path_str(out_path);
+ Dex2oatFileWrapper<std::function<void ()>> out_fd(
+ open_output_file(out_path, /*recreate*/true, /*permissions*/0644),
+ [out_path_str]() { unlink(out_path_str.c_str()); });
+ if (out_fd.get() < 0) {
ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
- goto fail;
+ return -1;
}
- if (!set_permissions_and_ownership(out_fd, is_public, uid, out_path)) {
- goto fail;
+ if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) {
+ return -1;
}
// Create a swap file if necessary.
+ base::unique_fd swap_fd;
if (ShouldUseSwapFileForDexopt()) {
// Make sure there really is enough space.
+ char swap_file_name[PKG_PATH_MAX];
strcpy(swap_file_name, out_path);
if (add_extension_to_file_name(swap_file_name, ".swap")) {
- swap_fd = open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600);
+ swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
}
- if (swap_fd < 0) {
+ if (swap_fd.get() < 0) {
// Could not create swap file. Optimistically go on and hope that we can compile
// without it.
ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
@@ -1486,111 +1660,108 @@
}
// Avoid generating an app image for extract only since it will not contain any classes.
- strcpy(image_path, out_path);
- trim_extension(image_path);
- if (add_extension_to_file_name(image_path, ".art")) {
- char app_image_format[kPropertyValueMax];
- bool have_app_image_format =
- get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
- // Use app images only if it is enabled (by a set image format) and we are compiling
- // profile-guided (so the app image doesn't conservatively contain all classes).
- if (profile_guided && have_app_image_format) {
- // Recreate is true since we do not want to modify a mapped image. If the app is already
- // running and we modify the image file, it can cause crashes (b/27493510).
- image_fd = open_output_file(image_path, /*recreate*/true, /*permissions*/0600);
- if (image_fd < 0) {
- // Could not create application image file. Go on since we can compile without it.
- ALOGE("installd could not create '%s' for image file during dexopt\n", image_path);
- } else if (!set_permissions_and_ownership(image_fd, is_public, uid, image_path)) {
- image_fd = -1;
- }
- }
- // If we have a valid image file path but no image fd, erase the image file.
- if (image_fd < 0) {
- if (unlink(image_path) < 0) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Couldn't unlink image file " << image_path;
- }
- }
- }
+ Dex2oatFileWrapper<std::function<void ()>> image_fd;
+ const std::string image_path = create_image_filename(out_path);
+ if (!image_path.empty()) {
+ char app_image_format[kPropertyValueMax];
+ bool have_app_image_format =
+ get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+ // Use app images only if it is enabled (by a set image format) and we are compiling
+ // profile-guided (so the app image doesn't conservatively contain all classes).
+ if (profile_guided && have_app_image_format) {
+ // Recreate is true since we do not want to modify a mapped image. If the app is
+ // already running and we modify the image file, it can cause crashes (b/27493510).
+ image_fd.reset(open_output_file(image_path.c_str(),
+ true /*recreate*/,
+ 0600 /*permissions*/),
+ [image_path]() { unlink(image_path.c_str()); }
+ );
+ if (image_fd.get() < 0) {
+ // Could not create application image file. Go on since we can compile without
+ // it.
+ LOG(ERROR) << "installd could not create '"
+ << image_path
+ << "' for image file during dexopt";
+ } else if (!set_permissions_and_ownership(image_fd.get(),
+ is_public,
+ uid,
+ image_path.c_str())) {
+ image_fd.reset(-1);
+ }
+ }
+ // If we have a valid image file path but no image fd, explicitly erase the image file.
+ if (image_fd.get() < 0) {
+ if (unlink(image_path.c_str()) < 0) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+ }
+ }
+ }
}
ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
- pid_t pid;
- pid = fork();
+ pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
drop_capabilities(uid);
SetDex2OatAndPatchOatScheduling(boot_complete);
- if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) {
+ if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
- exit(67);
+ _exit(67);
}
if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
|| dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
- run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
+ run_patchoat(input_fd.get(),
+ out_fd.get(),
+ input_file,
+ out_path,
+ pkgname,
+ instruction_set);
} else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
// Pass dex2oat the relative path to the input file.
const char *input_file_name = get_location_from_path(input_file);
- run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd,
- instruction_set, compiler_filter, vm_safe_mode, debuggable, boot_complete,
- reference_profile_fd, shared_libraries);
+ run_dex2oat(input_fd.get(),
+ out_fd.get(),
+ image_fd.get(),
+ input_file_name,
+ out_path,
+ swap_fd.get(),
+ instruction_set,
+ compiler_filter,
+ vm_safe_mode,
+ debuggable,
+ boot_complete,
+ reference_profile_fd.get(),
+ shared_libraries);
} else {
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- exit(73);
+ _exit(73);
}
- exit(68); /* only get here on exec failure */
+ _exit(68); /* only get here on exec failure */
} else {
- res = wait_child(pid);
+ int res = wait_child(pid);
if (res == 0) {
ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
} else {
ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
- goto fail;
+ return -1;
}
}
+ struct utimbuf ut;
ut.actime = input_stat.st_atime;
ut.modtime = input_stat.st_mtime;
utime(out_path, &ut);
- close(out_fd);
- close(input_fd);
- if (swap_fd >= 0) {
- close(swap_fd);
- }
- if (reference_profile_fd >= 0) {
- close(reference_profile_fd);
- }
- if (image_fd >= 0) {
- close(image_fd);
- }
- return 0;
+ // We've been successful, don't delete output.
+ out_fd.SetCleanup(false);
+ image_fd.SetCleanup(false);
+ reference_profile_fd.SetCleanup(false);
-fail:
- if (out_fd >= 0) {
- close(out_fd);
- unlink(out_path);
- }
- if (input_fd >= 0) {
- close(input_fd);
- }
- if (reference_profile_fd >= 0) {
- close(reference_profile_fd);
- // We failed to compile. Unlink the reference profile. Current profiles are already unlinked
- // when profmoan advises compilation.
- clear_reference_profile(pkgname);
- }
- if (swap_fd >= 0) {
- close(swap_fd);
- }
- if (image_fd >= 0) {
- close(image_fd);
- }
- return -1;
+ return 0;
}
int mark_boot_complete(const char* instruction_set)
@@ -1925,11 +2096,59 @@
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;
@@ -1943,37 +2162,37 @@
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;
+ bool oat_success = move_ab_path(b_path, a_path);
+ bool success;
+
+ if (oat_success) {
+ // Note: we can live without an app image. As such, ignore failure to move the image file.
+ // If we decide to require the app image, or the app image being moved correctly,
+ // then change accordingly.
+ constexpr bool kIgnoreAppImageFailure = true;
+
+ bool art_success = true;
+ if (!a_image_path.empty()) {
+ art_success = move_ab_path(b_image_path, a_image_path);
}
- if (!S_ISREG(s.st_mode)) {
- LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
- // Try to unlink, but swallow errors.
- unlink(b_path.c_str());
- return -1;
- }
+
+ success = art_success || kIgnoreAppImageFailure;
+ } else {
+ // Cleanup: delete B image, ignore errors.
+ unlink(b_image_path.c_str());
+
+ success = false;
}
- // Rename B to A.
- if (!unlink_and_rename(b_path.c_str(), a_path)) {
- // Delete the b_path so we don't try again (or fail earlier).
- if (unlink(b_path.c_str()) != 0) {
- PLOG(ERROR) << "Could not unlink " << b_path;
- }
-
- return -1;
- }
-
- return 0;
+ return success ? 0 : -1;
}
} // namespace installd
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index 7a42c5c..e990f1b 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 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 6a67e29..93e1ce5 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 @@
// 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 @@
}
// 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 3e52346..c90beec 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 1583f86..2bc3dfa 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -219,7 +219,8 @@
// We use otapreopt_chroot to get into the chroot.
static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
-static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
+ char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
// Time to fork and run otapreopt.
// Check that the tool exists.
@@ -231,12 +232,14 @@
pid_t pid = fork();
if (pid == 0) {
- const char* argv[1 + 9 + 1];
+ const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
argv[0] = kOtaPreopt;
- for (size_t i = 1; i <= 9; ++i) {
- argv[i] = arg[i - 1];
+
+ for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
+ argv[i + 1] = args[i];
}
- argv[10] = nullptr;
+
+ argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
execv(argv[0], (char * const *)argv);
PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
@@ -252,22 +255,30 @@
}
}
+static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
+ char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ return dexopt(args);
+}
+
+using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
+ char reply[REPLY_MAX]);
+
static int do_dexopt(char **arg, char reply[REPLY_MAX])
{
- int dexopt_flags = atoi(arg[6]);
- if ((dexopt_flags & DEXOPT_OTA) != 0) {
- return do_ota_dexopt(arg, reply);
+ const char* args[DEXOPT_PARAM_COUNT];
+ for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
+ CHECK(arg[i] != nullptr);
+ args[i] = arg[i];
}
- return dexopt(arg[0], // apk_path
- atoi(arg[1]), // uid
- arg[2], // pkgname
- arg[3], // instruction_set
- atoi(arg[4]), // dexopt_needed
- arg[5], // oat_dir
- dexopt_flags,
- arg[7], // compiler_filter
- parse_null(arg[8]), // volume_uuid
- parse_null(arg[9])); // shared_libraries
+
+ int dexopt_flags = atoi(arg[6]);
+ DexoptFn dexopt_fn;
+ if ((dexopt_flags & DEXOPT_OTA) != 0) {
+ dexopt_fn = do_ota_dexopt;
+ } else {
+ dexopt_fn = do_regular_dexopt;
+ }
+ return dexopt_fn(args, reply);
}
static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
@@ -546,7 +557,7 @@
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 8513695..41732cc 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -26,51 +26,14 @@
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 5ff46e6..5093178 100644
--- a/cmds/installd/installd_deps.h
+++ b/cmds/installd/installd_deps.h
@@ -57,9 +57,6 @@
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 3f76770..5fa972a 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 @@
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 @@
//
// 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 @@
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 @@
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 @@
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;
+ }
+
+ 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 ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
+ 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 @@
}
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
////////////////////////////////////
// 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 @@
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 @@
}
}
+ 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 @@
////////////////////////
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,7 +735,6 @@
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.
const char *file_name_start;
const char *file_name_end;
@@ -526,8 +755,13 @@
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_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 @@
}
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 @@
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 @@
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 @@
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 @@
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 0000000..059ae75
--- /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 f7f69a9..5ea89e6 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 @@
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 @@
}
}
+ // 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 @@
// Now go on and run otapreopt.
- const char* argv[1 + 9 + 1];
- CHECK_EQ(argc, 10);
- argv[0] = "/system/bin/otapreopt";
- for (size_t i = 1; i <= 9; ++i) {
- argv[i] = arg[i];
+ // 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[10] = nullptr;
+ argv[0] = "/system/bin/otapreopt";
+
+ // 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[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 a31734a..8af4a90 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -18,24 +18,49 @@
# 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."
+# 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 +70,7 @@
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 0000000..d51ab70
--- /dev/null
+++ b/cmds/installd/otapreopt_slot.sh
@@ -0,0 +1,36 @@
+#!/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}."
+ rm -rf /data/dalvik-cache/*
+ mv /data/ota/$SLOT_SUFFIX/dalvik-cache/* /data/dalvik-cache/
+ rmdir /data/ota/$SLOT_SUFFIX/dalvik-cache
+ rmdir /data/ota/$SLOT_SUFFIX
+ 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 0000000..436e554
--- /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 e8fcdef..0000000
--- a/cmds/installd/string_helpers.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_OTAPREOPT_STRING_HELPERS_H_
-#define ART_OTAPREOPT_STRING_HELPERS_H_
-
-#include <sstream>
-#include <string>
-
-#include <android-base/macros.h>
-
-namespace android {
-namespace installd {
-
-static inline bool StringStartsWith(const std::string& target,
- const char* prefix) {
- return target.compare(0, strlen(prefix), prefix) == 0;
-}
-
-// Split the input according to the separator character. Doesn't honor quotation.
-static inline std::vector<std::string> Split(const std::string& in, const char separator) {
- if (in.empty()) {
- return std::vector<std::string>();
- }
-
- std::vector<std::string> ret;
- std::stringstream strstr(in);
- std::string token;
-
- while (std::getline(strstr, token, separator)) {
- ret.push_back(token);
- }
-
- return ret;
-}
-
-template <typename StringT>
-static inline std::string Join(const std::vector<StringT>& strings, char separator) {
- if (strings.empty()) {
- return "";
- }
-
- std::string result(strings[0]);
- for (size_t i = 1; i < strings.size(); ++i) {
- result += separator;
- result += strings[i];
- }
- return result;
-}
-
-} // namespace installd
-} // namespace android
-
-#endif // ART_OTAPREOPT_STRING_HELPERS_H_
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c838993..674f760 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 60df356..8123e9b 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -62,6 +62,9 @@
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 @@
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 4b7a706..4ff00b5 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -34,7 +34,6 @@
<!-- device administration -->
<feature name="android.software.device_admin" />
- <feature name="android.software.managed_users" />
<!-- devices with GPS must include device/google/clockwork/gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 67e28da..e202060 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -757,7 +757,15 @@
/** Copy key. */
AKEYCODE_COPY = 278,
/** Paste key. */
- AKEYCODE_PASTE = 279
+ AKEYCODE_PASTE = 279,
+ /** fingerprint navigation key, up. */
+ AKEYCODE_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 09300a2..fe4b1fa 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -66,6 +66,8 @@
virtual void onFrameReplaced(const BufferItem& item) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const override;
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index b2daae4..8ec0546 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -136,6 +136,13 @@
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const;
+ // See IGraphicBufferConsumer::getOccupancyHistory
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) override;
+
+ // See IGraphicBufferConsumer::discardFreeBuffers
+ virtual status_t discardFreeBuffers() override;
+
// dump our state in a String
virtual void dump(String8& result, const char* prefix) const;
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 0371db7..82bc121 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -20,6 +20,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueueDefs.h>
#include <gui/BufferSlot.h>
+#include <gui/OccupancyTracker.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -124,6 +125,11 @@
// all slots, even if they're currently dequeued, queued, or acquired.
void freeAllBuffersLocked();
+ // discardFreeBuffersLocked releases all currently-free buffers held by the
+ // queue, in order to reduce the memory consumption of the queue to the
+ // minimum possible without discarding data.
+ void discardFreeBuffersLocked();
+
// If delta is positive, makes more slots available. If negative, takes
// away slots. Returns false if the request can't be met.
bool adjustAvailableSlotsLocked(int delta);
@@ -322,6 +328,8 @@
// 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 48c3396..838632c 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -170,9 +170,6 @@
// 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 @@
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 9307a26..0490c3c 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -85,6 +85,13 @@
// See IGraphicBufferConsumer::setDefaultBufferDataSpace
status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace);
+ // See IGraphicBufferConsumer::getOccupancyHistory
+ status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory);
+
+ // See IGraphicBufferConsumer::discardFreeBuffers
+ status_t discardFreeBuffers();
+
private:
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
new file mode 100644
index 0000000..4dc7467
--- /dev/null
+++ b/include/gui/FrameTimestamps.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
+#define ANDROID_GUI_FRAMETIMESTAMPS_H
+
+#include <utils/Timers.h>
+#include <utils/Flattenable.h>
+
+namespace android {
+
+struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
+ FrameTimestamps() :
+ frameNumber(0),
+ postedTime(0),
+ acquireTime(0),
+ refreshStartTime(0),
+ glCompositionDoneTime(0),
+ displayRetireTime(0),
+ releaseTime(0) {}
+
+ uint64_t frameNumber;
+ nsecs_t postedTime;
+ nsecs_t acquireTime;
+ nsecs_t refreshStartTime;
+ nsecs_t glCompositionDoneTime;
+ nsecs_t displayRetireTime;
+ nsecs_t releaseTime;
+};
+
+} // namespace android
+#endif
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 3f39799..1efcf3c 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -25,6 +25,8 @@
#include <binder/IInterface.h>
+#include <gui/FrameTimestamps.h>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -78,6 +80,11 @@
// stream is first attached and when it is either detached or replaced by a
// different stream.
virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
+
+ // See IGraphicBufferProducer::getFrameTimestamps
+ // This queries the consumer for the timestamps
+ virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
+ FrameTimestamps* /*outTimestamps*/) const { return false; }
};
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index e983c16..3b10d78 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -27,6 +27,7 @@
#include <binder/IInterface.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
+#include <gui/OccupancyTracker.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -265,6 +266,17 @@
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const = 0;
+ // Retrieves any stored segments of the occupancy history of this
+ // BufferQueue and clears them. Optionally closes out the pending segment if
+ // forceFlush is true.
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) = 0;
+
+ // discardFreeBuffers releases all currently-free buffers held by the queue,
+ // in order to reduce the memory consumption of the queue to the minimum
+ // possible without discarding data.
+ virtual status_t discardFreeBuffers() = 0;
+
// dump state into a string
virtual void dump(String8& result, const char* prefix) const = 0;
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 336ca70..c62bc58 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 @@
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 @@
// 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 @@
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 af26721..74a4123 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -137,6 +137,12 @@
* 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 @@
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 bb79bd0..c27a741 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -77,6 +77,9 @@
* 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 0000000..1d15e7f
--- /dev/null
+++ b/include/gui/OccupancyTracker.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_GUI_OCCUPANCYTRACKER_H
+#define ANDROID_GUI_OCCUPANCYTRACKER_H
+
+#include <binder/Parcelable.h>
+
+#include <utils/Timers.h>
+
+#include <deque>
+#include <unordered_map>
+
+namespace android {
+
+class String8;
+
+class OccupancyTracker
+{
+public:
+ OccupancyTracker()
+ : mPendingSegment(),
+ mSegmentHistory(),
+ mLastOccupancy(0),
+ mLastOccupancyChangeTime(0) {}
+
+ struct Segment : public Parcelable {
+ Segment()
+ : totalTime(0),
+ numFrames(0),
+ occupancyAverage(0.0f),
+ usedThirdBuffer(false) {}
+
+ Segment(nsecs_t totalTime, size_t numFrames, float occupancyAverage,
+ bool usedThirdBuffer)
+ : totalTime(totalTime),
+ numFrames(numFrames),
+ occupancyAverage(occupancyAverage),
+ usedThirdBuffer(usedThirdBuffer) {}
+
+ // Parcelable interface
+ virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const Parcel* parcel) override;
+
+ nsecs_t totalTime;
+ size_t numFrames;
+
+ // Average occupancy of the queue over this segment. (0.0, 1.0) implies
+ // double-buffered, (1.0, 2.0) implies triple-buffered.
+ float occupancyAverage;
+
+ // Whether a third buffer was used at all during this segment (since a
+ // segment could read as double-buffered on average, but still require a
+ // third buffer to avoid jank for some smaller portion)
+ bool usedThirdBuffer;
+ };
+
+ void registerOccupancyChange(size_t occupancy);
+ std::vector<Segment> getSegmentHistory(bool forceFlush);
+
+private:
+ static constexpr size_t MAX_HISTORY_SIZE = 10;
+ static constexpr nsecs_t NEW_SEGMENT_DELAY = ms2ns(100);
+ static constexpr size_t LONG_SEGMENT_THRESHOLD = 3;
+
+ struct PendingSegment {
+ void clear() {
+ totalTime = 0;
+ numFrames = 0;
+ mOccupancyTimes.clear();
+ }
+
+ nsecs_t totalTime;
+ size_t numFrames;
+ std::unordered_map<size_t, nsecs_t> mOccupancyTimes;
+ };
+
+ void recordPendingSegment();
+
+ PendingSegment mPendingSegment;
+ std::deque<Segment> mSegmentHistory;
+
+ size_t mLastOccupancy;
+ nsecs_t mLastOccupancyChangeTime;
+
+}; // class OccupancyTracker
+
+} // namespace android
+
+#endif
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index bcce14a..8177ec6 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -134,6 +134,12 @@
status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]);
+ // See IGraphicBufferProducer::getFrameTimestamps
+ bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+ nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+ nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime);
+
status_t getUniqueId(uint64_t* outId) const;
protected:
@@ -185,13 +191,13 @@
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 @@
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 @@
// 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 312e02f..b8ee331 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -83,6 +83,16 @@
// 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 @@
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 fafd194..5e731c3 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -73,10 +73,11 @@
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 @@
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 b7012eb..0bd14ea 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -319,6 +319,10 @@
DEFINE_KEYCODE(CUT),
DEFINE_KEYCODE(COPY),
DEFINE_KEYCODE(PASTE),
+ DEFINE_KEYCODE(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 03801ca..7ae07ad 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -543,6 +543,8 @@
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_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_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 eeacf43..bb974b3 100644
--- a/include/media/openmax/OMX_Core.h
+++ b/include/media/openmax/OMX_Core.h
@@ -516,6 +516,9 @@
* 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 @@
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 1724576..b688d1d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -78,6 +78,8 @@
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 bf15ee4..2c02431 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -170,7 +170,11 @@
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 @@
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 @@
* 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 @@
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 4885e05..4b3fcc6 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -55,7 +55,7 @@
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 ad73ee7..799944f 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -36,7 +36,6 @@
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 0000000..cf8c173
--- /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 0000000..97c9a89
--- /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/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 5443f09..62ebbd5 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);
status_t free(buffer_handle_t handle);
@@ -86,7 +85,8 @@
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 6099548..a25809c 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -20,13 +20,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <ui/Gralloc1.h>
+
#include <utils/Singleton.h>
-#include <hardware/gralloc.h>
-
-
-struct gralloc_module_t;
-
namespace android {
// ---------------------------------------------------------------------------
@@ -39,6 +36,7 @@
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 @@
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 799b2b5..f5961cf 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -64,6 +64,7 @@
ISurfaceComposer.cpp \
ISurfaceComposerClient.cpp \
LayerState.cpp \
+ OccupancyTracker.cpp \
Sensor.cpp \
SensorEventQueue.cpp \
SensorManager.cpp \
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ccbb5a2..6de98f5 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,6 +61,15 @@
}
}
+bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ }
+ return false;
+}
+
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index cbc8893..ca2a374 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -260,6 +260,7 @@
mCore->mDequeueCondition.broadcast();
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+ mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
VALIDATE_CONSISTENCY();
}
@@ -717,6 +718,19 @@
return mCore->mSidebandStream;
}
+status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Mutex::Autolock lock(mCore->mMutex);
+ *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+ return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::discardFreeBuffers() {
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->discardFreeBuffersLocked();
+ return NO_ERROR;
+}
+
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
const IPCThreadState* ipc = IPCThreadState::self();
const pid_t pid = ipc->getCallingPid();
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index b574f9d..3367830 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -263,6 +263,16 @@
VALIDATE_CONSISTENCY();
}
+void BufferQueueCore::discardFreeBuffersLocked() {
+ for (int s : mFreeBuffers) {
+ mFreeSlots.insert(s);
+ clearBufferSlotLocked(s);
+ }
+ mFreeBuffers.clear();
+
+ VALIDATE_CONSISTENCY();
+}
+
bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
if (delta >= 0) {
// If we're going to fail, do so before modifying anything
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 234a863..b7b56f0 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -509,11 +509,15 @@
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 +900,11 @@
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 +1108,8 @@
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
@@ -1337,14 +1344,6 @@
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);
@@ -1413,6 +1412,22 @@
return NO_ERROR;
}
+bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ ATRACE_CALL();
+ BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
+ sp<IConsumerListener> listener;
+
+ {
+ Mutex::Autolock lock(mCore->mMutex);
+ listener = mCore->mConsumerListener;
+ }
+ if (listener != NULL) {
+ return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ }
+ return false;
+}
+
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
// If we're here, it means that a producer we were connected to died.
// We're guaranteed that we are still connected to it because we remove
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index a6a9712..a1bdf4a 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -235,6 +235,25 @@
return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
}
+status_t ConsumerBase::getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("getOccupancyHistory: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->getOccupancyHistory(forceFlush, outHistory);
+}
+
+status_t ConsumerBase::discardFreeBuffers() {
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->discardFreeBuffers();
+}
+
void ConsumerBase::dump(String8& result) const {
dump(result, "");
}
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 9a0b7a4..3175b91 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,6 +31,7 @@
ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
ON_BUFFER_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
+ GET_FRAME_TIMESTAMPS
};
class BpConsumerListener : public BpInterface<IConsumerListener>
@@ -60,6 +61,42 @@
data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IConsumerListener::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write token: %d", result);
+ return false;
+ }
+ result = data.writeUint64(frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write: %d", result);
+ return false;
+ }
+ result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to transact: %d", result);
+ return false;
+ }
+ bool found = false;
+ result = reply.readBool(&found);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read: %d", result);
+ return false;
+ }
+ if (found) {
+ result = reply.read(*outTimestamps);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read timestamps: %d",
+ result);
+ return false;
+ }
+ }
+ return found;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -88,6 +125,30 @@
CHECK_INTERFACE(IConsumerListener, data, reply);
onSidebandStreamChanged();
return NO_ERROR; }
+ case GET_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(IConsumerListener, data, reply);
+ uint64_t frameNumber = 0;
+ status_t result = data.readUint64(&frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to read: %d", result);
+ return result;
+ }
+ FrameTimestamps timestamps;
+ bool found = getFrameTimestamps(frameNumber, ×tamps);
+ result = reply->writeBool(found);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write: %d", result);
+ return result;
+ }
+ if (found) {
+ result = reply->write(timestamps);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write timestamps: %d", result);
+ return result;
+ }
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index d926b20..e39ff6e 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,6 +51,8 @@
SET_CONSUMER_USAGE_BITS,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
+ GET_OCCUPANCY_HISTORY,
+ DISCARD_FREE_BUFFERS,
DUMP,
};
@@ -260,6 +262,46 @@
return stream;
}
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t error = data.writeBool(forceFlush);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
+ &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply.readParcelableVector(outHistory);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ status_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return result;
+ }
+
+ virtual status_t discardFreeBuffers() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ int32_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return result;
+ }
+
virtual void dump(String8& result, const char* prefix) const {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
@@ -409,6 +451,31 @@
}
return NO_ERROR;
}
+ case GET_OCCUPANCY_HISTORY: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ bool forceFlush = false;
+ status_t error = data.readBool(&forceFlush);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ std::vector<OccupancyTracker::Segment> history;
+ status_t result = getOccupancyHistory(forceFlush, &history);
+ error = reply->writeParcelableVector(history);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply->writeInt32(result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return NO_ERROR;
+ }
+ case DISCARD_FREE_BUFFERS: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ status_t result = discardFreeBuffers();
+ status_t error = reply->writeInt32(result);
+ return error;
+ }
case DUMP: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
String8 result = data.readString8();
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 531bb53..56656e3 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -50,12 +50,12 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 015945a..0a8e6a5 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 @@
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 @@
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 decffbf..47cb047 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -41,7 +41,8 @@
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 @@
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 @@
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 0000000..9687aaf
--- /dev/null
+++ b/libs/gui/OccupancyTracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "OccupancyTracker"
+
+#include <gui/OccupancyTracker.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+status_t OccupancyTracker::Segment::writeToParcel(Parcel* parcel) const {
+ status_t result = parcel->writeInt64(totalTime);
+ if (result != OK) {
+ return result;
+ }
+ result = parcel->writeUint64(static_cast<uint64_t>(numFrames));
+ if (result != OK) {
+ return result;
+ }
+ result = parcel->writeFloat(occupancyAverage);
+ if (result != OK) {
+ return result;
+ }
+ return parcel->writeBool(usedThirdBuffer);
+}
+
+status_t OccupancyTracker::Segment::readFromParcel(const Parcel* parcel) {
+ status_t result = parcel->readInt64(&totalTime);
+ if (result != OK) {
+ return result;
+ }
+ uint64_t uintNumFrames = 0;
+ result = parcel->readUint64(&uintNumFrames);
+ if (result != OK) {
+ return result;
+ }
+ numFrames = static_cast<size_t>(uintNumFrames);
+ result = parcel->readFloat(&occupancyAverage);
+ if (result != OK) {
+ return result;
+ }
+ return parcel->readBool(&usedThirdBuffer);
+}
+
+void OccupancyTracker::registerOccupancyChange(size_t occupancy) {
+ ATRACE_CALL();
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastOccupancyChangeTime;
+ if (delta > NEW_SEGMENT_DELAY) {
+ recordPendingSegment();
+ } else {
+ mPendingSegment.totalTime += delta;
+ if (mPendingSegment.mOccupancyTimes.count(mLastOccupancy)) {
+ mPendingSegment.mOccupancyTimes[mLastOccupancy] += delta;
+ } else {
+ mPendingSegment.mOccupancyTimes[mLastOccupancy] = delta;
+ }
+ }
+ if (occupancy > mLastOccupancy) {
+ ++mPendingSegment.numFrames;
+ }
+ mLastOccupancyChangeTime = now;
+ mLastOccupancy = occupancy;
+}
+
+std::vector<OccupancyTracker::Segment> OccupancyTracker::getSegmentHistory(
+ bool forceFlush) {
+ if (forceFlush) {
+ recordPendingSegment();
+ }
+ std::vector<Segment> segments(mSegmentHistory.cbegin(),
+ mSegmentHistory.cend());
+ mSegmentHistory.clear();
+ return segments;
+}
+
+void OccupancyTracker::recordPendingSegment() {
+ // Only record longer segments to get a better measurement of actual double-
+ // vs. triple-buffered time
+ if (mPendingSegment.numFrames > LONG_SEGMENT_THRESHOLD) {
+ float occupancyAverage = 0.0f;
+ bool usedThirdBuffer = false;
+ for (const auto& timePair : mPendingSegment.mOccupancyTimes) {
+ size_t occupancy = timePair.first;
+ float timeRatio = static_cast<float>(timePair.second) /
+ mPendingSegment.totalTime;
+ occupancyAverage += timeRatio * occupancy;
+ usedThirdBuffer = usedThirdBuffer || (occupancy > 1);
+ }
+ mSegmentHistory.push_front({mPendingSegment.totalTime,
+ mPendingSegment.numFrames, occupancyAverage, usedThirdBuffer});
+ if (mSegmentHistory.size() > MAX_HISTORY_SIZE) {
+ mSegmentHistory.pop_back();
+ }
+ }
+ mPendingSegment.clear();
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index bcfe714..dbf8114 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -48,7 +48,8 @@
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 @@
}
uint64_t Surface::getNextFrameNumber() const {
- return mGraphicBufferProducer->getNextFrameNumber();
+ Mutex::Autolock lock(mMutex);
+ return mNextFrameNumber;
}
String8 Surface::getConsumerName() const {
@@ -133,6 +135,39 @@
outTransformMatrix);
}
+bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+ nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+ nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime) {
+ ATRACE_CALL();
+
+ FrameTimestamps timestamps;
+ bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
+ ×tamps);
+ if (found) {
+ if (outPostedTime) {
+ *outPostedTime = timestamps.postedTime;
+ }
+ if (outAcquireTime) {
+ *outAcquireTime = timestamps.acquireTime;
+ }
+ if (outRefreshStartTime) {
+ *outRefreshStartTime = timestamps.refreshStartTime;
+ }
+ if (outGlCompositionDoneTime) {
+ *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
+ }
+ if (outDisplayRetireTime) {
+ *outDisplayRetireTime = timestamps.displayRetireTime;
+ }
+ if (outReleaseTime) {
+ *outReleaseTime = timestamps.releaseTime;
+ }
+ return true;
+ }
+ return false;
+}
+
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
Surface* c = getSelf(window);
return c->setSwapInterval(interval);
@@ -259,8 +294,10 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 2189047..1c460e8 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 @@
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 @@
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 @@
if (!s) {
return BAD_INDEX;
}
- s->what |= layer_state_t::ePositionAppliesWithResize;
+ s->what |= layer_state_t::eGeometryAppliesWithResize;
return NO_ERROR;
}
@@ -612,6 +614,14 @@
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 @@
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 @@
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 4671e50..33c1d90 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -112,10 +112,10 @@
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 @@
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 85d63b4..8a9eeee 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -34,6 +34,10 @@
#include <gtest/gtest.h>
+#include <thread>
+
+using namespace std::chrono_literals;
+
namespace android {
class BufferQueueTest : public ::testing::Test {
@@ -850,4 +854,217 @@
returnedBuffer->getNativeBuffer()->handle);
}
+TEST_F(BufferQueueTest, TestOccupancyHistory) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 3 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[3] = {};
+ mProducer->setMaxDequeuedBufferCount(3);
+ for (size_t i = 0; i < 3; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 3; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Create 3 segments
+
+ // The first segment is a two-buffer segment, so we only put one buffer into
+ // the queue at a time
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+
+ // Sleep between segments
+ std::this_thread::sleep_for(500ms);
+
+ // The second segment is a double-buffer segment. It starts the same as the
+ // two-buffer segment, but then at the end, we put two buffers in the queue
+ // at the same time before draining it.
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Sleep between segments
+ std::this_thread::sleep_for(500ms);
+
+ // The third segment is a triple-buffer segment, so the queue is switching
+ // between one buffer and two buffers deep.
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Now we read the segments
+ std::vector<OccupancyTracker::Segment> history;
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+
+ // Since we didn't force a flush, we should only get the first two segments
+ // (since the third segment hasn't been closed out by the appearance of a
+ // new segment yet)
+ ASSERT_EQ(2u, history.size());
+
+ // The first segment (which will be history[1], since the newest segment
+ // should be at the front of the vector) should be a two-buffer segment,
+ // which implies that the occupancy average should be between 0 and 1, and
+ // usedThirdBuffer should be false
+ const auto& firstSegment = history[1];
+ ASSERT_EQ(5u, firstSegment.numFrames);
+ ASSERT_LT(0, firstSegment.occupancyAverage);
+ ASSERT_GT(1, firstSegment.occupancyAverage);
+ ASSERT_EQ(false, firstSegment.usedThirdBuffer);
+
+ // The second segment should be a double-buffered segment, which implies that
+ // the occupancy average should be between 0 and 1, but usedThirdBuffer
+ // should be true
+ const auto& secondSegment = history[0];
+ ASSERT_EQ(7u, secondSegment.numFrames);
+ ASSERT_LT(0, secondSegment.occupancyAverage);
+ ASSERT_GT(1, secondSegment.occupancyAverage);
+ ASSERT_EQ(true, secondSegment.usedThirdBuffer);
+
+ // If we read the segments again without flushing, we shouldn't get any new
+ // segments
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+ ASSERT_EQ(0u, history.size());
+
+ // Read the segments again, this time forcing a flush so we get the third
+ // segment
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(true, &history));
+ ASSERT_EQ(1u, history.size());
+
+ // This segment should be a triple-buffered segment, which implies that the
+ // occupancy average should be between 1 and 2, and usedThirdBuffer should
+ // be true
+ const auto& thirdSegment = history[0];
+ ASSERT_EQ(6u, thirdSegment.numFrames);
+ ASSERT_LT(1, thirdSegment.occupancyAverage);
+ ASSERT_GT(2, thirdSegment.occupancyAverage);
+ ASSERT_EQ(true, thirdSegment.usedThirdBuffer);
+}
+
+TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 4 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[4] = {};
+ mProducer->setMaxDequeuedBufferCount(4);
+ for (size_t i = 0; i < 4; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 4; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Get buffers in all states: dequeued, filled, acquired, free
+
+ // Fill 3 buffers
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ // Dequeue 1 buffer
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+
+ // Acquire and free 1 buffer
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ // Acquire 1 buffer, leaving 1 filled buffer in queue
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ // Now discard the free buffers
+ ASSERT_EQ(OK, mConsumer->discardFreeBuffers());
+
+ // Check no free buffers in dump
+ String8 dumpString;
+ mConsumer->dump(dumpString, nullptr);
+
+ // Parse the dump to ensure that all buffer slots that are FREE also
+ // have a null GraphicBuffer
+ // Fragile - assumes the following format for the dump for a buffer entry:
+ // ":%p\][^:]*state=FREE" where %p is the buffer pointer in hex.
+ ssize_t idx = dumpString.find("state=FREE");
+ while (idx != -1) {
+ ssize_t bufferPtrIdx = idx - 1;
+ while (bufferPtrIdx > 0) {
+ if (dumpString[bufferPtrIdx] == ':') {
+ bufferPtrIdx++;
+ break;
+ }
+ bufferPtrIdx--;
+ }
+ ASSERT_GT(bufferPtrIdx, 0) << "Can't parse queue dump to validate";
+ ssize_t nullPtrIdx = dumpString.find("0x0]", bufferPtrIdx);
+ ASSERT_EQ(bufferPtrIdx, nullPtrIdx) << "Free buffer not discarded";
+ idx = dumpString.find("FREE", idx + 1);
+ }
+}
+
} // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 45b6463..9f33047 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -370,13 +370,16 @@
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 ee6c093..e690ede 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -17,7 +17,7 @@
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_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 0000000..4c73ce4
--- /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 0000000..d5b88de
--- /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 4fe0946..f28af6e 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -169,8 +169,8 @@
{
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);
if (err == NO_ERROR) {
width = static_cast<int>(inWidth);
height = static_cast<int>(inHeight);
@@ -390,7 +390,7 @@
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 9b265af..3b83fb6 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 @@
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
{
@@ -77,10 +68,8 @@
}
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 +79,9 @@
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)
{
ATRACE_CALL();
@@ -101,22 +90,46 @@
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);
@@ -130,24 +143,23 @@
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 90a1c11..481d43c 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 @@
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::registerBuffer(const GraphicBuffer* buffer)
+{
+ ATRACE_CALL();
+
+ gralloc1_error_t error = mDevice->retain(buffer);
+ ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
+ buffer->getNativeBuffer()->handle, error);
+
+ return error;
}
status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle)
{
ATRACE_CALL();
- status_t err;
- err = mAllocMod->unregisterBuffer(mAllocMod, handle);
+ gralloc1_error_t error = mDevice->release(handle);
+ ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
+ 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)
-{
- ATRACE_CALL();
- status_t err;
-
- err = mAllocMod->lock(mAllocMod, handle, static_cast<int>(usage),
- bounds.left, bounds.top, bounds.width(), bounds.height(),
- vaddr);
-
- ALOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err));
- return err;
+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;
}
-status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr)
+status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
+ const Rect& bounds, void** vaddr)
{
- ATRACE_CALL();
- status_t err;
+ return lockAsync(handle, usage, bounds, vaddr, -1);
+}
- if (mAllocMod->lock_ycbcr == NULL) {
- return -EINVAL; // do not log failure
- }
-
- err = mAllocMod->lock_ycbcr(mAllocMod, handle, static_cast<int>(usage),
- bounds.left, bounds.top, bounds.width(), bounds.height(),
- ycbcr);
-
- 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;
}
- ALOGW_IF(err, "lockAsync(...) failed %d (%s)", err, strerror(-err));
- return err;
+ 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;
+ }
+
+ 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);
- }
- return -EINVAL; // do not log failure
+ 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;
}
- ALOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err));
- return err;
+ 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;
+ }
+ }
+ 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);
+
+ 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 c72388a..8e69330 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -628,6 +628,24 @@
#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 d23f435..18ef7d5 100644
--- a/opengl/libagl/context.h
+++ b/opengl/libagl/context.h
@@ -26,7 +26,8 @@
#endif
#include <private/pixelflinger/ggl_context.h>
-#include <hardware/gralloc.h>
+
+#include <system/window.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
@@ -615,7 +616,7 @@
culling_t cull;
lighting_t lighting;
user_clip_planes_t clipPlanes;
- compute_iterators_t lerp; __attribute__((aligned(32)));
+ compute_iterators_t lerp __attribute__((aligned(32)));
vertex_t current;
vec4_t currentColorClamped;
vec3_t currentNormal;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 92139e9..c1efd1c 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -32,6 +32,7 @@
#include <utils/threads.h>
#include <ui/ANativeObjectBase.h>
#include <ui/Fence.h>
+#include <ui/GraphicBufferMapper.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -242,7 +243,6 @@
ANativeWindow* nativeWindow;
ANativeWindowBuffer* buffer;
ANativeWindowBuffer* previousBuffer;
- gralloc_module_t const* module;
int width;
int height;
void* bits;
@@ -341,16 +341,12 @@
EGLConfig config,
int32_t depthFormat,
ANativeWindow* window)
- : egl_surface_t(dpy, config, depthFormat),
- nativeWindow(window), buffer(0), previousBuffer(0), module(0),
- bits(NULL)
+ : egl_surface_t(dpy, config, depthFormat),
+ nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL)
{
- hw_module_t const* pModule;
- hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
- module = reinterpret_cast<gralloc_module_t const*>(pModule);
pixelFormatTable = gglGetPixelFormatTable();
-
+
// keep a reference on the window
nativeWindow->common.incRef(&nativeWindow->common);
nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width);
@@ -440,22 +436,16 @@
status_t egl_window_surface_v2_t::lock(
ANativeWindowBuffer* buf, int usage, void** vaddr)
{
- int err;
-
- err = module->lock(module, buf->handle,
- usage, 0, 0, buf->width, buf->height, vaddr);
-
- return err;
+ auto& mapper = GraphicBufferMapper::get();
+ return mapper.lock(buf->handle, usage,
+ android::Rect(buf->width, buf->height), vaddr);
}
status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf)
{
if (!buf) return BAD_VALUE;
- int err = NO_ERROR;
-
- err = module->unlock(module, buf->handle);
-
- return err;
+ auto& mapper = GraphicBufferMapper::get();
+ return mapper.unlock(buf->handle);
}
void egl_window_surface_v2_t::copyBlt(
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 479bf7e..e7fe9d7 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -229,7 +229,7 @@
#endif
vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
}
- const vec4_t eyeViewer = { 0, 0, 0x10000, 0 };
+ const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}};
#if OBJECT_SPACE_LIGHTING
c->transforms.mvui.point3(&c->transforms.mvui,
&c->lighting.objViewer, &eyeViewer);
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index cdeccb3..034c857 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -253,13 +253,13 @@
{
GLfloat const* const m = lhs.m;
for (int i=0 ; i<4 ; i++) {
- register const float rhs_i0 = rhs.m[ I(i,0) ];
- register float ri0 = m[ I(0,0) ] * rhs_i0;
- register float ri1 = m[ I(0,1) ] * rhs_i0;
- register float ri2 = m[ I(0,2) ] * rhs_i0;
- register float ri3 = m[ I(0,3) ] * rhs_i0;
+ const float rhs_i0 = rhs.m[ I(i,0) ];
+ float ri0 = m[ I(0,0) ] * rhs_i0;
+ float ri1 = m[ I(0,1) ] * rhs_i0;
+ float ri2 = m[ I(0,2) ] * rhs_i0;
+ float ri3 = m[ I(0,3) ] * rhs_i0;
for (int j=1 ; j<4 ; j++) {
- register const float rhs_ij = rhs.m[ I(i,j) ];
+ const float rhs_ij = rhs.m[ I(i,j) ];
ri0 += m[ I(j,0) ] * rhs_ij;
ri1 += m[ I(j,1) ] * rhs_ij;
ri2 += m[ I(j,2) ] * rhs_ij;
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 9aa1c4f..3fe5ed0 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -25,6 +25,9 @@
#include <ETC1/etc1.h>
+#include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -128,17 +131,11 @@
ANativeWindowBuffer* native_buffer = u.texture->buffer;
if (native_buffer) {
c->rasterizer.procs.activeTexture(c, i);
- hw_module_t const* pModule;
- if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
- continue;
- gralloc_module_t const* module =
- reinterpret_cast<gralloc_module_t const*>(pModule);
-
+ auto& mapper = GraphicBufferMapper::get();
void* vaddr;
- int err = module->lock(module, native_buffer->handle,
- GRALLOC_USAGE_SW_READ_OFTEN,
- 0, 0, native_buffer->width, native_buffer->height,
+ mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN,
+ Rect(native_buffer->width, native_buffer->height),
&vaddr);
u.texture->setImageBits(vaddr);
@@ -156,14 +153,10 @@
ANativeWindowBuffer* native_buffer = u.texture->buffer;
if (native_buffer) {
c->rasterizer.procs.activeTexture(c, i);
- hw_module_t const* pModule;
- if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
- continue;
- gralloc_module_t const* module =
- reinterpret_cast<gralloc_module_t const*>(pModule);
+ auto& mapper = GraphicBufferMapper::get();
+ mapper.unlock(native_buffer->handle);
- module->unlock(module, native_buffer->handle);
u.texture->setImageBits(NULL);
c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
}
@@ -405,7 +398,7 @@
return 0;
}
-static size_t dataSizePalette4(int numLevels, int width, int height, int format)
+static GLsizei dataSizePalette4(int numLevels, int width, int height, int format)
{
int indexBits = 8;
int entrySize = 0;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 5de5599..f5dfb92 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -50,6 +50,8 @@
using namespace android;
+#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0
+
// ----------------------------------------------------------------------------
namespace android {
@@ -80,6 +82,9 @@
"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
@@ -201,6 +206,12 @@
(__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
{ "eglCreateStreamFromFileDescriptorKHR",
(__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+ // EGL_ANDROID_get_frame_timestamps
+ { "eglGetFrameTimestampsANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglQueryTimestampSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
};
/*
@@ -1174,7 +1185,7 @@
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(),
@@ -1183,6 +1194,13 @@
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);
@@ -1914,3 +1932,105 @@
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 90f27d1..cfecf77 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
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 8f3b9cb..97eda4c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,6 +139,7 @@
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 0000000..30337ad
--- /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 8f1eaf3..f0c024e 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -19,4 +19,11 @@
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 374a5de..b9be675 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -134,6 +134,14 @@
{ 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 fb83eff..0c4dc26 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -435,15 +435,15 @@
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 dc49b06..5f743fc 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 {
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index ce858d2..dc708e8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -44,10 +44,8 @@
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 \
@@ -149,6 +147,10 @@
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 2a025b8..415bdca 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -173,5 +173,15 @@
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 b6d7381..12db505 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -63,6 +63,8 @@
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 37b6420..c67feb3 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -383,6 +383,13 @@
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 a67b3ff..5c2c0ad 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -415,6 +415,17 @@
}
// ----------------------------------------------------------------------------
+#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 dd9b104..105e980 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -182,6 +182,11 @@
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 @@
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 ed8cc08..4fe3cfd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -149,7 +149,12 @@
}
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 @@
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 @@
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 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 8ab61e9..fb04af8 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 @@
std::string dump() const;
- const std::vector<Capability>& getCapabilities() const {
+ const std::unordered_set<Capability>& getCapabilities() const {
return mCapabilities;
};
@@ -88,7 +89,8 @@
// 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 @@
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 @@
[[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 @@
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 2641ee6..8bcee39 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -75,7 +75,7 @@
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 @@
&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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 dc7c355..bdacc73 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -213,7 +213,7 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 2629794..c87ba72 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -144,6 +144,11 @@
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 @@
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 @@
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 @@
}
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::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 @@
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 b88e250..41671f6 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -81,6 +81,8 @@
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 @@
// 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 @@
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 @@
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 4afd8a2..ef41658 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 @@
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 @@
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 @@
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 c861817..170e382 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 @@
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 @@
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 c0baa49..61bb0bd8 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -304,8 +304,11 @@
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 @@
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 @@
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 5b82355..bf9b39c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -122,7 +122,6 @@
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 feb8936..a104e8f 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 @@
}
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 @@
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 e816437..d21b155 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 @@
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 885d712..0e18a93 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -26,7 +26,9 @@
FenceTracker::FenceTracker() :
mFrameCounter(0),
mOffset(0),
- mFrames() {}
+ mFrames(),
+ mMutex() {
+}
void FenceTracker::dump(String8* outString) {
Mutex::Autolock lock(mMutex);
@@ -135,31 +137,30 @@
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 @@
}
#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 @@
mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
mFrameCounter++;
+}
+bool FenceTracker::getFrameTimestamps(const Layer& layer,
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+ Mutex::Autolock lock(mMutex);
checkFencesForCompletion();
+ int32_t layerId = layer.getSequence();
+
+ size_t i = 0;
+ for (; i < MAX_FRAME_HISTORY; i++) {
+ if (mFrames[i].layers.count(layerId) &&
+ mFrames[i].layers[layerId].frameNumber == frameNumber) {
+ break;
+ }
+ }
+ if (i == MAX_FRAME_HISTORY) {
+ return false;
+ }
+
+ const FrameRecord& frameRecord = mFrames[i];
+ const LayerRecord& layerRecord = mFrames[i].layers[layerId];
+ outTimestamps->frameNumber = frameNumber;
+ outTimestamps->postedTime = layerRecord.postedTime;
+ outTimestamps->acquireTime = layerRecord.acquireTime;
+ outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
+ outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
+ outTimestamps->displayRetireTime = frameRecord.retireTime;
+ outTimestamps->releaseTime = layerRecord.releaseTime;
+ return true;
}
} // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
index de99820..4cb14a5 100644
--- a/services/surfaceflinger/FenceTracker.h
+++ b/services/surfaceflinger/FenceTracker.h
@@ -29,7 +29,7 @@
namespace android {
class Layer;
-
+struct FrameTimestamps;
/*
* Keeps a circular buffer of fence/timestamp data for the last N frames in
* SurfaceFlinger. Gets timestamps for fences after they have signaled.
@@ -40,9 +40,11 @@
void dump(String8* outString);
void addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
+ bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
+ FrameTimestamps* outTimestamps);
protected:
- static constexpr size_t MAX_FRAME_HISTORY = 128;
+ static constexpr size_t MAX_FRAME_HISTORY = 8;
struct LayerRecord {
String8 name; // layer name
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 44db852..efcbe80 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 @@
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);
@@ -716,18 +719,33 @@
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 null buffer: %s (%d)", mName.string(),
+ 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 clear transform: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
return;
}
@@ -1058,8 +1076,13 @@
}
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;
@@ -1093,6 +1116,20 @@
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()) {
@@ -1102,6 +1139,29 @@
}
}
+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
@@ -1359,9 +1419,10 @@
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();
}
}
@@ -1571,11 +1632,15 @@
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;
@@ -1672,7 +1737,8 @@
return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
}
-void Layer::onPostComposition() {
+bool Layer::onPostComposition() {
+ bool frameLatencyNeeded = mFrameLatencyNeeded;
if (mFrameLatencyNeeded) {
nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
@@ -1704,6 +1770,7 @@
mFrameTracker.advanceFrame();
mFrameLatencyNeeded = false;
}
+ return frameLatencyNeeded;
}
#ifdef USE_HWC2
@@ -1752,6 +1819,13 @@
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);
@@ -1764,16 +1838,19 @@
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,
@@ -1865,13 +1942,20 @@
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
@@ -2014,7 +2098,6 @@
if (bufWidth != uint32_t(oldActiveBuffer->width) ||
bufHeight != uint32_t(oldActiveBuffer->height)) {
recomputeVisibleRegions = true;
- mFreezePositionUpdates = false;
}
}
@@ -2178,6 +2261,24 @@
*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 ba7184f..c070539 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -96,7 +96,9 @@
Transform transform;
inline bool operator ==(const Geometry& rhs) const {
- return (w == rhs.w && h == rhs.h);
+ return (w == rhs.w && h == rhs.h) &&
+ (transform.tx() == rhs.transform.tx()) &&
+ (transform.ty() == rhs.transform.ty());
}
inline bool operator !=(const Geometry& rhs) const {
return !operator ==(rhs);
@@ -120,6 +122,8 @@
bool modified;
Rect crop;
+ Rect requestedCrop;
+
Rect finalCrop;
// If set, defers this state update until the Layer identified by handle
@@ -156,7 +160,7 @@
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 @@
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 @@
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 @@
// 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 @@
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 34dc24b..974c7a3 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -134,31 +134,12 @@
}
-/* 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 @@
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 fd33d5c..36cfa37 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -127,10 +127,6 @@
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 71b6b59..f64fe51 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -57,7 +57,6 @@
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 27357b9..d6a032f 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -436,6 +436,13 @@
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 9cc1ed7..0259881 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -63,6 +63,8 @@
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 c700116..13e440d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -165,7 +165,6 @@
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
- mDaltonize(false),
mHasColorMatrix(false),
mHasPoweredOff(false),
mFrameBuckets(),
@@ -180,9 +179,6 @@
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);
@@ -196,6 +192,10 @@
}
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");
}
void SurfaceFlinger::onFirstRef()
@@ -478,6 +478,13 @@
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);
@@ -510,6 +517,8 @@
// set initial conditions (e.g. unblank default device)
initializeDisplays();
+ mRenderEngine->primeCache();
+
// start boot animation
startBootAnim();
@@ -618,9 +627,6 @@
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
@@ -701,6 +707,7 @@
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) {
@@ -719,6 +726,101 @@
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);
@@ -917,6 +1019,15 @@
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;
@@ -952,32 +1063,22 @@
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
@@ -985,8 +1086,6 @@
layer->releasePendingBuffer();
}
mLayersWithQueuedFrames.clear();
-
- previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0);
}
void SurfaceFlinger::doDebugFlashRegions()
@@ -1022,7 +1121,12 @@
}
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));
}
@@ -1046,11 +1150,7 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
{
ATRACE_CALL();
ALOGV("postComposition");
@@ -1058,7 +1158,11 @@
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition();
+ if (frameLatched) {
+ recordBufferingStats(layers[i]->getName().string(),
+ layers[i]->getOccupancyHistory(false));
+ }
}
sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
@@ -1078,10 +1182,8 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
mFenceTracker.addFrame(refreshStartTime, presentFence,
hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1226,8 +1328,7 @@
}
layer->setGeometry(displayDevice);
- if (mDebugDisableHWC || mDebugRegion || mDaltonize ||
- mHasColorMatrix) {
+ if (mDebugDisableHWC || mDebugRegion) {
layer->forceClientComposition(hwcId);
}
}
@@ -1235,6 +1336,9 @@
}
}
+
+ mat4 colorMatrix = mColorMatrix * mDaltonizer();
+
// Set the per-frame data
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
@@ -1242,13 +1346,25 @@
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));
}
@@ -1286,6 +1402,9 @@
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);
@@ -1659,6 +1778,8 @@
if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+ recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
+ mLayersPendingRemoval[i]->getOccupancyHistory(true));
mLayersPendingRemoval[i]->onRemoved();
}
mLayersPendingRemoval.clear();
@@ -1918,18 +2039,7 @@
}
}
- 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);
@@ -1944,6 +2054,15 @@
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");
@@ -2061,6 +2180,10 @@
}
}
+ if (applyColorMatrix) {
+ getRenderEngine().setupColorTransform(oldColorMatrix);
+ }
+
// disable scissor at the end of the frame
mRenderEngine->disableScissor();
return true;
@@ -2264,10 +2387,10 @@
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;
}
}
@@ -2304,7 +2427,7 @@
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) {
@@ -2620,14 +2743,12 @@
dumpAll = false;
}
-#ifdef ENABLE_FENCE_TRACKING
if ((index < numArgs) &&
(args[index] == String16("--fences"))) {
index++;
mFenceTracker.dump(&result);
dumpAll = false;
}
-#endif
}
if (dumpAll) {
@@ -2748,6 +2869,59 @@
NUM_BUCKETS - 1, bucketTimeSec, percent);
}
+void SurfaceFlinger::recordBufferingStats(const char* layerName,
+ std::vector<OccupancyTracker::Segment>&& history) {
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ auto& stats = mBufferingStats[layerName];
+ for (const auto& segment : history) {
+ if (!segment.usedThirdBuffer) {
+ stats.twoBufferTime += segment.totalTime;
+ }
+ if (segment.occupancyAverage < 1.0f) {
+ stats.doubleBufferedTime += segment.totalTime;
+ } else if (segment.occupancyAverage < 2.0f) {
+ stats.tripleBufferedTime += segment.totalTime;
+ }
+ ++stats.numSegments;
+ stats.totalTime += segment.totalTime;
+ }
+}
+
+void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+ result.append("Buffering stats:\n");
+ result.append(" [Layer name] <Active time> <Two buffer> "
+ "<Double buffered> <Triple buffered>\n");
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ typedef std::tuple<std::string, float, float, float> BufferTuple;
+ std::map<float, BufferTuple, std::greater<float>> sorted;
+ for (const auto& statsPair : mBufferingStats) {
+ const char* name = statsPair.first.c_str();
+ const BufferingStats& stats = statsPair.second;
+ if (stats.numSegments == 0) {
+ continue;
+ }
+ float activeTime = ns2ms(stats.totalTime) / 1000.0f;
+ float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
+ stats.totalTime;
+ float doubleBufferRatio = static_cast<float>(
+ stats.doubleBufferedTime) / stats.totalTime;
+ float tripleBufferRatio = static_cast<float>(
+ stats.tripleBufferedTime) / stats.totalTime;
+ sorted.insert({activeTime, {name, twoBufferRatio,
+ doubleBufferRatio, tripleBufferRatio}});
+ }
+ for (const auto& sortedPair : sorted) {
+ float activeTime = sortedPair.first;
+ const BufferTuple& values = sortedPair.second;
+ result.appendFormat(" [%s] %.2f %.3f %.3f %.3f\n",
+ std::get<0>(values).c_str(), activeTime,
+ std::get<1>(values), std::get<2>(values),
+ std::get<3>(values));
+ }
+ result.append("\n");
+}
+
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
@@ -2801,6 +2975,8 @@
dumpStaticScreenStats(result);
result.append("\n");
+ dumpBufferingStats(result);
+
/*
* Dump the visible layer list
*/
@@ -2883,8 +3059,7 @@
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);
@@ -3039,16 +3214,24 @@
// 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;
@@ -3056,15 +3239,14 @@
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();
@@ -3437,6 +3619,14 @@
// 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);
@@ -3573,6 +3763,11 @@
}
}
+bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+ return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
+}
+
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 633e956..f50f9e7 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 @@
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 @@
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode);
+ // Called on the main thread in response to setActiveColorMode()
+ void setActiveColorModeInternal(const sp<DisplayDevice>& hw, android_color_mode_t colorMode);
+
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -364,6 +377,16 @@
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 @@
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 @@
RenderEngine* mRenderEngine;
nsecs_t mBootTime;
bool mGpuToCpuSupported;
- bool mDropMissedFrames;
sp<EventThread> mEventThread;
sp<EventThread> mSFEventThread;
sp<EventControlThread> mEventControlThread;
@@ -477,6 +506,8 @@
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,9 @@
bool mBootFinished;
bool mForceFullDamage;
FenceTracker mFenceTracker;
+#ifdef USE_HWC2
+ bool mPropagateBackpressure = true;
+#endif
// these are thread safe
mutable MessageQueue mEventQueue;
@@ -515,8 +549,11 @@
*/
Daltonizer mDaltonizer;
+#ifndef USE_HWC2
bool mDaltonize;
+#endif
+ mat4 mPreviousColorMatrix;
mat4 mColorMatrix;
bool mHasColorMatrix;
@@ -526,6 +563,29 @@
nsecs_t mFrameBuckets[NUM_BUCKETS];
nsecs_t mTotalTime;
std::atomic<nsecs_t> mLastSwapTime;
+
+ // Double- vs. triple-buffering stats
+ struct BufferingStats {
+ BufferingStats()
+ : numSegments(0),
+ totalTime(0),
+ twoBufferTime(0),
+ doubleBufferedTime(0),
+ tripleBufferedTime(0) {}
+
+ size_t numSegments;
+ nsecs_t totalTime;
+
+ // "Two buffer" means that a third buffer was never used, whereas
+ // "double-buffered" means that on average the segment only used two
+ // buffers (though it may have used a third for some part of the
+ // segment)
+ nsecs_t twoBufferTime;
+ nsecs_t doubleBufferedTime;
+ nsecs_t tripleBufferedTime;
+ };
+ mutable Mutex mBufferingStatsMutex;
+ std::unordered_map<std::string, BufferingStats> mBufferingStats;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index c71b3bc..e0e4c61 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 @@
}
bool SurfaceFlingerConsumer::getTransformToDisplayInverse() const {
+ Mutex::Autolock lock(mMutex);
return mTransformToDisplayInverse;
}
@@ -251,6 +253,12 @@
}
}
+bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ sp<const Layer> l = mLayer.promote();
+ return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 51b002f..4271039 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -23,6 +23,8 @@
namespace android {
// ----------------------------------------------------------------------------
+class Layer;
+
/*
* This is a thin wrapper around GLConsumer.
*/
@@ -35,10 +37,10 @@
};
SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
- uint32_t tex)
+ uint32_t tex, const Layer* layer)
: GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
mTransformToDisplayInverse(false), mSurfaceDamage(),
- mPrevReleaseFence(Fence::NO_FENCE)
+ mPrevReleaseFence(Fence::NO_FENCE), mLayer(layer)
{}
class BufferRejecter {
@@ -64,8 +66,9 @@
// 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 @@
void releasePendingBuffer();
#endif
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const override;
+
private:
virtual void onSidebandStreamChanged();
@@ -103,6 +109,9 @@
// The release fence of the already displayed buffer (previous frame).
sp<Fence> mPrevReleaseFence;
+
+ // The layer for this SurfaceFlingerConsumer
+ wp<const Layer> mLayer;
};
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 7f3b269..e7f911e 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 @@
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);
@@ -462,6 +461,14 @@
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 +534,8 @@
// set initial conditions (e.g. unblank default device)
initializeDisplays();
+ mRenderEngine->primeCache();
+
// start boot animation
startBootAnim();
}
@@ -565,20 +574,8 @@
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 +637,6 @@
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 +737,55 @@
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 +988,14 @@
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 +1053,16 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
{
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition();
+ if (frameLatched) {
+ recordBufferingStats(layers[i]->getName().string(),
+ layers[i]->getOccupancyHistory(false));
+ }
}
const HWComposer& hwc = getHwComposer();
@@ -1059,10 +1083,8 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
mFenceTracker.addFrame(refreshStartTime, presentFence,
hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1666,6 +1688,8 @@
if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+ recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
+ mLayersPendingRemoval[i]->getOccupancyHistory(true));
mLayersPendingRemoval[i]->onRemoved();
}
mLayersPendingRemoval.clear();
@@ -2267,10 +2291,10 @@
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;
}
}
@@ -2307,7 +2331,7 @@
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) {
@@ -2623,14 +2647,12 @@
dumpAll = false;
}
-#ifdef ENABLE_FENCE_TRACKING
if ((index < numArgs) &&
(args[index] == String16("--fences"))) {
index++;
mFenceTracker.dump(&result);
dumpAll = false;
}
-#endif
}
if (dumpAll) {
@@ -2751,6 +2773,58 @@
NUM_BUCKETS - 1, bucketTimeSec, percent);
}
+void SurfaceFlinger::recordBufferingStats(const char* layerName,
+ std::vector<OccupancyTracker::Segment>&& history) {
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ auto& stats = mBufferingStats[layerName];
+ for (const auto& segment : history) {
+ if (!segment.usedThirdBuffer) {
+ stats.twoBufferTime += segment.totalTime;
+ }
+ if (segment.occupancyAverage < 1.0f) {
+ stats.doubleBufferedTime += segment.totalTime;
+ } else if (segment.occupancyAverage < 2.0f) {
+ stats.tripleBufferedTime += segment.totalTime;
+ }
+ ++stats.numSegments;
+ stats.totalTime += segment.totalTime;
+ }
+}
+
+void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+ result.append("Buffering stats:\n");
+ result.append(" [Layer name] <Active time> <Two buffer> "
+ "<Double buffered> <Triple buffered>\n");
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ typedef std::tuple<std::string, float, float, float> BufferTuple;
+ std::map<float, BufferTuple, std::greater<float>> sorted;
+ for (const auto& statsPair : mBufferingStats) {
+ const char* name = statsPair.first.c_str();
+ const BufferingStats& stats = statsPair.second;
+ if (stats.numSegments == 0) {
+ continue;
+ }
+ float activeTime = ns2ms(stats.totalTime) / 1000.0f;
+ float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
+ stats.totalTime;
+ float doubleBufferRatio = static_cast<float>(
+ stats.doubleBufferedTime) / stats.totalTime;
+ float tripleBufferRatio = static_cast<float>(
+ stats.tripleBufferedTime) / stats.totalTime;
+ sorted.insert({activeTime, {name, twoBufferRatio,
+ doubleBufferRatio, tripleBufferRatio}});
+ }
+ for (const auto& sortedPair : sorted) {
+ float activeTime = sortedPair.first;
+ const BufferTuple& values = sortedPair.second;
+ result.appendFormat(" [%s] %.2f %.3f %.3f %.3f\n",
+ std::get<0>(values).c_str(), activeTime,
+ std::get<1>(values), std::get<2>(values),
+ std::get<3>(values));
+ }
+ result.append("\n");
+}
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
@@ -2802,6 +2876,8 @@
dumpStaticScreenStats(result);
result.append("\n");
+ dumpBufferingStats(result);
+
/*
* Dump the visible layer list
*/
@@ -3040,14 +3116,20 @@
// 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();
@@ -3548,6 +3630,11 @@
return result;
}
+bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+ return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
+}
+
void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
if (DEBUG_SCREENSHOTS) {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 97a1e8b..53a63bd 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 @@
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 2b4ea2a..435aa0c 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -3,4 +3,4 @@
user system
group graphics drmrpc readproc
onrestart restart zygote
- writepid /sys/fs/cgroup/stune/foreground/tasks
+ writepid /dev/stune/foreground/tasks