Merge "Removed HardwarePropertiesManagerService helper." into nyc-dev
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
new file mode 100644
index 0000000..674c163
--- /dev/null
+++ b/aidl/gui/android/view/Surface.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/Surface.aidl
+**
+** Copyright 2007, 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.
+*/
+
+package android.view;
+
+parcelable Surface cpp_header "gui/Surface.h";
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 87f637c..23954ea 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -42,6 +42,8 @@
using namespace android;
+#define LOG_TAG "atrace"
+
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
enum { MAX_SYS_FILES = 10 };
@@ -769,6 +771,7 @@
// Read the current kernel trace and write it to stdout.
static void dumpTrace()
{
+ ALOGE("Dumping trace");
int traceFD = open(k_tracePath, O_RDWR);
if (traceFD == -1) {
fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index e15a0c5..5898b41 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -90,9 +90,7 @@
* See bugreport-format.txt for more info.
*/
// TODO: change to "v1" before final N build
-static std::string VERSION_DEFAULT = "v1-dev1";
-// TODO: remove before final N build
-static std::string VERSION_DUMPSYS_SPLIT = "v1-dev1-dumpsys-split";
+static std::string VERSION_DEFAULT = "v1-dev2";
/* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
* otherwise gets just those modified in the last half an hour. */
@@ -607,9 +605,8 @@
run_command("PROCESSES AND THREADS", 10, "ps", "-Z", "-t", "-p", "-P", NULL);
run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
- run_command("ROUTE", 10, "route", NULL);
run_command("PRINTENV", 10, "printenv", NULL);
- run_command("NETSTAT", 10, "netstat", NULL);
+ run_command("NETSTAT", 10, "netstat", "-n", NULL);
run_command("LSMOD", 10, "lsmod", NULL);
do_dmesg();
@@ -851,12 +848,7 @@
/* the full dumpsys is starting to take a long time, so we need
to increase its timeout. we really need to do the timeouts in
dumpsys itself... */
- if (version == VERSION_DUMPSYS_SPLIT) {
- // Skipping meminfo and cpuinfo services.
- run_command("DUMPSYS", 60, "dumpsys", "--skip", "meminfo,cpuinfo", NULL);
- } else {
- run_command("DUMPSYS", 60, "dumpsys", NULL);
- }
+ run_command("DUMPSYS", 60, "dumpsys", "--skip", "meminfo,cpuinfo", NULL);
printf("========================================================\n");
printf("== Checkins\n");
@@ -909,8 +901,8 @@
" -B: send broadcast when finished (requires -o)\n"
" -P: send broadcast when started and update system properties on progress (requires -o and -B)\n"
" -R: take bugreport in remote mode (requires -o, -z, -d and -B, shouldn't be used with -P)\n"
- " -V: sets the bugreport format version (%s or %s)\n",
- VERSION_DEFAULT.c_str(), VERSION_DUMPSYS_SPLIT.c_str());
+ " -V: sets the bugreport format version (valid values: %s)\n",
+ VERSION_DEFAULT.c_str());
}
static void sigpipe_handler(int n) {
@@ -1066,7 +1058,9 @@
}
/* parse arguments */
- log_args("Dumpstate command line", argc, const_cast<const char **>(argv));
+ std::string args;
+ format_args(argc, const_cast<const char **>(argv), &args);
+ MYLOGD("Dumpstate command line: %s\n", args.c_str());
int c;
while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
switch (c) {
@@ -1103,7 +1097,7 @@
exit(1);
}
- if (version != VERSION_DEFAULT && version != VERSION_DUMPSYS_SPLIT) {
+ if (version != VERSION_DEFAULT) {
usage();
exit(1);
}
@@ -1176,8 +1170,8 @@
log_path.c_str(), tmp_path.c_str(), screenshot_path.c_str());
if (do_zip_file) {
- MYLOGD("Creating initial .zip file\n");
path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
+ MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
create_parent_dirs(path.c_str());
zip_file.reset(fopen(path.c_str(), "wb"));
if (!zip_file) {
@@ -1258,12 +1252,10 @@
// duration is logged into MYLOG instead.
print_header(version);
- if (version == VERSION_DUMPSYS_SPLIT) {
- // 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("DUMPSYS MEMINFO", 30, SU_PATH, "shell", "dumpsys", "meminfo", "-a", NULL);
- run_command("DUMPSYS CPUINFO", 30, SU_PATH, "shell", "dumpsys", "cpuinfo", "-a", NULL);
- }
+ // 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("DUMPSYS MEMINFO", 30, SU_PATH, "shell", "dumpsys", "meminfo", "-a", NULL);
+ run_command("DUMPSYS CPUINFO", 30, SU_PATH, "shell", "dumpsys", "cpuinfo", "-a", NULL);
/* collect stack traces from Dalvik and native processes (needs root) */
dump_traces_path = dump_traces();
@@ -1282,7 +1274,6 @@
/* close output if needed */
if (is_redirecting) {
fclose(stdout);
- fclose(stderr);
}
/* rename or zip the (now complete) .tmp file to its final location */
@@ -1391,5 +1382,9 @@
MYLOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
MYLOGI("done\n");
+ if (is_redirecting) {
+ fclose(stderr);
+ }
+
return 0;
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 9c975d2..288fe39 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -171,8 +171,8 @@
/* dump eMMC Extended CSD data */
void dump_emmc_ecsd(const char *ext_csd_path);
-/** logs command-line arguments */
-void log_args(const std::string& message, int argc, const char *argv[]);
+/** gets command-line arguments */
+void format_args(int argc, const char *argv[], std::string *args);
/*
* Helper class used to report how long it takes for a section to finish.
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index f0ae325..d21ef7b 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -394,7 +394,7 @@
sprintf(title, "SHOW MAP %d (%s)", pid, name);
sprintf(arg, "%d", pid);
- run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
+ run_command(title, 10, SU_PATH, "root", "showmap", "-q", arg, NULL);
}
static int _dump_file_from_fd(const char *title, const char *path, int fd) {
@@ -501,7 +501,7 @@
dirp = opendir(dir);
if (dirp == NULL) {
retval = -errno;
- fprintf(stderr, "%s: %s\n", dir, strerror(errno));
+ MYLOGE("%s: %s\n", dir, strerror(errno));
return retval;
}
@@ -607,6 +607,9 @@
return true;
}
+// TODO: refactor all those commands that convert args
+void format_args(const char* command, const char *args[], std::string *string);
+
int run_command(const char *title, int timeout_seconds, const char *command, ...) {
DurationReporter duration_reporter(title);
fflush(stdout);
@@ -616,13 +619,24 @@
va_list ap;
va_start(ap, command);
if (title) printf("------ %s (%s", title, command);
+ bool null_terminated = false;
for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
args[arg] = va_arg(ap, const char *);
- if (args[arg] == NULL) break;
+ if (args[arg] == nullptr) {
+ null_terminated = true;
+ break;
+ }
if (title) printf(" %s", args[arg]);
}
if (title) printf(") ------\n");
fflush(stdout);
+ if (!null_terminated) {
+ // Fail now, otherwise execvp() call on run_command_always() might hang.
+ std::string cmd;
+ format_args(command, args, &cmd);
+ MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
+ return -1;
+ }
ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
@@ -661,31 +675,43 @@
sigaction(SIGPIPE, &sigact, NULL);
execvp(command, (char**) args);
- printf("*** exec(%s): %s\n", command, strerror(errno));
- fflush(stdout);
- _exit(-1);
+ // execvp's result will be handled after waitpid_with_timeout() below...
+ _exit(-1); // ...but it doesn't hurt to force exit, just in case
}
/* handle parent case */
int status;
bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
uint64_t elapsed = DurationReporter::nanotime() - start;
+ std::string cmd; // used to log command and its args
if (!ret) {
if (errno == ETIMEDOUT) {
- printf("*** %s: Timed out after %.3fs (killing pid %d)\n", command,
+ format_args(command, args, &cmd);
+ printf("*** command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
+ (float) elapsed / NANOS_PER_SEC, pid);
+ MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
(float) elapsed / NANOS_PER_SEC, pid);
} else {
- printf("*** %s: Error after %.4fs (killing pid %d)\n", command,
+ format_args(command, args, &cmd);
+ printf("*** command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
+ (float) elapsed / NANOS_PER_SEC, pid);
+ MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
(float) elapsed / NANOS_PER_SEC, pid);
}
kill(pid, SIGTERM);
if (!waitpid_with_timeout(pid, 5, NULL)) {
kill(pid, SIGKILL);
if (!waitpid_with_timeout(pid, 5, NULL)) {
- printf("*** %s: Cannot kill %d even with SIGKILL.\n", command, pid);
+ printf("couldn not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
+ MYLOGE("couldn not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
}
}
return -1;
+ } else if (status) {
+ format_args(command, args, &cmd);
+ printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
+ MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
+ return -2;
}
if (WIFSIGNALED(status)) {
@@ -702,7 +728,7 @@
void send_broadcast(const std::string& action, const std::vector<std::string>& args) {
if (args.size() > 1000) {
- fprintf(stderr, "send_broadcast: too many arguments (%d)\n", (int) args.size());
+ MYLOGE("send_broadcast: too many arguments (%d)\n", (int) args.size());
return;
}
const char *am_args[1024] = { SU_PATH, "shell", "/system/bin/am", "broadcast",
@@ -713,7 +739,9 @@
}
// Always terminate with NULL.
am_args[am_index + 1] = NULL;
- log_args("send_broadcast arguments", am_index, am_args);
+ std::string args_string;
+ format_args(am_index + 1, am_args, &args_string);
+ MYLOGD("send_broadcast command: %s\n", args_string.c_str());
run_command_always(NULL, 5, am_args);
}
@@ -755,12 +783,12 @@
void redirect_to_socket(FILE *redirect, const char *service) {
int s = android_get_control_socket(service);
if (s < 0) {
- fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
+ MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
exit(1);
}
fcntl(s, F_SETFD, FD_CLOEXEC);
if (listen(s, 4) < 0) {
- fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
+ MYLOGE("listen(control socket): %s\n", strerror(errno));
exit(1);
}
@@ -768,7 +796,7 @@
socklen_t alen = sizeof(addr);
int fd = accept(s, &addr, &alen);
if (fd < 0) {
- fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
+ MYLOGE("accept(control socket): %s\n", strerror(errno));
exit(1);
}
@@ -810,7 +838,7 @@
int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd < 0) {
- fprintf(stderr, "%s: %s\n", path, strerror(errno));
+ MYLOGE("%s: %s\n", path, strerror(errno));
exit(1);
}
@@ -842,7 +870,7 @@
strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
- fprintf(stderr, "rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
+ MYLOGE("rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
return NULL; // Can't rename old traces.txt -- no permission? -- leave it alone instead
}
@@ -850,12 +878,12 @@
int fd = TEMP_FAILURE_RETRY(open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
0666)); /* -rw-rw-rw- */
if (fd < 0) {
- fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
+ MYLOGE("%s: %s\n", traces_path, strerror(errno));
return NULL;
}
int chmod_ret = fchmod(fd, 0666);
if (chmod_ret < 0) {
- fprintf(stderr, "fchmod on %s failed: %s\n", traces_path, strerror(errno));
+ MYLOGE("fchmod on %s failed: %s\n", traces_path, strerror(errno));
close(fd);
return NULL;
}
@@ -867,20 +895,20 @@
/* walk /proc and kill -QUIT all Dalvik processes */
DIR *proc = opendir("/proc");
if (proc == NULL) {
- fprintf(stderr, "/proc: %s\n", strerror(errno));
+ MYLOGE("/proc: %s\n", strerror(errno));
goto error_close_fd;
}
/* use inotify to find when processes are done dumping */
ifd = inotify_init();
if (ifd < 0) {
- fprintf(stderr, "inotify_init: %s\n", strerror(errno));
+ MYLOGE("inotify_init: %s\n", strerror(errno));
goto error_close_fd;
}
wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
if (wfd < 0) {
- fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
+ MYLOGE("inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
goto error_close_ifd;
}
@@ -915,7 +943,7 @@
++dalvik_found;
uint64_t start = DurationReporter::nanotime();
if (kill(pid, SIGQUIT)) {
- fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
+ MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
continue;
}
@@ -923,16 +951,16 @@
struct pollfd pfd = { ifd, POLLIN, 0 };
int ret = poll(&pfd, 1, 5000); /* 5 sec timeout */
if (ret < 0) {
- fprintf(stderr, "poll: %s\n", strerror(errno));
+ MYLOGE("poll: %s\n", strerror(errno));
} else if (ret == 0) {
- fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
+ MYLOGE("warning: timed out dumping pid %d\n", pid);
} else {
struct inotify_event ie;
read(ifd, &ie, sizeof(ie));
}
if (lseek(fd, 0, SEEK_END) < 0) {
- fprintf(stderr, "lseek: %s\n", strerror(errno));
+ MYLOGE("lseek: %s\n", strerror(errno));
} else {
dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n",
pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
@@ -940,7 +968,7 @@
} else if (should_dump_native_traces(data)) {
/* dump native process if appropriate */
if (lseek(fd, 0, SEEK_END) < 0) {
- fprintf(stderr, "lseek: %s\n", strerror(errno));
+ MYLOGE("lseek: %s\n", strerror(errno));
} else {
static uint16_t timeout_failures = 0;
uint64_t start = DurationReporter::nanotime();
@@ -961,14 +989,14 @@
}
if (dalvik_found == 0) {
- fprintf(stderr, "Warning: no Dalvik processes found to dump stacks\n");
+ MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
}
static char dump_traces_path[PATH_MAX];
strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
if (rename(traces_path, dump_traces_path)) {
- fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
+ MYLOGE("rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
goto error_close_ifd;
}
result = dump_traces_path;
@@ -1021,7 +1049,7 @@
// adjusts max on the fly
if (progress > weight_total) {
int new_total = weight_total * 1.2;
- fprintf(stderr, "Adjusting total weight from %d to %d\n", weight_total, new_total);
+ MYLOGD("Adjusting total weight from %d to %d\n", weight_total, new_total);
weight_total = new_total;
sprintf(key, "dumpstate.%d.max", getpid());
sprintf(value, "%d", weight_total);
@@ -1035,9 +1063,14 @@
sprintf(key, "dumpstate.%d.progress", getpid());
sprintf(value, "%d", progress);
- // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
- // directly for debuggging.
- fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
+ if (progress % 100 == 0) {
+ // We don't want to spam logcat, so only log multiples of 100.
+ MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total);
+ } else {
+ // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
+ // directly for debuggging.
+ fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
+ }
int status = property_set(key, value);
if (status) {
@@ -1189,11 +1222,28 @@
printf("\n");
}
-void log_args(const std::string& message, int argc, const char *argv[]) {
- std::string args;
+// TODO: refactor all those commands that convert args
+void format_args(int argc, const char *argv[], std::string *args) {
+ LOG_ALWAYS_FATAL_IF(args == nullptr);
for (int i = 0; i < argc; i++) {
- args.append(argv[i]);
- args.append(" ");
+ args->append(argv[i]);
+ if (i < argc -1) {
+ args->append(" ");
+ }
}
- MYLOGI("%s: %s\n", message.c_str(), args.c_str());
+}
+void format_args(const char* command, const char *args[], std::string *string) {
+ LOG_ALWAYS_FATAL_IF(args == nullptr || command == nullptr);
+ string->append(command);
+ if (args[0] == nullptr) return;
+ string->append(" ");
+
+ for (int arg = 1; arg <= 1000; ++arg) {
+ if (args[arg] == nullptr) return;
+ string->append(args[arg]);
+ if (args[arg+1] != nullptr) {
+ string->append(" ");
+ }
+ }
+ MYLOGE("internal error: missing NULL entry on %s", string->c_str());
}
diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk
index 9be0901..8335c14 100644
--- a/cmds/dumpsys/Android.mk
+++ b/cmds/dumpsys/Android.mk
@@ -5,10 +5,11 @@
dumpsys.cpp
LOCAL_SHARED_LIBRARIES := \
+ libbase \
libutils \
liblog \
libbinder
-
+
ifeq ($(TARGET_OS),linux)
LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index ef009da..003fcc3 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -5,21 +5,33 @@
#define LOG_TAG "dumpsys"
-#include <utils/Log.h>
+#include <algorithm>
+#include <chrono>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
#include <binder/TextOutput.h>
+#include <utils/Log.h>
#include <utils/Vector.h>
+#include <fcntl.h>
#include <getopt.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
using namespace android;
+using android::base::unique_fd;
+using android::base::WriteFully;
static int sort_func(const String16* lhs, const String16* rhs)
{
@@ -121,23 +133,107 @@
return 0;
}
- for (size_t i=0; i<N; i++) {
- if (IsSkipped(skippedServices, services[i])) continue;
+ for (size_t i = 0; i < N; i++) {
+ String16 service_name = std::move(services[i]);
+ if (IsSkipped(skippedServices, service_name)) continue;
- sp<IBinder> service = sm->checkService(services[i]);
+ sp<IBinder> service = sm->checkService(service_name);
if (service != NULL) {
+ int sfd[2];
+
+ if (pipe(sfd) != 0) {
+ aerr << "Failed to create pipe to dump service info for " << service_name
+ << ": " << strerror(errno) << endl;
+ continue;
+ }
+
+ unique_fd local_end(sfd[0]);
+ unique_fd remote_end(sfd[1]);
+ sfd[0] = sfd[1] = -1;
+
if (N > 1) {
aout << "------------------------------------------------------------"
"-------------------" << endl;
- aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
+ aout << "DUMP OF SERVICE " << service_name << ":" << endl;
}
- int err = service->dump(STDOUT_FILENO, args);
- if (err != 0) {
- aerr << "Error dumping service info: (" << strerror(err)
- << ") " << services[i] << endl;
+
+ // dump blocks until completion, so spawn a thread..
+ std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
+ int err = service->dump(remote_end.get(), args);
+
+ // It'd be nice to be able to close the remote end of the socketpair before the dump
+ // call returns, to terminate our reads if the other end closes their copy of the
+ // file descriptor, but then hangs for some reason. There doesn't seem to be a good
+ // way to do this, though.
+ remote_end.clear();
+
+ if (err != 0) {
+ aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
+ << endl;
+ }
+ });
+
+ // TODO: Make this configurable at runtime.
+ constexpr auto timeout = std::chrono::seconds(10);
+ auto end = std::chrono::steady_clock::now() + timeout;
+
+ struct pollfd pfd = {
+ .fd = local_end.get(),
+ .events = POLLIN
+ };
+
+ bool timed_out = false;
+ bool error = false;
+ while (true) {
+ // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
+ auto time_left_ms = [end]() {
+ auto now = std::chrono::steady_clock::now();
+ auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
+ return std::max(diff.count(), 0ll);
+ };
+
+ int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
+ if (rc < 0) {
+ aerr << "Error in poll while dumping service " << service_name << " : "
+ << strerror(errno) << endl;
+ error = true;
+ break;
+ } else if (rc == 0) {
+ timed_out = true;
+ break;
+ }
+
+ char buf[4096];
+ rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
+ if (rc < 0) {
+ aerr << "Failed to read while dumping service " << service_name << ": "
+ << strerror(errno) << endl;
+ error = true;
+ break;
+ } else if (rc == 0) {
+ // EOF.
+ break;
+ }
+
+ if (!WriteFully(STDOUT_FILENO, buf, rc)) {
+ aerr << "Failed to write while dumping service " << service_name << ": "
+ << strerror(errno) << endl;
+ error = true;
+ break;
+ }
+ }
+
+ if (timed_out) {
+ aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
+ }
+
+ if (timed_out || error) {
+ dump_thread.detach();
+ } else {
+ dump_thread.join();
}
} else {
- aerr << "Can't find service: " << services[i] << endl;
+ aerr << "Can't find service: " << service_name << endl;
}
}
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 65bcf39..d35099a 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -80,6 +80,36 @@
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
+# OTA chroot tool
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := otapreopt_chroot
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_cflags)
+
+LOCAL_SRC_FILES := otapreopt_chroot.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+include $(BUILD_EXECUTABLE)
+
+# OTA postinstall script
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= otapreopt_script
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := otapreopt_script.sh
+
+# Let this depend on otapreopt and the chroot tool, so we just have to mention one in a
+# configuration.
+LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot
+
+include $(BUILD_PREBUILT)
+
# Tests.
include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index e31c6e2..77bcfc2 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -161,28 +161,39 @@
return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
}
-static void unlink_reference_profile(const char* pkgname) {
+static bool unlink_reference_profile(const char* pkgname) {
std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
std::string reference_profile = create_primary_profile(reference_profile_dir);
if (unlink(reference_profile.c_str()) != 0) {
- PLOG(WARNING) << "Could not unlink " << reference_profile;
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Could not unlink " << reference_profile;
+ return false;
+ }
}
+ return true;
}
-static void unlink_current_profiles(const char* pkgname) {
+static bool unlink_current_profiles(const char* pkgname) {
+ bool success = true;
std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
for (auto user : users) {
std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
std::string profile = create_primary_profile(profile_dir);
if (unlink(profile.c_str()) != 0) {
- PLOG(WARNING) << "Could not unlink " << profile;
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Could not unlink " << profile;
+ success = false;
+ }
}
}
+ return success;
}
-static void unlink_all_profiles(const char* pkgname) {
- unlink_reference_profile(pkgname);
- unlink_current_profiles(pkgname);
+static bool unlink_all_profiles(const char* pkgname) {
+ bool success = true;
+ success &= unlink_reference_profile(pkgname);
+ success &= unlink_current_profiles(pkgname);
+ return success;
}
int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
@@ -815,7 +826,9 @@
strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
have_dex2oat_compiler_filter_flag = true;
} else if (extract_only) {
- strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-at-runtime");
+ // Temporarily make extract-only mean interpret-only, so extracted files will be verified.
+ // b/26833007
+ strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
have_dex2oat_compiler_filter_flag = true;
} else if (have_dex2oat_compiler_filter_flag) {
sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag);
@@ -1276,12 +1289,20 @@
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 is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
bool extract_only = (dexopt_flags & DEXOPT_EXTRACTONLY) != 0;
fd_t reference_profile_fd = -1;
+
+ if (is_public && use_profiles) {
+ // We should not give public access to apks compiled with profile information.
+ // Log an error and return early if are asked to do so.
+ ALOGE("use_profiles should not be used with is_public.");
+ return -1;
+ }
+
if (use_profiles) {
if (analyse_profiles(uid, pkgname)) {
// Open again reference profile in read only mode as dex2oat does not get write
@@ -1749,6 +1770,11 @@
return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
}
+int rm_profiles(const char* pkgname)
+{
+ return unlink_all_profiles(pkgname) ? 0 : -1;
+}
+
int link_file(const char* relative_path, const char* from_base, const char* to_base) {
char from_path[PKG_PATH_MAX];
char to_path[PKG_PATH_MAX];
@@ -1774,5 +1800,84 @@
return 0;
}
+// Helper for move_ab, so that we can have common failure-case cleanup.
+static bool unlink_and_rename(const char* from, const char* to) {
+ // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
+ // return a failure.
+ struct stat s;
+ if (stat(to, &s) == 0) {
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << from << " is not a regular file to replace for A/B.";
+ return false;
+ }
+ if (unlink(to) != 0) {
+ LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
+ return false;
+ }
+ } else {
+ // This may be a permission problem. We could investigate the error code, but we'll just
+ // let the rename failure do the work for us.
+ }
+
+ // Try to rename "to" to "from."
+ if (rename(from, to) != 0) {
+ PLOG(ERROR) << "Could not rename " << from << " to " << to;
+ 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;
+ }
+ if (validate_apk_path(apk_path) != 0) {
+ LOG(ERROR) << "invalid apk_path " << apk_path;
+ return -1;
+ }
+ if (validate_apk_path(oat_dir) != 0) {
+ LOG(ERROR) << "invalid oat_dir " << oat_dir;
+ return -1;
+ }
+
+ char a_path[PKG_PATH_MAX];
+ if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
+ return -1;
+ }
+
+ // B path = A path + ".b"
+ std::string b_path = StringPrintf("%s.b", 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 -1;
+ }
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+ // Try to unlink, but swallow errors.
+ unlink(b_path.c_str());
+ return -1;
+ }
+ }
+
+ // 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;
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index fe03397..b473e3e 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -56,8 +56,12 @@
int idmap(const char *target_path, const char *overlay_path, uid_t uid);
int create_oat_dir(const char* oat_dir, const char *instruction_set);
int rm_package_dir(const char* apk_path);
+int rm_profiles(const char* pkgname);
int link_file(const char *relative_path, const char *from_base, const char *to_base);
+// Move a B version over to the A location. Only works for oat_dir != nullptr.
+int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
new file mode 100644
index 0000000..3e2f815
--- /dev/null
+++ b/cmds/installd/file_parsing.h
@@ -0,0 +1,60 @@
+/*
+ * 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_FILE_PARSING_H_
+#define OTAPREOPT_FILE_PARSING_H_
+
+#include <fstream>
+#include <functional>
+#include <string>
+
+namespace android {
+namespace installd {
+
+bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) {
+ std::ifstream input_stream(strFile);
+
+ if (!input_stream.is_open()) {
+ return false;
+ }
+
+ while (!input_stream.eof()) {
+ // Read the next line.
+ std::string line;
+ getline(input_stream, line);
+
+ // Is the line empty? Simplifies the next check.
+ if (line.empty()) {
+ continue;
+ }
+
+ // Is this a comment (starts with pound)?
+ if (line[0] == '#') {
+ continue;
+ }
+
+ if (!parse(line)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace installd
+} // namespace android
+
+#endif // OTAPREOPT_FILE_PARSING_H_
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 63290a9..2bf27a2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -128,13 +128,13 @@
bool create_cache_path(char path[PKG_PATH_MAX],
const char *src,
const char *instruction_set) {
- size_t srclen = strlen(src);
-
- /* demand that we are an absolute path */
- if ((src == 0) || (src[0] != '/') || strstr(src,"..")) {
+ /* demand that we are an absolute path */
+ if ((src == nullptr) || (src[0] != '/') || strstr(src,"..")) {
return false;
}
+ size_t srclen = strlen(src);
+
if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX?
return false;
}
@@ -216,29 +216,40 @@
return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
}
-static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
- // Time to fork and run otapreopt.
- pid_t pid = fork();
- if (pid == 0) {
- const char* argv[1 + 9 + 1];
- argv[0] = "/system/bin/otapreopt";
- for (size_t i = 1; i <= 9; ++i) {
- argv[i] = arg[i - 1];
- }
- argv[10] = nullptr;
+// We use otapreopt_chroot to get into the chroot.
+static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
- execv(argv[0], (char * const *)argv);
- ALOGE("execv(OTAPREOPT) failed: %s\n", strerror(errno));
- exit(99);
- } else {
- int res = wait_child(pid);
- if (res == 0) {
- ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
- } else {
- ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ // Time to fork and run otapreopt.
+
+ // Check that the tool exists.
+ struct stat s;
+ if (stat(kOtaPreopt, &s) != 0) {
+ LOG(ERROR) << "Otapreopt chroot tool not found.";
+ return -1;
}
- return res;
- }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ const char* argv[1 + 9 + 1];
+ argv[0] = kOtaPreopt;
+ for (size_t i = 1; i <= 9; ++i) {
+ argv[i] = arg[i - 1];
+ }
+ argv[10] = nullptr;
+
+ execv(argv[0], (char * const *)argv);
+ PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
+ exit(99);
+ } else {
+ int res = wait_child(pid);
+ if (res == 0) {
+ ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
+ } else {
+ ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+ }
+ return res;
+ }
}
static int do_dexopt(char **arg, char reply[REPLY_MAX])
@@ -330,12 +341,23 @@
return rm_package_dir(arg[0]);
}
+static int do_rm_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+{
+ /* package_name */
+ return rm_profiles(arg[0]);
+}
+
static int do_link_file(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
{
/* relative_path, from_base, to_base */
return link_file(arg[0], arg[1], arg[2]);
}
+static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ // apk_path, instruction_set, oat_dir
+ return move_ab(arg[0], arg[1], arg[2]);
+}
+
struct cmdinfo {
const char *name;
unsigned numargs;
@@ -363,7 +385,9 @@
{ "idmap", 3, do_idmap },
{ "createoatdir", 2, do_create_oat_dir },
{ "rmpackagedir", 1, do_rm_package_dir },
+ { "rmprofiles", 1, do_rm_profiles },
{ "linkfile", 3, do_link_file },
+ { "move_ab", 3, do_move_ab },
};
static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 3aac48b..89a4225 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -17,6 +17,7 @@
#include <algorithm>
#include <inttypes.h>
#include <random>
+#include <regex>
#include <selinux/android.h>
#include <selinux/avc.h>
#include <stdlib.h>
@@ -35,6 +36,7 @@
#include <private/android_filesystem_config.h>
#include <commands.h>
+#include <file_parsing.h>
#include <globals.h>
#include <installd_deps.h> // Need to fill in requirements of commands.
#include <string_helpers.h>
@@ -54,8 +56,8 @@
namespace android {
namespace installd {
-static constexpr const char* kBootClassPathPropertyName = "env.BOOTCLASSPATH";
-static constexpr const char* kAndroidRootPathPropertyName = "env.ANDROID_ROOT";
+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;
@@ -131,41 +133,55 @@
private:
bool ReadSystemProperties() {
- // TODO(agampe): What to do about the things in default.prop? It's only heap sizes, so it's easy
- // to emulate for now, but has issues (e.g., vendors modifying the boot classpath
- // may require larger values here - revisit). That's why this goes first, so that
- // if those dummy values are overridden in build.prop, that's what we'll get.
- //
- // Note: It seems we'll get access to the B root partition, so we should read the default.prop
- // file.
- // if (!system_properties_.Load(b_mount_path_ + "/default.prop") {
- // return false;
- // }
- system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xms", "64m");
- system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xmx", "64m");
- system_properties_.SetProperty("dalvik.vm.dex2oat-Xms", "64m");
- system_properties_.SetProperty("dalvik.vm.dex2oat-Xmx", "512m");
+ static constexpr const char* kPropertyFiles[] = {
+ "/default.prop", "/system/build.prop"
+ };
- // TODO(agampe): Do this properly/test.
- return system_properties_.Load(b_mount_path_ + "/system/build.prop");
+ for (size_t i = 0; i < arraysize(kPropertyFiles); ++i) {
+ if (!system_properties_.Load(kPropertyFiles[i])) {
+ return false;
+ }
+ }
+
+ return true;
}
bool ReadEnvironment() {
- // Read important environment variables. For simplicity, store them as
- // system properties.
- // TODO(agampe): We'll have to parse init.environ.rc for BOOTCLASSPATH.
- // For now, just the A version.
- const char* boot_classpath = getenv("BOOTCLASSPATH");
- if (boot_classpath == nullptr) {
- return false;
- }
- system_properties_.SetProperty(kBootClassPathPropertyName, boot_classpath);
+ // Parse the environment variables from init.environ.rc, which have the form
+ // export NAME VALUE
+ // For simplicity, don't respect string quotation. The values we are interested in can be
+ // encoded without them.
+ std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+ bool parse_result = ParseFile("/init.environ.rc", [&](const std::string& line) {
+ std::smatch export_match;
+ if (!std::regex_match(line, export_match, export_regex)) {
+ return true;
+ }
- const char* root_path = getenv("ANDROID_ROOT");
- if (root_path == nullptr) {
+ if (export_match.size() != 3) {
+ return true;
+ }
+
+ std::string name = export_match[1].str();
+ std::string value = export_match[2].str();
+
+ system_properties_.SetProperty(name, value);
+
+ return true;
+ });
+ if (!parse_result) {
return false;
}
- system_properties_.SetProperty(kAndroidRootPathPropertyName, b_mount_path_ + root_path);
+
+ // 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;
+ }
+ }
return true;
}
@@ -239,9 +255,7 @@
// TODO: Delete files, just for a blank slate.
const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName);
- std::string preopted_boot_art_path = StringPrintf("%s/system/framework/%s/boot.art",
- b_mount_path_.c_str(),
- isa);
+ 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 {
@@ -254,7 +268,7 @@
// 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(b_mount_path_ + "/system/bin/patchoat");
+ cmd.push_back("/system/bin/patchoat");
cmd.push_back("--input-image-location=/system/framework/boot.art");
cmd.push_back(StringPrintf("--output-image-file=%s", art_path.c_str()));
@@ -279,7 +293,7 @@
const char* isa) {
// 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(b_mount_path_ + "/system/bin/dex2oat");
+ 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, ':')) {
cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
@@ -305,8 +319,7 @@
"--compiler-filter=",
false,
cmd);
- cmd.push_back(StringPrintf("--image-classes=%s/system/etc/preloaded-classes",
- b_mount_path_.c_str()));
+ cmd.push_back("--image-classes=/system/etc/preloaded-classes");
// TODO: Compiled-classes.
const std::string* extra_opts =
system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
@@ -468,10 +481,6 @@
}
}
- // The path where the B partitions are mounted.
- // TODO(agampe): If we're running this *inside* the change-root, we wouldn't need this.
- std::string b_mount_path_;
-
// 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_;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
new file mode 100644
index 0000000..f7f69a9
--- /dev/null
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -0,0 +1,99 @@
+/*
+ ** 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.
+ */
+
+#include <linux/unistd.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "otapreopt"
+#endif
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+static int otapreopt_chroot(const int argc, char **arg) {
+ // We need to run the otapreopt tool from the postinstall partition. As such, set up a
+ // mount namespace and change root.
+
+ // Create our own mount namespace.
+ if (unshare(CLONE_NEWNS) != 0) {
+ PLOG(ERROR) << "Failed to unshare() for otapreopt.";
+ exit(200);
+ }
+
+ // Make postinstall private, so that our changes don't propagate.
+ if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) {
+ PLOG(ERROR) << "Failed to mount private.";
+ exit(201);
+ }
+
+ // Bind mount necessary directories.
+ constexpr const char* kBindMounts[] = {
+ "/data", "/dev", "/proc", "/sys"
+ };
+ for (size_t i = 0; i < arraysize(kBindMounts); ++i) {
+ std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]);
+ if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) {
+ PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i];
+ exit(202);
+ }
+ }
+
+ // Chdir into /postinstall.
+ if (chdir("/postinstall") != 0) {
+ PLOG(ERROR) << "Unable to chdir into /postinstall.";
+ exit(203);
+ }
+
+ // Make /postinstall the root in our mount namespace.
+ if (chroot(".") != 0) {
+ PLOG(ERROR) << "Failed to chroot";
+ exit(204);
+ }
+
+ if (chdir("/") != 0) {
+ PLOG(ERROR) << "Unable to chdir into /.";
+ exit(205);
+ }
+
+ // 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];
+ }
+ argv[10] = nullptr;
+
+ execv(argv[0], (char * const *)argv);
+ PLOG(ERROR) << "execv(OTAPREOPT) failed.";
+ exit(99);
+}
+
+} // namespace installd
+} // namespace android
+
+int main(const int argc, char *argv[]) {
+ return android::installd::otapreopt_chroot(argc, argv);
+}
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
new file mode 100644
index 0000000..a31734a
--- /dev/null
+++ b/cmds/installd/otapreopt_script.sh
@@ -0,0 +1,50 @@
+#!/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 run as a postinstall step to drive otapreopt.
+
+# Maximum number of packages/steps.
+MAXIMUM_PACKAGES=1000
+
+PREPARE=$(cmd otadexopt prepare)
+if [ "$PREPARE" != "Success" ] ; then
+ echo "Failed to prepare."
+ exit 1
+fi
+
+i=0
+while ((i<MAXIMUM_PACKAGES)) ; do
+ cmd otadexopt step
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+ sleep 1
+ i=$((i+1))
+done
+
+DONE=$(cmd otadexopt done)
+if [ "$DONE" = "OTA incomplete." ] ; then
+ echo "Incomplete."
+else
+ echo "Complete or error."
+fi
+
+cmd otadexopt cleanup
+
+exit 0
diff --git a/cmds/installd/system_properties.h b/cmds/installd/system_properties.h
index 1b5fb3a..2d940a3 100644
--- a/cmds/installd/system_properties.h
+++ b/cmds/installd/system_properties.h
@@ -21,6 +21,8 @@
#include <string>
#include <unordered_map>
+#include <file_parsing.h>
+
namespace android {
namespace installd {
@@ -28,31 +30,11 @@
class SystemProperties {
public:
bool Load(const std::string& strFile) {
- std::ifstream input_stream(strFile);
-
- if (!input_stream.is_open()) {
- return false;
- }
-
- while (!input_stream.eof()) {
- // Read the next line.
- std::string line;
- getline(input_stream, line);
-
- // Is the line empty? Simplifies the next check.
- if (line.empty()) {
- continue;
- }
-
- // Is this a comment (starts with pound)?
- if (line[0] == '#') {
- continue;
- }
-
+ return ParseFile(strFile, [&](const std::string& line) {
size_t equals_pos = line.find('=');
if (equals_pos == std::string::npos || equals_pos == 0) {
// Did not find equals sign, or it's the first character - isn't a valid line.
- continue;
+ return true;
}
std::string key = line.substr(0, equals_pos);
@@ -60,9 +42,9 @@
line.length() - equals_pos + 1);
properties_.insert(std::make_pair(key, value));
- }
- return true;
+ return true;
+ });
}
// Look up the key in the map. Returns null if the key isn't mapped.
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 9e99085..01218c9 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -167,20 +167,27 @@
return res;
}
-void binder_send_reply(struct binder_state *bs,
- struct binder_io *reply,
- binder_uintptr_t buffer_to_free,
- int status)
+void binder_free_buffer(struct binder_state *bs,
+ binder_uintptr_t buffer_to_free)
{
struct {
uint32_t cmd_free;
binder_uintptr_t buffer;
+ } __attribute__((packed)) data;
+ data.cmd_free = BC_FREE_BUFFER;
+ data.buffer = buffer_to_free;
+ binder_write(bs, &data, sizeof(data));
+}
+
+void binder_send_reply(struct binder_state *bs,
+ struct binder_io *reply,
+ int status)
+{
+ struct {
uint32_t cmd_reply;
struct binder_transaction_data txn;
} __attribute__((packed)) data;
- data.cmd_free = BC_FREE_BUFFER;
- data.buffer = buffer_to_free;
data.cmd_reply = BC_REPLY;
data.txn.target.ptr = 0;
data.txn.cookie = 0;
@@ -243,7 +250,9 @@
bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);
res = func(bs, txn, &msg, &reply);
- binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
+ binder_free_buffer(bs, txn->data.ptr.buffer);
+ if ((txn->flags & TF_ONE_WAY) == 0)
+ binder_send_reply(bs, &reply, res);
}
ptr += sizeof(*txn);
break;
diff --git a/include/android/sensor.h b/include/android/sensor.h
index f2647be..5a61213 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -393,6 +393,13 @@
/*****************************************************************************/
/**
+ * Enable the selected sensor with a specified sampling period and max batch report latency.
+ * Returns a negative error code on failure.
+ */
+int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
+ int32_t samplingPeriodUs, int maxBatchReportLatencyUs);
+
+/**
* Enable the selected sensor. Returns a negative error code on failure.
*/
int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor);
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
index aa415d5..60a624c 100644
--- a/include/binder/MemoryDealer.h
+++ b/include/binder/MemoryDealer.h
@@ -41,6 +41,9 @@
virtual void deallocate(size_t offset);
virtual void dump(const char* what) const;
+ // allocations are aligned to some value. return that value so clients can account for it.
+ static size_t getAllocationAlignment();
+
sp<IMemoryHeap> getMemoryHeap() const { return heap(); }
protected:
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 2645d09..1b950ab 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -32,7 +32,6 @@
#include <list>
#include <set>
-#include <vector>
#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
@@ -68,8 +67,13 @@
// consumer can run asynchronously.
enum { MAX_MAX_ACQUIRED_BUFFERS = BufferQueueDefs::NUM_BUFFER_SLOTS - 2 };
- // The default API number used to indicate that no producer is connected
- enum { NO_CONNECTED_API = 0 };
+ enum {
+ // The API number used to indicate the currently connected producer
+ CURRENTLY_CONNECTED_API = -1,
+
+ // The API number used to indicate that no producer is connected
+ NO_CONNECTED_API = 0,
+ };
typedef Vector<BufferItem> Fifo;
@@ -121,9 +125,8 @@
void freeAllBuffersLocked();
// If delta is positive, makes more slots available. If negative, takes
- // away slots. Returns false if the request can't be met. Any slots that
- // were freed will be appended to freedSlots.
- bool adjustAvailableSlotsLocked(int delta, std::vector<int>* freedSlots);
+ // away slots. Returns false if the request can't be met.
+ bool adjustAvailableSlotsLocked(int delta);
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
void waitWhileAllocatingLocked() const;
@@ -289,9 +292,6 @@
bool mAsyncMode;
// mSingleBufferMode indicates whether or not single buffer mode is enabled.
- // In single buffer mode, the last buffer that was dequeued is cached and
- // returned to all calls to dequeueBuffer and acquireBuffer. This allows the
- // consumer and producer to access the same buffer simultaneously.
bool mSingleBufferMode;
// When single buffer mode is enabled, this indicates whether the consumer
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 1b63552..9307a26 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -26,8 +26,6 @@
#include <utils/threads.h>
#include <gui/IConsumerListener.h>
-#include <queue>
-
namespace android {
// ----------------------------------------------------------------------------
@@ -110,18 +108,18 @@
// from the derived class.
virtual void onLastStrongRef(const void* id);
- // Handlers for the IConsumerListener interface, these will be called from
- // the message queue thread. These calls are used to notify the ConsumerBase
- // of asynchronous events in the BufferQueue. The onFrameAvailableHandler,
- // onFrameReplacedHandler, and onBuffersReleasedHandler methods should not
- // need to be overridden by derived classes, but if they are overridden the
- // ConsumerBase implementation must be called from the derived class. The
- // ConsumerBase version of onSidebandStreamChangedHandler does nothing and
- // can be overriden by derived classes if they want the notification.
- virtual void onFrameAvailableHandler(const BufferItem& item);
- virtual void onFrameReplacedHandler(const BufferItem& item);
- virtual void onBuffersReleasedHandler();
- virtual void onSidebandStreamChangedHandler();
+ // Implementation of the IConsumerListener interface. These
+ // calls are used to notify the ConsumerBase of asynchronous events in the
+ // BufferQueue. The onFrameAvailable, onFrameReplaced, and
+ // onBuffersReleased methods should not need to be overridden by derived
+ // classes, but if they are overridden the ConsumerBase implementation must
+ // be called from the derived class. The ConsumerBase version of
+ // onSidebandStreamChanged does nothing and can be overriden by derived
+ // classes if they want the notification.
+ virtual void onFrameAvailable(const BufferItem& item) override;
+ virtual void onFrameReplaced(const BufferItem& item) override;
+ virtual void onBuffersReleased() override;
+ virtual void onSidebandStreamChanged() override;
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
@@ -246,35 +244,6 @@
//
// This mutex is intended to be locked by derived classes.
mutable Mutex mMutex;
-
- // Implements the ConsumerListener interface
- virtual void onFrameAvailable(const BufferItem& item) override;
- virtual void onFrameReplaced(const BufferItem& item) override;
- virtual void onBuffersReleased() override;
- virtual void onSidebandStreamChanged() override;
-
- enum MessageType {
- ON_FRAME_AVAILABLE,
- ON_FRAME_REPLACED,
- ON_BUFFERS_RELEASED,
- ON_SIDEBAND_STREAM_CHANGED,
- EXIT,
- };
-
- mutable Mutex mMessageQueueLock;
- Condition mMessageAvailable;
- std::queue<std::pair<MessageType, BufferItem>> mMessageQueue;
-
- class MessageThread : public Thread {
- public:
- MessageThread(ConsumerBase* consumerBase) :
- mConsumerBase(consumerBase) {};
- protected:
- virtual bool threadLoop() override;
- ConsumerBase* mConsumerBase;
- };
-
- sp<MessageThread> mMessageThread;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index f6b4230..fee7c63 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -526,9 +526,10 @@
// Used to enable/disable single buffer mode.
//
- // In single buffer mode the last buffer that was dequeued will be cached
- // and returned to all calls to dequeueBuffer and acquireBuffer. This allows
- // the producer and consumer to simultaneously access the same buffer.
+ // When single buffer mode is enabled the first buffer that is queued or
+ // dequeued will be cached and returned to all subsequent calls to
+ // dequeueBuffer and acquireBuffer. This allows the producer and consumer to
+ // simultaneously access the same buffer.
virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
// Used to enable/disable auto-refresh.
diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h
index 895da4b..3848a6c 100644
--- a/include/gui/IProducerListener.h
+++ b/include/gui/IProducerListener.h
@@ -41,9 +41,6 @@
// This is called without any lock held and can be called concurrently by
// multiple threads.
virtual void onBufferReleased() = 0; // Asynchronous
-
- // onSlotFreed is called when the BufferQueue frees a buffer in a slot.
- virtual void onSlotFreed(int /*slot*/) {}; // Asynchronous
};
class IProducerListener : public ProducerListener, public IInterface
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 3afdaae..9f51cdd 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -23,6 +23,8 @@
#include <ui/ANativeObjectBase.h>
#include <ui/Region.h>
+#include <binder/Parcelable.h>
+
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
@@ -348,6 +350,43 @@
bool mSharedBufferHasBeenQueued;
};
+namespace view {
+
+/**
+ * A simple holder for an IGraphicBufferProducer, to match the managed-side
+ * android.view.Surface parcelable behavior.
+ *
+ * This implements android/view/Surface.aidl
+ *
+ * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
+ * used in managed Binder calls.
+ */
+class Surface : public Parcelable {
+ public:
+
+ String16 name;
+ sp<IGraphicBufferProducer> graphicBufferProducer;
+
+ virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const Parcel* parcel) override;
+
+ // nameAlreadyWritten set to true by Surface.java, because it splits
+ // Parceling itself between managed and native code, so it only wants a part
+ // of the full parceling to happen on its native side.
+ status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
+
+ // nameAlreadyRead set to true by Surface.java, because it splits
+ // Parceling itself between managed and native code, so it only wants a part
+ // of the full parceling to happen on its native side.
+ status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
+
+ private:
+
+ static String16 readMaybeEmptyString16(const Parcel* parcel);
+};
+
+} // namespace view
+
}; // namespace android
#endif // ANDROID_GUI_SURFACE_H
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index 993a92f..76ce68d 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -57,6 +57,9 @@
// release surface data from java
void clear();
+ // disconnect any api that's connected
+ void disconnect();
+
status_t setLayerStack(uint32_t layerStack);
status_t setLayer(uint32_t layer);
status_t setPosition(float x, float y);
diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h
index feec509..654773d 100644
--- a/include/media/hardware/HardwareAPI.h
+++ b/include/media/hardware/HardwareAPI.h
@@ -23,6 +23,8 @@
#include <system/window.h>
#include <utils/RefBase.h>
+#include "VideoAPI.h"
+
#include <OMX_Component.h>
namespace android {
@@ -225,79 +227,6 @@
OMX_BOOL bEnable;
};
-// Structure describing a media image (frame)
-// Currently only supporting YUV
-// @deprecated. Use MediaImage2 instead
-struct MediaImage {
- enum Type {
- MEDIA_IMAGE_TYPE_UNKNOWN = 0,
- MEDIA_IMAGE_TYPE_YUV,
- };
-
- enum PlaneIndex {
- Y = 0,
- U,
- V,
- MAX_NUM_PLANES
- };
-
- Type mType;
- uint32_t mNumPlanes; // number of planes
- uint32_t mWidth; // width of largest plane (unpadded, as in nFrameWidth)
- uint32_t mHeight; // height of largest plane (unpadded, as in nFrameHeight)
- uint32_t mBitDepth; // useable bit depth
- struct PlaneInfo {
- uint32_t mOffset; // offset of first pixel of the plane in bytes
- // from buffer offset
- uint32_t mColInc; // column increment in bytes
- uint32_t mRowInc; // row increment in bytes
- uint32_t mHorizSubsampling; // subsampling compared to the largest plane
- uint32_t mVertSubsampling; // subsampling compared to the largest plane
- };
- PlaneInfo mPlane[MAX_NUM_PLANES];
-};
-
-struct MediaImage2 {
- enum Type : uint32_t {
- MEDIA_IMAGE_TYPE_UNKNOWN = 0,
- MEDIA_IMAGE_TYPE_YUV,
- MEDIA_IMAGE_TYPE_YUVA,
- MEDIA_IMAGE_TYPE_RGB,
- MEDIA_IMAGE_TYPE_RGBA,
- MEDIA_IMAGE_TYPE_Y,
- };
-
- enum PlaneIndex : uint32_t {
- Y = 0,
- U = 1,
- V = 2,
- R = 0,
- G = 1,
- B = 2,
- A = 3,
- MAX_NUM_PLANES = 4,
- };
-
- Type mType;
- uint32_t mNumPlanes; // number of planes
- uint32_t mWidth; // width of largest plane (unpadded, as in nFrameWidth)
- uint32_t mHeight; // height of largest plane (unpadded, as in nFrameHeight)
- uint32_t mBitDepth; // useable bit depth (always MSB)
- uint32_t mBitDepthAllocated; // bits per component (must be 8 or 16)
-
- struct PlaneInfo {
- uint32_t mOffset; // offset of first pixel of the plane in bytes
- // from buffer offset
- int32_t mColInc; // column increment in bytes
- int32_t mRowInc; // row increment in bytes
- uint32_t mHorizSubsampling; // subsampling compared to the largest plane
- uint32_t mVertSubsampling; // subsampling compared to the largest plane
- };
- PlaneInfo mPlane[MAX_NUM_PLANES];
-
- void initFromV1(const MediaImage&); // for internal use only
-};
-
// A pointer to this struct is passed to OMX_GetParameter when the extension
// index for the 'OMX.google.android.index.describeColorFormat'
// extension is given. This method can be called from any component state
@@ -386,109 +315,108 @@
OMX_PTR pSidebandWindow; // OUT
};
-// Color description parameters. This is passed via OMX_SetConfig or OMX_GetConfig
-// to video encoders and decoders when the
-// 'OMX.google.android.index.describeColorAspects' extension is given.
+// Color space description (aspects) parameters.
+// This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the
+// 'OMX.google.android.index.describeColorAspects' extension is given. Component SHALL behave
+// as described below if it supports this extension.
//
-// Video encoders: the framework uses OMX_SetConfig to specify color aspects
-// of the coded video before the component transitions to idle state.
+// bDataSpaceChanged and bRequestingDataSpace is assumed to be OMX_FALSE unless noted otherwise.
//
-// Video decoders: the framework uses OMX_SetConfig to specify color aspects
-// of the coded video parsed from the container before the component transitions
-// to idle state. If the bitstream contains color information, the component should
-// update the appropriate color aspects - unless the bitstream contains the
-// "unspecified" value. For "reserved" values, the component should set the aspect
-// to "Other".
+// VIDEO ENCODERS: the framework uses OMX_SetConfig to specify color aspects of the coded video.
+// This may happen:
+// a) before the component transitions to idle state
+// b) before the input frame is sent via OMX_EmptyThisBuffer in executing state
+// c) during execution, just before an input frame with a different color aspect information
+// is sent.
//
-// The framework subsequently uses OMX_GetConfig to get any updates of the
-// color aspects from the decoder. If the color aspects change at any time
-// during the processing of the stream, the component shall signal a
-// OMX_EventPortSettingsChanged event with data2 set to the extension index
-// (or OMX_IndexConfigCommonOutputCrop, as it is handled identically). Component
-// shall not signal a separate event purely for color aspect change, if it occurs
-// together with a port definition (e.g. size) or crop change.
+// The framework also uses OMX_GetConfig to
+// d) verify the color aspects that will be written to the stream
+// e) (optional) verify the color aspects that should be reported to the container for a
+// given dataspace/pixelformat received
//
-// NOTE: this structure is expected to grow in the future if new color aspects are
-// added to codec bitstreams. OMX component should not require a specific nSize
-// though could verify that nSize is at least the size of the structure at the
-// time of implementation. All new fields will be added at the end of the structure
-// ensuring backward compatibility.
-
+// 1. Encoders SHOULD maintain an internal color aspect state, initialized to Unspecified values.
+// This represents the values that will be written into the bitstream.
+// 2. Upon OMX_SetConfig, they SHOULD update their internal state to the aspects received
+// (including Unspecified values). For specific aspect values that are not supported by the
+// codec standard, encoders SHOULD substitute Unspecified values; or they MAY use a suitable
+// alternative (e.g. to suggest the use of BT.709 EOTF instead of SMPTE 240M.)
+// 3. OMX_GetConfig SHALL return the internal state (values that will be written).
+// 4. OMX_SetConfig SHALL always succeed before receiving the first frame. It MAY fail afterwards,
+// but only if the configured values would change AND the component does not support updating the
+// color information to those values mid-stream. If component supports updating a portion of
+// the color information, those values should be updated in the internal state, and OMX_SetConfig
+// SHALL succeed. Otherwise, the internal state SHALL remain intact and OMX_SetConfig SHALL fail
+// with OMX_ErrorUnsupportedSettings.
+// 5. When the framework receives an input frame with an unexpected dataspace, it will query
+// encoders for the color aspects that should be reported to the container using OMX_GetConfig
+// with bDataSpaceChanged set to OMX_TRUE, and nPixelFormat/nDataSpace containing the new
+// format/dataspace values. This allows vendors to use extended dataspace during capture and
+// composition (e.g. screenrecord) - while performing color-space conversion inside the encoder -
+// and encode and report a different color-space information in the bitstream/container.
+// sColorAspects contains the requested color aspects by the client for reference, which may
+// include aspects not supported by the encoding. This is used together with guidance for
+// dataspace selection; see 6. below.
+//
+// VIDEO DECODERS: the framework uses OMX_SetConfig to specify the default color aspects to use
+// for the video.
+// This may happen:
+// f) before the component transitions to idle state
+// g) during execution, when the resolution or the default color aspects change.
+//
+// The framework also uses OMX_GetConfig to
+// h) get the final color aspects reported by the coded bitstream after taking the default values
+// into account.
+//
+// 1. Decoders should maintain two color aspect states - the default state as reported by the
+// framework, and the coded state as reported by the bitstream - as each state can change
+// independently from the other.
+// 2. Upon OMX_SetConfig, it SHALL update its default state regardless of whether such aspects
+// could be supplied by the component bitstream. (E.g. it should blindly support all enumeration
+// values, even unknown ones, and the Other value). This SHALL always succeed.
+// 3. Upon OMX_GetConfig, the component SHALL return the final color aspects by replacing
+// Unspecified coded values with the default values. This SHALL always succeed.
+// 4. Whenever the component processes color aspect information in the bitstream even with an
+// Unspecified value, it SHOULD update its internal coded state with that information just before
+// the frame with the new information would be outputted, and the component SHALL signal an
+// OMX_EventPortSettingsChanged event with data2 set to the extension index.
+// NOTE: Component SHOULD NOT signal a separate event purely for color aspect change, if it occurs
+// together with a port definition (e.g. size) or crop change.
+// 5. If the aspects a component encounters in the bitstream cannot be represented with enumeration
+// values as defined below, the component SHALL set those aspects to Other. Restricted values in
+// the bitstream SHALL be treated as defined by the relevant bitstream specifications/standards,
+// or as Unspecified, if not defined.
+//
+// BOTH DECODERS AND ENCODERS: the framework uses OMX_GetConfig during idle and executing state to
+// i) (optional) get guidance for the dataspace to set for given color aspects, by setting
+// bRequestingDataSpace to OMX_TRUE. The component SHALL return OMX_ErrorUnsupportedSettings
+// IF it does not support this request.
+//
+// 6. This is an information request that can happen at any time, independent of the normal
+// configuration process. This allows vendors to use extended dataspace during capture, playback
+// and composition - while performing color-space conversion inside the component. Component
+// SHALL set the desired dataspace into nDataSpace. Otherwise, it SHALL return
+// OMX_ErrorUnsupportedSettings to let the framework choose a nearby standard dataspace.
+//
+// 6.a. For encoders, this query happens before the first frame is received using surface encoding.
+// This allows the encoder to use a specific dataspace for the color aspects (e.g. because the
+// device supports additional dataspaces, or because it wants to perform color-space extension
+// to facilitate a more optimal rendering/capture pipeline.).
+//
+// 6.b. For decoders, this query happens before the first frame, and every time the color aspects
+// change, while using surface buffers. This allows the decoder to use a specific dataspace for
+// the color aspects (e.g. because the device supports additional dataspaces, or because it wants
+// to perform color-space extension by inline color-space conversion to facilitate a more optimal
+// rendering pipeline.).
+//
struct DescribeColorAspectsParams {
- OMX_U32 nSize; // IN
- OMX_VERSIONTYPE nVersion; // IN
- OMX_U32 nPortIndex; // IN
- OMX_U32 nRange; // IN/OUT (one of the ColorAspects.Range enums)
- OMX_U32 nPrimaries; // IN/OUT (one of the ColorAspects.Primaries enums)
- OMX_U32 nTransfer; // IN/OUT (one of the ColorAspects.Transfer enums)
- OMX_U32 nMatrixCoeffs; // IN/OUT (one of the ColorAspects.MatrixCoeffs enums)
-};
-
-struct ColorAspects {
- // this is in sync with the range values in graphics.h
- enum Range : uint32_t {
- RangeUnspecified,
- RangeFull,
- RangeLimited,
- RangeOther = 0xff,
- };
-
- enum Primaries : uint32_t {
- PrimariesUnspecified,
- PrimariesBT709_5, // Rec.ITU-R BT.709-5 or equivalent
- PrimariesBT470_6M, // Rec.ITU-R BT.470-6 System M or equivalent
- PrimariesBT601_6_625, // Rec.ITU-R BT.601-6 625 or equivalent
- PrimariesBT601_6_525, // Rec.ITU-R BT.601-6 525 or equivalent
- PrimariesGenericFilm, // Generic Film
- PrimariesBT2020, // Rec.ITU-R BT.2020 or equivalent
- PrimariesOther = 0xff,
- };
-
- // this partially in sync with the transfer values in graphics.h prior to the transfers
- // unlikely to be required by Android section
- enum Transfer : uint32_t {
- TransferUnspecified,
- TransferLinear, // Linear transfer characteristics
- TransferSRGB, // sRGB or equivalent
- TransferSMPTE170M, // SMPTE 170M or equivalent (e.g. BT.601/709/2020)
- TransferGamma22, // Assumed display gamma 2.2
- TransferGamma28, // Assumed display gamma 2.8
- TransferST2084, // SMPTE ST 2084 for 10/12/14/16 bit systems
- TransferHLG, // ARIB STD-B67 hybrid-log-gamma
-
- // transfers unlikely to be required by Android
- TransferSMPTE240M = 0x40, // SMPTE 240M
- TransferXvYCC, // IEC 61966-2-4
- TransferBT1361, // Rec.ITU-R BT.1361 extended gamut
- TransferST428, // SMPTE ST 428-1
- TransferOther = 0xff,
- };
-
- enum MatrixCoeffs : uint32_t {
- MatrixUnspecified,
- MatrixBT709_5, // Rec.ITU-R BT.709-5 or equivalent
- MatrixBT470_6M, // KR=0.30, KB=0.11 or equivalent
- MatrixBT601_6, // Rec.ITU-R BT.601-6 625 or equivalent
- MatrixSMPTE240M, // SMPTE 240M or equivalent
- MatrixBT2020, // Rec.ITU-R BT.2020 non-constant luminance
- MatrixBT2020Constant, // Rec.ITU-R BT.2020 constant luminance
- MatrixOther = 0xff,
- };
-
- // this is in sync with the standard values in graphics.h
- enum Standard : uint32_t {
- StandardUnspecified,
- StandardBT709, // PrimariesBT709_5 and MatrixBT709_5
- StandardBT601_625, // PrimariesBT601_6_625 and MatrixBT601_6
- StandardBT601_625_Unadjusted, // PrimariesBT601_6_625 and KR=0.222, KB=0.071
- StandardBT601_525, // PrimariesBT601_6_525 and MatrixBT601_6
- StandardBT601_525_Unadjusted, // PrimariesBT601_6_525 and MatrixSMPTE240M
- StandardBT2020, // PrimariesBT2020 and MatrixBT2020
- StandardBT2020Constant, // PrimariesBT2020 and MatrixBT2020Constant
- StandardBT470M, // PrimariesBT470_6M and MatrixBT470_6M
- StandardFilm, // PrimariesGenericFilm and KR=0.253, KB=0.068
- StandardOther = 0xff,
- };
+ OMX_U32 nSize; // IN
+ OMX_VERSIONTYPE nVersion; // IN
+ OMX_U32 nPortIndex; // IN
+ OMX_BOOL bRequestingDataSpace; // IN
+ OMX_BOOL bDataSpaceChanged; // IN
+ OMX_U32 nPixelFormat; // IN
+ OMX_U32 nDataSpace; // OUT
+ ColorAspects sAspects; // IN/OUT
};
} // namespace android
diff --git a/include/media/hardware/VideoAPI.h b/include/media/hardware/VideoAPI.h
new file mode 100644
index 0000000..481cc67
--- /dev/null
+++ b/include/media/hardware/VideoAPI.h
@@ -0,0 +1,301 @@
+/*
+ * 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 VIDEO_API_H_
+
+#define VIDEO_API_H_
+
+namespace android {
+
+/**
+ * Structure describing a media image (frame)
+ * Currently only supporting YUV
+ * @deprecated. Use MediaImage2 instead
+ */
+struct MediaImage {
+ enum Type {
+ MEDIA_IMAGE_TYPE_UNKNOWN = 0,
+ MEDIA_IMAGE_TYPE_YUV,
+ };
+
+ enum PlaneIndex {
+ Y = 0,
+ U,
+ V,
+ MAX_NUM_PLANES
+ };
+
+ Type mType;
+ uint32_t mNumPlanes; // number of planes
+ uint32_t mWidth; // width of largest plane (unpadded, as in nFrameWidth)
+ uint32_t mHeight; // height of largest plane (unpadded, as in nFrameHeight)
+ uint32_t mBitDepth; // useable bit depth
+ struct PlaneInfo {
+ uint32_t mOffset; // offset of first pixel of the plane in bytes
+ // from buffer offset
+ uint32_t mColInc; // column increment in bytes
+ uint32_t mRowInc; // row increment in bytes
+ uint32_t mHorizSubsampling; // subsampling compared to the largest plane
+ uint32_t mVertSubsampling; // subsampling compared to the largest plane
+ };
+ PlaneInfo mPlane[MAX_NUM_PLANES];
+};
+
+/**
+ * Structure describing a media image (frame)
+ */
+struct MediaImage2 {
+ enum Type : uint32_t {
+ MEDIA_IMAGE_TYPE_UNKNOWN = 0,
+ MEDIA_IMAGE_TYPE_YUV,
+ MEDIA_IMAGE_TYPE_YUVA,
+ MEDIA_IMAGE_TYPE_RGB,
+ MEDIA_IMAGE_TYPE_RGBA,
+ MEDIA_IMAGE_TYPE_Y,
+ };
+
+ enum PlaneIndex : uint32_t {
+ Y = 0,
+ U = 1,
+ V = 2,
+ R = 0,
+ G = 1,
+ B = 2,
+ A = 3,
+ MAX_NUM_PLANES = 4,
+ };
+
+ Type mType;
+ uint32_t mNumPlanes; // number of planes
+ uint32_t mWidth; // width of largest plane (unpadded, as in nFrameWidth)
+ uint32_t mHeight; // height of largest plane (unpadded, as in nFrameHeight)
+ uint32_t mBitDepth; // useable bit depth (always MSB)
+ uint32_t mBitDepthAllocated; // bits per component (must be 8 or 16)
+
+ struct PlaneInfo {
+ uint32_t mOffset; // offset of first pixel of the plane in bytes
+ // from buffer offset
+ int32_t mColInc; // column increment in bytes
+ int32_t mRowInc; // row increment in bytes
+ uint32_t mHorizSubsampling; // subsampling compared to the largest plane
+ uint32_t mVertSubsampling; // subsampling compared to the largest plane
+ };
+ PlaneInfo mPlane[MAX_NUM_PLANES];
+
+ void initFromV1(const MediaImage&); // for internal use only
+};
+
+/**
+ * Aspects of color.
+ */
+
+// NOTE: this structure is expected to grow in the future if new color aspects are
+// added to codec bitstreams. OMX component should not require a specific nSize
+// though could verify that nSize is at least the size of the structure at the
+// time of implementation. All new fields will be added at the end of the structure
+// ensuring backward compatibility.
+struct ColorAspects {
+ // this is in sync with the range values in graphics.h
+ enum Range : uint32_t {
+ RangeUnspecified,
+ RangeFull,
+ RangeLimited,
+ RangeOther = 0xff,
+ };
+
+ enum Primaries : uint32_t {
+ PrimariesUnspecified,
+ PrimariesBT709_5, // Rec.ITU-R BT.709-5 or equivalent
+ PrimariesBT470_6M, // Rec.ITU-R BT.470-6 System M or equivalent
+ PrimariesBT601_6_625, // Rec.ITU-R BT.601-6 625 or equivalent
+ PrimariesBT601_6_525, // Rec.ITU-R BT.601-6 525 or equivalent
+ PrimariesGenericFilm, // Generic Film
+ PrimariesBT2020, // Rec.ITU-R BT.2020 or equivalent
+ PrimariesOther = 0xff,
+ };
+
+ // this partially in sync with the transfer values in graphics.h prior to the transfers
+ // unlikely to be required by Android section
+ enum Transfer : uint32_t {
+ TransferUnspecified,
+ TransferLinear, // Linear transfer characteristics
+ TransferSRGB, // sRGB or equivalent
+ TransferSMPTE170M, // SMPTE 170M or equivalent (e.g. BT.601/709/2020)
+ TransferGamma22, // Assumed display gamma 2.2
+ TransferGamma28, // Assumed display gamma 2.8
+ TransferST2084, // SMPTE ST 2084 for 10/12/14/16 bit systems
+ TransferHLG, // ARIB STD-B67 hybrid-log-gamma
+
+ // transfers unlikely to be required by Android
+ TransferSMPTE240M = 0x40, // SMPTE 240M
+ TransferXvYCC, // IEC 61966-2-4
+ TransferBT1361, // Rec.ITU-R BT.1361 extended gamut
+ TransferST428, // SMPTE ST 428-1
+ TransferOther = 0xff,
+ };
+
+ enum MatrixCoeffs : uint32_t {
+ MatrixUnspecified,
+ MatrixBT709_5, // Rec.ITU-R BT.709-5 or equivalent
+ MatrixBT470_6M, // KR=0.30, KB=0.11 or equivalent
+ MatrixBT601_6, // Rec.ITU-R BT.601-6 625 or equivalent
+ MatrixSMPTE240M, // SMPTE 240M or equivalent
+ MatrixBT2020, // Rec.ITU-R BT.2020 non-constant luminance
+ MatrixBT2020Constant, // Rec.ITU-R BT.2020 constant luminance
+ MatrixOther = 0xff,
+ };
+
+ // this is in sync with the standard values in graphics.h
+ enum Standard : uint32_t {
+ StandardUnspecified,
+ StandardBT709, // PrimariesBT709_5 and MatrixBT709_5
+ StandardBT601_625, // PrimariesBT601_6_625 and MatrixBT601_6
+ StandardBT601_625_Unadjusted, // PrimariesBT601_6_625 and KR=0.222, KB=0.071
+ StandardBT601_525, // PrimariesBT601_6_525 and MatrixBT601_6
+ StandardBT601_525_Unadjusted, // PrimariesBT601_6_525 and MatrixSMPTE240M
+ StandardBT2020, // PrimariesBT2020 and MatrixBT2020
+ StandardBT2020Constant, // PrimariesBT2020 and MatrixBT2020Constant
+ StandardBT470M, // PrimariesBT470_6M and MatrixBT470_6M
+ StandardFilm, // PrimariesGenericFilm and KR=0.253, KB=0.068
+ StandardOther = 0xff,
+ };
+
+ Range mRange; // IN/OUT
+ Primaries mPrimaries; // IN/OUT
+ Transfer mTransfer; // IN/OUT
+ MatrixCoeffs mMatrixCoeffs; // IN/OUT
+};
+
+#ifdef STRINGIFY_ENUMS
+
+inline static const char *asString(MediaImage::Type i, const char *def = "??") {
+ switch (i) {
+ case MediaImage::MEDIA_IMAGE_TYPE_UNKNOWN: return "Unknown";
+ case MediaImage::MEDIA_IMAGE_TYPE_YUV: return "YUV";
+ default: return def;
+ }
+}
+
+inline static const char *asString(MediaImage::PlaneIndex i, const char *def = "??") {
+ switch (i) {
+ case MediaImage::Y: return "Y";
+ case MediaImage::U: return "U";
+ case MediaImage::V: return "V";
+ default: return def;
+ }
+}
+
+inline static const char *asString(MediaImage2::Type i, const char *def = "??") {
+ switch (i) {
+ case MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN: return "Unknown";
+ case MediaImage2::MEDIA_IMAGE_TYPE_YUV: return "YUV";
+ case MediaImage2::MEDIA_IMAGE_TYPE_YUVA: return "YUVA";
+ case MediaImage2::MEDIA_IMAGE_TYPE_RGB: return "RGB";
+ case MediaImage2::MEDIA_IMAGE_TYPE_RGBA: return "RGBA";
+ case MediaImage2::MEDIA_IMAGE_TYPE_Y: return "Y";
+ default: return def;
+ }
+}
+
+inline static char asChar2(
+ MediaImage2::PlaneIndex i, MediaImage2::Type j, char def = '?') {
+ const char *planes = asString(j, NULL);
+ // handle unknown values
+ if (j == MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN || planes == NULL || i >= strlen(planes)) {
+ return def;
+ }
+ return planes[i];
+}
+
+inline static const char *asString(ColorAspects::Range i, const char *def = "??") {
+ switch (i) {
+ case ColorAspects::RangeUnspecified: return "Unspecified";
+ case ColorAspects::RangeFull: return "Full";
+ case ColorAspects::RangeLimited: return "Limited";
+ case ColorAspects::RangeOther: return "Other";
+ default: return def;
+ }
+}
+
+inline static const char *asString(ColorAspects::Primaries i, const char *def = "??") {
+ switch (i) {
+ case ColorAspects::PrimariesUnspecified: return "Unspecified";
+ case ColorAspects::PrimariesBT709_5: return "BT709_5";
+ case ColorAspects::PrimariesBT470_6M: return "BT470_6M";
+ case ColorAspects::PrimariesBT601_6_625: return "BT601_6_625";
+ case ColorAspects::PrimariesBT601_6_525: return "BT601_6_525";
+ case ColorAspects::PrimariesGenericFilm: return "GenericFilm";
+ case ColorAspects::PrimariesBT2020: return "BT2020";
+ case ColorAspects::PrimariesOther: return "Other";
+ default: return def;
+ }
+}
+
+inline static const char *asString(ColorAspects::Transfer i, const char *def = "??") {
+ switch (i) {
+ case ColorAspects::TransferUnspecified: return "Unspecified";
+ case ColorAspects::TransferLinear: return "Linear";
+ case ColorAspects::TransferSRGB: return "SRGB";
+ case ColorAspects::TransferSMPTE170M: return "SMPTE170M";
+ case ColorAspects::TransferGamma22: return "Gamma22";
+ case ColorAspects::TransferGamma28: return "Gamma28";
+ case ColorAspects::TransferST2084: return "ST2084";
+ case ColorAspects::TransferHLG: return "HLG";
+ case ColorAspects::TransferSMPTE240M: return "SMPTE240M";
+ case ColorAspects::TransferXvYCC: return "XvYCC";
+ case ColorAspects::TransferBT1361: return "BT1361";
+ case ColorAspects::TransferST428: return "ST428";
+ case ColorAspects::TransferOther: return "Other";
+ default: return def;
+ }
+}
+
+inline static const char *asString(ColorAspects::MatrixCoeffs i, const char *def = "??") {
+ switch (i) {
+ case ColorAspects::MatrixUnspecified: return "Unspecified";
+ case ColorAspects::MatrixBT709_5: return "BT709_5";
+ case ColorAspects::MatrixBT470_6M: return "BT470_6M";
+ case ColorAspects::MatrixBT601_6: return "BT601_6";
+ case ColorAspects::MatrixSMPTE240M: return "SMPTE240M";
+ case ColorAspects::MatrixBT2020: return "BT2020";
+ case ColorAspects::MatrixBT2020Constant: return "BT2020Constant";
+ case ColorAspects::MatrixOther: return "Other";
+ default: return def;
+ }
+}
+
+inline static const char *asString(ColorAspects::Standard i, const char *def = "??") {
+ switch (i) {
+ case ColorAspects::StandardUnspecified: return "Unspecified";
+ case ColorAspects::StandardBT709: return "BT709";
+ case ColorAspects::StandardBT601_625: return "BT601_625";
+ case ColorAspects::StandardBT601_625_Unadjusted: return "BT601_625_Unadjusted";
+ case ColorAspects::StandardBT601_525: return "BT601_525";
+ case ColorAspects::StandardBT601_525_Unadjusted: return "BT601_525_Unadjusted";
+ case ColorAspects::StandardBT2020: return "BT2020";
+ case ColorAspects::StandardBT2020Constant: return "BT2020Constant";
+ case ColorAspects::StandardBT470M: return "BT470M";
+ case ColorAspects::StandardFilm: return "Film";
+ case ColorAspects::StandardOther: return "Other";
+ default: return def;
+ }
+}
+
+#endif
+
+} // namespace android
+
+#endif // VIDEO_API_H_
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index a741f6d..5fb8ca8 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -18,6 +18,10 @@
Each section has its own include guard. This file should be included AFTER
the OMX include files. */
+#ifdef ANDROID
+namespace android {
+#endif
+
#ifdef OMX_Audio_h
/* asString definitions if media/openmax/OMX_Audio.h was included */
@@ -288,6 +292,7 @@
// case OMX_EventDynamicResourcesAvailable: return "DynamicResourcesAvailable";
// case OMX_EventPortFormatDetected: return "PortFormatDetected";
case OMX_EventOutputRendered: return "OutputRendered";
+ case OMX_EventDataSpaceChanged: return "DataSpaceChanged";
default: return def;
}
}
@@ -904,10 +909,11 @@
inline static const char *asString(OMX_VIDEO_HEVCPROFILETYPE i, const char *def = "!!") {
switch (i) {
- case OMX_VIDEO_HEVCProfileUnknown: return "Unknown"; // unused
- case OMX_VIDEO_HEVCProfileMain: return "Main";
- case OMX_VIDEO_HEVCProfileMain10: return "Main10";
- default: return def;
+ case OMX_VIDEO_HEVCProfileUnknown: return "Unknown"; // unused
+ case OMX_VIDEO_HEVCProfileMain: return "Main";
+ case OMX_VIDEO_HEVCProfileMain10: return "Main10";
+ case OMX_VIDEO_HEVCProfileMain10HDR10: return "Main10HDR10";
+ default: return def;
}
}
@@ -947,3 +953,7 @@
#endif // AS_STRING_FOR_OMX_VIDEOEXT_H
#endif // OMX_VideoExt_h
+
+#ifdef ANDROID
+} // namespace android
+#endif
diff --git a/include/media/openmax/OMX_Audio.h b/include/media/openmax/OMX_Audio.h
index a0cbd3b..d8bee76 100644
--- a/include/media/openmax/OMX_Audio.h
+++ b/include/media/openmax/OMX_Audio.h
@@ -178,7 +178,7 @@
OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
OMX_U32 nPortIndex; /**< port that this structure applies to */
OMX_U32 nChannels; /**< Number of channels (e.g. 2 for stereo) */
- OMX_NUMERICALDATATYPE eNumData; /**< indicates PCM data as signed or unsigned */
+ OMX_NUMERICALDATATYPE eNumData; /**< indicates PCM data as signed, unsigned or floating pt. */
OMX_ENDIANTYPE eEndian; /**< indicates PCM data as little or big endian */
OMX_BOOL bInterleaved; /**< True for normal interleaved data; false for
non-interleaved data (e.g. block data) */
diff --git a/include/media/openmax/OMX_Core.h b/include/media/openmax/OMX_Core.h
index f746a69..99a7622 100644
--- a/include/media/openmax/OMX_Core.h
+++ b/include/media/openmax/OMX_Core.h
@@ -524,6 +524,21 @@
* frame.
*/
OMX_EventOutputRendered = 0x7F000001,
+
+ /** For framework internal use only: event sent by OMXNodeInstance when it receives a graphic
+ * input buffer with a new dataspace for encoding. |arg1| will contain the dataspace. |arg2|
+ * will contain the ColorAspects requested by the component (or framework defaults) using
+ * the following bitfield layout:
+ *
+ * +----------+-------------+----------------+------------+
+ * | Range | Primaries | MatrixCoeffs | Transfer |
+ * +----------+-------------+----------------+------------+
+ * bits: 31....24 23.......16 15...........8 7........0
+ *
+ * TODO: We would really need to tie this to an output buffer, but OMX does not provide a
+ * fool-proof way to do that for video encoders.
+ */
+ OMX_EventDataSpaceChanged,
OMX_EventMax = 0x7FFFFFFF
} OMX_EVENTTYPE;
diff --git a/include/media/openmax/OMX_Types.h b/include/media/openmax/OMX_Types.h
index 5afaba0..515e002 100644
--- a/include/media/openmax/OMX_Types.h
+++ b/include/media/openmax/OMX_Types.h
@@ -280,12 +280,18 @@
/** The OMX_NUMERICALDATATYPE enumeration is used to indicate if data
- is signed or unsigned
+ is signed, unsigned or floating point (Android extension).
+
+ Android floating point support policy:
+ If component does not support floating point raw audio, it can reset
+ configuration to signed 16-bit integer (support for which is required.)
+ nBitsPerSample will be set to 32 for float data.
*/
typedef enum OMX_NUMERICALDATATYPE
{
OMX_NumericalDataSigned, /**< signed data */
OMX_NumericalDataUnsigned, /**< unsigned data */
+ OMX_NumericalDataFloat = 0x7F000001, /**< floating point data */
OMX_NumercialDataMax = 0x7FFFFFFF
} OMX_NUMERICALDATATYPE;
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index 836a153..1a2f0b5 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -177,10 +177,12 @@
/** HEVC Profile enum type */
typedef enum OMX_VIDEO_HEVCPROFILETYPE {
- OMX_VIDEO_HEVCProfileUnknown = 0x0,
- OMX_VIDEO_HEVCProfileMain = 0x1,
- OMX_VIDEO_HEVCProfileMain10 = 0x2,
- OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF
+ OMX_VIDEO_HEVCProfileUnknown = 0x0,
+ OMX_VIDEO_HEVCProfileMain = 0x1,
+ OMX_VIDEO_HEVCProfileMain10 = 0x2,
+ // Main10 profile with HDR SEI support.
+ OMX_VIDEO_HEVCProfileMain10HDR10 = 0x1000,
+ OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF
} OMX_VIDEO_HEVCPROFILETYPE;
/** HEVC Level enum type */
diff --git a/include/powermanager/PowerManager.h b/include/powermanager/PowerManager.h
index cbddc11..3268b45 100644
--- a/include/powermanager/PowerManager.h
+++ b/include/powermanager/PowerManager.h
@@ -28,8 +28,9 @@
USER_ACTIVITY_EVENT_OTHER = 0,
USER_ACTIVITY_EVENT_BUTTON = 1,
USER_ACTIVITY_EVENT_TOUCH = 2,
+ USER_ACTIVITY_EVENT_ACCESSIBILITY = 3,
- USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_TOUCH, // Last valid event code.
+ USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_ACCESSIBILITY, // Last valid event code.
};
}; // namespace android
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 8739625..51eac11 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -135,6 +135,8 @@
void dump(const char* what) const;
void dump(String8& res, const char* what) const;
+ static size_t getAllocationAlignment() { return kMemoryAlign; }
+
private:
struct chunk_t {
@@ -264,6 +266,12 @@
return mAllocator;
}
+// static
+size_t MemoryDealer::getAllocationAlignment()
+{
+ return SimpleBestFitAllocator::getAllocationAlignment();
+}
+
// ----------------------------------------------------------------------------
// align all the memory blocks on a cache-line boundary
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index aacbed5..4029496 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -277,51 +277,35 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- {
- Mutex::Autolock lock(mCore->mMutex);
-
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
- return NO_INIT;
- }
-
- if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
- BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
- "mode");
- return BAD_VALUE;
- }
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isAcquired()) {
- BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.detachConsumer();
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeSlots.insert(slot);
- mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
- VALIDATE_CONSISTENCY();
- producerListener = mCore->mConnectedProducerListener;
- consumerListener = mCore->mConsumerListener;
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
}
- // Call back without lock held
- if (producerListener != NULL) {
- producerListener->onSlotFreed(slot);
- }
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
+ if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
+ BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
+ "mode");
+ return BAD_VALUE;
}
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isAcquired()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mBufferState.detachConsumer();
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -592,40 +576,30 @@
return BAD_VALUE;
}
- sp<IConsumerListener> listener;
- {
- Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("setMaxBufferCount: producer is already connected");
- return INVALID_OPERATION;
- }
+ Mutex::Autolock lock(mCore->mMutex);
- if (bufferCount < mCore->mMaxAcquiredBufferCount) {
- BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than"
- "mMaxAcquiredBufferCount (%d)", bufferCount,
- mCore->mMaxAcquiredBufferCount);
- return BAD_VALUE;
- }
-
- int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
- mCore->mDequeueBufferCannotBlock, bufferCount) -
- mCore->getMaxBufferCountLocked();
- if (!mCore->adjustAvailableSlotsLocked(delta, nullptr)) {
- BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number"
- " of available slots. Delta = %d", delta);
- return BAD_VALUE;
- }
-
- mCore->mMaxBufferCount = bufferCount;
- if (delta < 0) {
- listener = mCore->mConsumerListener;
- }
+ if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("setMaxBufferCount: producer is already connected");
+ return INVALID_OPERATION;
}
- // Call back without lock held
- if (listener != NULL) {
- listener->onBuffersReleased();
+ if (bufferCount < mCore->mMaxAcquiredBufferCount) {
+ BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than"
+ "mMaxAcquiredBufferCount (%d)", bufferCount,
+ mCore->mMaxAcquiredBufferCount);
+ return BAD_VALUE;
}
+
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+ mCore->mDequeueBufferCannotBlock, bufferCount) -
+ mCore->getMaxBufferCountLocked();
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of "
+ "available slots. Delta = %d", delta);
+ return BAD_VALUE;
+ }
+
+ mCore->mMaxBufferCount = bufferCount;
return NO_ERROR;
}
@@ -640,9 +614,7 @@
return BAD_VALUE;
}
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- std::vector<int> freedSlots;
+ sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
@@ -679,7 +651,7 @@
}
int delta = maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount;
- if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
return BAD_VALUE;
}
@@ -687,19 +659,12 @@
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
VALIDATE_CONSISTENCY();
if (delta < 0) {
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
+ listener = mCore->mConsumerListener;
}
}
-
// Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
- }
- if (producerListener != NULL) {
- for (int i : freedSlots) {
- producerListener->onSlotFreed(i);
- }
+ if (listener != NULL) {
+ listener->onBuffersReleased();
}
return NO_ERROR;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index fc3ffed..ba07362 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -226,8 +226,7 @@
VALIDATE_CONSISTENCY();
}
-bool BufferQueueCore::adjustAvailableSlotsLocked(int delta,
- std::vector<int>* freedSlots) {
+bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
if (delta >= 0) {
// If we're going to fail, do so before modifying anything
if (delta > static_cast<int>(mUnusedSlots.size())) {
@@ -254,17 +253,11 @@
clearBufferSlotLocked(*slot);
mUnusedSlots.push_back(*slot);
mFreeSlots.erase(slot);
- if (freedSlots) {
- freedSlots->push_back(*slot);
- }
} else if (!mFreeBuffers.empty()) {
int slot = mFreeBuffers.back();
clearBufferSlotLocked(slot);
mUnusedSlots.push_back(slot);
mFreeBuffers.pop_back();
- if (freedSlots) {
- freedSlots->push_back(slot);
- }
} else {
return false;
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index cb5d773..4fd8b89 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -90,9 +90,7 @@
BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
maxDequeuedBuffers);
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- std::vector<int> freedSlots;
+ sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
@@ -144,26 +142,20 @@
}
int delta = maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount;
- if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
return BAD_VALUE;
}
mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
VALIDATE_CONSISTENCY();
if (delta < 0) {
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
+ listener = mCore->mConsumerListener;
}
mCore->mDequeueCondition.broadcast();
} // Autolock scope
// Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
- }
- if (producerListener != NULL) {
- for (int i : freedSlots) {
- producerListener->onSlotFreed(i);
- }
+ if (listener != NULL) {
+ listener->onBuffersReleased();
}
return NO_ERROR;
@@ -173,9 +165,7 @@
ATRACE_CALL();
BQ_LOGV("setAsyncMode: async = %d", async);
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- std::vector<int> freedSlots;
+ sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
@@ -201,7 +191,7 @@
mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount)
- mCore->getMaxBufferCountLocked();
- if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of "
"available slots. Delta = %d", delta);
return BAD_VALUE;
@@ -209,20 +199,12 @@
mCore->mAsyncMode = async;
VALIDATE_CONSISTENCY();
mCore->mDequeueCondition.broadcast();
- if (delta < 0) {
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
- }
+ listener = mCore->mConsumerListener;
} // Autolock scope
// Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
- }
- if (producerListener != NULL) {
- for (int i : freedSlots) {
- producerListener->onSlotFreed(i);
- }
+ if (listener != NULL) {
+ listener->onBuffersReleased();
}
return NO_ERROR;
}
@@ -379,9 +361,6 @@
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- int found = BufferItem::INVALID_BUFFER_SLOT;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
@@ -399,6 +378,7 @@
height = mCore->mDefaultHeight;
}
+ int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
&found);
@@ -428,8 +408,6 @@
mCore->mFreeSlots.insert(found);
mCore->clearBufferSlotLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
continue;
}
}
@@ -466,10 +444,6 @@
if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, usage))
{
- if (buffer != NULL) {
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
- }
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
mSlots[found].mRequestBufferCalled = false;
@@ -557,14 +531,6 @@
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
- // Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
- }
- if (producerListener != NULL) {
- producerListener->onSlotFreed(found);
- }
-
return returnFlags;
}
@@ -572,60 +538,44 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- {
- Mutex::Autolock lock(mCore->mMutex);
-
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
- return NO_INIT;
- }
-
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
- BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer "
- "mode");
- return BAD_VALUE;
- }
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isDequeued()) {
- BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (!mSlots[slot].mRequestBufferCalled) {
- BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
- slot);
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.detachProducer();
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeSlots.insert(slot);
- mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
- VALIDATE_CONSISTENCY();
- producerListener = mCore->mConnectedProducerListener;
- consumerListener = mCore->mConsumerListener;
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
}
- // Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
}
- if (producerListener != NULL) {
- producerListener->onSlotFreed(slot);
+
+ if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
+ BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode");
+ return BAD_VALUE;
}
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mRequestBufferCalled) {
+ BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
+ slot);
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mBufferState.detachProducer();
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+ VALIDATE_CONSISTENCY();
+
return NO_ERROR;
}
@@ -641,55 +591,41 @@
return BAD_VALUE;
}
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- int found = BufferQueueCore::INVALID_BUFFER_SLOT;
- {
- Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
- return NO_INIT;
- }
+ Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- if (mCore->mSingleBufferMode) {
- BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
- "mode");
- return BAD_VALUE;
- }
-
- mCore->waitWhileAllocatingLocked();
-
- if (mCore->mFreeBuffers.empty()) {
- return NO_MEMORY;
- }
-
- found = mCore->mFreeBuffers.front();
- mCore->mFreeBuffers.remove(found);
- mCore->mFreeSlots.insert(found);
-
- BQ_LOGV("detachNextBuffer detached slot %d", found);
-
- *outBuffer = mSlots[found].mGraphicBuffer;
- *outFence = mSlots[found].mFence;
- mCore->clearBufferSlotLocked(found);
- VALIDATE_CONSISTENCY();
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
}
- // Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
}
- if (producerListener != NULL) {
- producerListener->onSlotFreed(found);
+
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
+ "mode");
+ return BAD_VALUE;
}
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mFreeBuffers.empty()) {
+ return NO_MEMORY;
+ }
+
+ int found = mCore->mFreeBuffers.front();
+ mCore->mFreeBuffers.remove(found);
+ mCore->mFreeSlots.insert(found);
+
+ BQ_LOGV("detachNextBuffer detached slot %d", found);
+
+ *outBuffer = mSlots[found].mGraphicBuffer;
+ *outFence = mSlots[found].mFence;
+ mCore->clearBufferSlotLocked(found);
+ VALIDATE_CONSISTENCY();
+
return NO_ERROR;
}
@@ -825,6 +761,14 @@
return BAD_VALUE;
}
+ // If single buffer mode has just been enabled, cache the slot of the
+ // first buffer that is queued and mark it as the shared buffer.
+ if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot ==
+ BufferQueueCore::INVALID_BUFFER_SLOT) {
+ mCore->mSingleBufferSlot = slot;
+ mSlots[slot].mBufferState.mShared = true;
+ }
+
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
@@ -1084,102 +1028,82 @@
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL();
+ Mutex::Autolock lock(mCore->mMutex);
+ mConsumerName = mCore->mConsumerName;
+ BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
+ producerControlledByApp ? "true" : "false");
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("connect: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConsumerListener == NULL) {
+ BQ_LOGE("connect: BufferQueue has no consumer");
+ return NO_INIT;
+ }
+
+ if (output == NULL) {
+ BQ_LOGE("connect: output was NULL");
+ return BAD_VALUE;
+ }
+
+ if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("connect: already connected (cur=%d req=%d)",
+ mCore->mConnectedApi, api);
+ return BAD_VALUE;
+ }
+
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+ mDequeueTimeout < 0 ?
+ mCore->mConsumerControlledByApp && producerControlledByApp : false,
+ mCore->mMaxBufferCount) -
+ mCore->getMaxBufferCountLocked();
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
+ "slots. Delta = %d", delta);
+ return BAD_VALUE;
+ }
+
int status = NO_ERROR;
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- std::vector<int> freedSlots;
- {
- Mutex::Autolock lock(mCore->mMutex);
- mConsumerName = mCore->mConsumerName;
- BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
- producerControlledByApp ? "true" : "false");
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ case NATIVE_WINDOW_API_CPU:
+ case NATIVE_WINDOW_API_MEDIA:
+ case NATIVE_WINDOW_API_CAMERA:
+ mCore->mConnectedApi = api;
+ output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
+ mCore->mTransformHint,
+ static_cast<uint32_t>(mCore->mQueue.size()));
- if (mCore->mIsAbandoned) {
- BQ_LOGE("connect: BufferQueue has been abandoned");
- return NO_INIT;
- }
-
- if (mCore->mConsumerListener == NULL) {
- BQ_LOGE("connect: BufferQueue has no consumer");
- return NO_INIT;
- }
-
- if (output == NULL) {
- BQ_LOGE("connect: output was NULL");
- return BAD_VALUE;
- }
-
- if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("connect: already connected (cur=%d req=%d)",
- mCore->mConnectedApi, api);
- return BAD_VALUE;
- }
-
- bool dequeueBufferCannotBlock = mDequeueTimeout < 0 ?
- mCore->mConsumerControlledByApp && producerControlledByApp :
- false;
- int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
- dequeueBufferCannotBlock, mCore->mMaxBufferCount) -
- mCore->getMaxBufferCountLocked();
-
- if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
- BQ_LOGE("connect: BufferQueue failed to adjust the number of "
- "available slots. Delta = %d", delta);
- return BAD_VALUE;
- }
-
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- case NATIVE_WINDOW_API_CPU:
- case NATIVE_WINDOW_API_MEDIA:
- case NATIVE_WINDOW_API_CAMERA:
- mCore->mConnectedApi = api;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()));
-
- // Set up a death notification so that we can disconnect
- // automatically if the remote producer dies
- if (listener != NULL &&
- IInterface::asBinder(listener)->remoteBinder() !=
- NULL) {
- status = IInterface::asBinder(listener)->linkToDeath(
- static_cast<IBinder::DeathRecipient*>(this));
- if (status != NO_ERROR) {
- BQ_LOGE("connect: linkToDeath failed: %s (%d)",
- strerror(-status), status);
- }
+ // Set up a death notification so that we can disconnect
+ // automatically if the remote producer dies
+ if (listener != NULL &&
+ IInterface::asBinder(listener)->remoteBinder() != NULL) {
+ status = IInterface::asBinder(listener)->linkToDeath(
+ static_cast<IBinder::DeathRecipient*>(this));
+ if (status != NO_ERROR) {
+ BQ_LOGE("connect: linkToDeath failed: %s (%d)",
+ strerror(-status), status);
}
- mCore->mConnectedProducerListener = listener;
- break;
- default:
- BQ_LOGE("connect: unknown API %d", api);
- status = BAD_VALUE;
- break;
- }
-
- mCore->mBufferHasBeenQueued = false;
- mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock;
-
- mCore->mAllowAllocation = true;
- VALIDATE_CONSISTENCY();
-
- if (delta < 0) {
- consumerListener = mCore->mConsumerListener;
- producerListener = listener;
- }
+ }
+ mCore->mConnectedProducerListener = listener;
+ break;
+ default:
+ BQ_LOGE("connect: unknown API %d", api);
+ status = BAD_VALUE;
+ break;
}
- // Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
- }
- if (producerListener != NULL) {
- for (int i : freedSlots) {
- producerListener->onSlotFreed(i);
- }
+ mCore->mBufferHasBeenQueued = false;
+ mCore->mDequeueBufferCannotBlock = false;
+ if (mDequeueTimeout < 0) {
+ mCore->mDequeueBufferCannotBlock =
+ mCore->mConsumerControlledByApp && producerControlledByApp;
}
+ mCore->mAllowAllocation = true;
+ VALIDATE_CONSISTENCY();
return status;
}
@@ -1199,6 +1123,15 @@
return NO_ERROR;
}
+ if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
+ api = mCore->mConnectedApi;
+ // If we're asked to disconnect the currently connected api but
+ // nobody is connected, it's not really an error.
+ if (api == BufferQueueCore::NO_CONNECTED_API) {
+ return NO_ERROR;
+ }
+ }
+
switch (api) {
case NATIVE_WINDOW_API_EGL:
case NATIVE_WINDOW_API_CPU:
@@ -1407,40 +1340,19 @@
ATRACE_CALL();
BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
- sp<IConsumerListener> consumerListener;
- sp<IProducerListener> producerListener;
- std::vector<int> freedSlots;
- {
- Mutex::Autolock lock(mCore->mMutex);
- int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
- mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
- if (!mCore->adjustAvailableSlotsLocked(delta, &freedSlots)) {
- BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number"
- " of available slots. Delta = %d", delta);
- return BAD_VALUE;
- }
-
- mDequeueTimeout = timeout;
- mCore->mDequeueBufferCannotBlock = false;
-
- VALIDATE_CONSISTENCY();
-
- if (delta < 0) {
- consumerListener = mCore->mConsumerListener;
- producerListener = mCore->mConnectedProducerListener;
- }
+ Mutex::Autolock lock(mCore->mMutex);
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
+ mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of "
+ "available slots. Delta = %d", delta);
+ return BAD_VALUE;
}
- // Call back without lock held
- if (consumerListener != NULL) {
- consumerListener->onBuffersReleased();
- }
- if (producerListener != NULL) {
- for (int i : freedSlots) {
- producerListener->onSlotFreed(i);
- }
- }
+ mDequeueTimeout = timeout;
+ mCore->mDequeueBufferCannotBlock = false;
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index a22b81b..2187e5e 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -74,26 +74,12 @@
} else {
mConsumer->setConsumerName(mName);
}
-
- mMessageThread = new MessageThread(this);
- mMessageThread->run();
}
ConsumerBase::~ConsumerBase() {
CB_LOGV("~ConsumerBase");
-
- mMessageThread->requestExit();
- {
- Mutex::Autolock lock(mMessageQueueLock);
- mMessageQueue.emplace(std::piecewise_construct,
- std::forward_as_tuple(EXIT),
- std::forward_as_tuple());
- mMessageAvailable.signal();
- }
-
- mMessageThread->join();
-
Mutex::Autolock lock(mMutex);
+
// Verify that abandon() has been called before we get here. This should
// be done by ConsumerBase::onLastStrongRef(), but it's possible for a
// derived class to override that method and not call
@@ -114,13 +100,6 @@
}
void ConsumerBase::onFrameAvailable(const BufferItem& item) {
- Mutex::Autolock lock(mMessageQueueLock);
- mMessageQueue.emplace(std::piecewise_construct,
- std::forward_as_tuple(ON_FRAME_AVAILABLE),
- std::forward_as_tuple(item));
- mMessageAvailable.signal();
-}
-void ConsumerBase::onFrameAvailableHandler(const BufferItem& item) {
CB_LOGV("onFrameAvailable");
sp<FrameAvailableListener> listener;
@@ -136,14 +115,6 @@
}
void ConsumerBase::onFrameReplaced(const BufferItem &item) {
- Mutex::Autolock lock(mMessageQueueLock);
- mMessageQueue.emplace(std::piecewise_construct,
- std::forward_as_tuple(ON_FRAME_REPLACED),
- std::forward_as_tuple(item));
- mMessageAvailable.signal();
-}
-
-void ConsumerBase::onFrameReplacedHandler(const BufferItem &item) {
CB_LOGV("onFrameReplaced");
sp<FrameAvailableListener> listener;
@@ -159,14 +130,6 @@
}
void ConsumerBase::onBuffersReleased() {
- Mutex::Autolock lock(mMessageQueueLock);
- mMessageQueue.emplace(std::piecewise_construct,
- std::forward_as_tuple(ON_BUFFERS_RELEASED),
- std::forward_as_tuple());
- mMessageAvailable.signal();
-}
-
-void ConsumerBase::onBuffersReleasedHandler() {
Mutex::Autolock lock(mMutex);
CB_LOGV("onBuffersReleased");
@@ -186,45 +149,6 @@
}
void ConsumerBase::onSidebandStreamChanged() {
- Mutex::Autolock lock(mMessageQueueLock);
- mMessageQueue.emplace(std::piecewise_construct,
- std::forward_as_tuple(ON_SIDEBAND_STREAM_CHANGED),
- std::forward_as_tuple());
- mMessageAvailable.signal();
-}
-
-void ConsumerBase::onSidebandStreamChangedHandler() {
-}
-
-bool ConsumerBase::MessageThread::threadLoop() {
- Mutex::Autolock lock(mConsumerBase->mMessageQueueLock);
-
- if (mConsumerBase->mMessageQueue.empty()) {
- mConsumerBase->mMessageAvailable.wait(mConsumerBase->mMessageQueueLock);
- }
-
- while (!mConsumerBase->mMessageQueue.empty()) {
- auto nextMessage = mConsumerBase->mMessageQueue.front();
-
- switch (nextMessage.first) {
- case ON_FRAME_AVAILABLE:
- mConsumerBase->onFrameAvailableHandler(nextMessage.second);
- break;
- case ON_FRAME_REPLACED:
- mConsumerBase->onFrameReplacedHandler(nextMessage.second);
- break;
- case ON_BUFFERS_RELEASED:
- mConsumerBase->onBuffersReleasedHandler();
- break;
- case ON_SIDEBAND_STREAM_CHANGED:
- mConsumerBase->onSidebandStreamChangedHandler();
- break;
- case EXIT:
- break;
- }
- mConsumerBase->mMessageQueue.pop();
- }
- return true;
}
void ConsumerBase::abandon() {
@@ -238,7 +162,7 @@
}
void ConsumerBase::abandonLocked() {
- CB_LOGV("abandonLocked");
+ CB_LOGV("abandonLocked");
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(i);
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index a75569f..cb1ad35 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -338,7 +338,7 @@
}
case GET_RELEASED_BUFFERS: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint64_t slotMask;
+ uint64_t slotMask = 0;
status_t result = getReleasedBuffers(&slotMask);
reply->writeInt64(static_cast<int64_t>(slotMask));
reply->writeInt32(result);
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index c66694d..3101f3a 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -513,6 +513,7 @@
QueueBufferOutput* const output =
reinterpret_cast<QueueBufferOutput *>(
reply->writeInplace(sizeof(QueueBufferOutput)));
+ memset(output, 0, sizeof(QueueBufferOutput));
status_t res = connect(listener, api, producerControlledByApp, output);
reply->writeInt32(res);
return NO_ERROR;
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 39c1da8..81adc95 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -22,7 +22,6 @@
enum {
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
- ON_SLOT_FREED,
};
class BpProducerListener : public BpInterface<IProducerListener>
@@ -38,17 +37,6 @@
data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
}
-
- virtual void onSlotFreed(int slot) {
- Parcel data, reply;
- data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
- data.writeInt32(slot);
- status_t err = remote()->transact(ON_SLOT_FREED, data, &reply,
- IBinder::FLAG_ONEWAY);
- if (err != NO_ERROR) {
- ALOGE("onSlotFreed failed to transact %d", err);
- }
- }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -64,12 +52,6 @@
CHECK_INTERFACE(IProducerListener, data, reply);
onBufferReleased();
return NO_ERROR;
- case ON_SLOT_FREED: {
- CHECK_INTERFACE(IProducerListener, data, reply);
- int slot = data.readInt32();
- onSlotFreed(slot);
- return NO_ERROR;
- }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index 4b7986e..5983a6c 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -125,7 +125,7 @@
}
status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
- return mSensorEventConnection->enableDisable(sensor->getHandle(), true, 0, 0, false);
+ return mSensorEventConnection->enableDisable(sensor->getHandle(), true, us2ns(200000), 0, false);
}
status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c295684..8025ca5 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1259,4 +1259,57 @@
return err;
}
+namespace view {
+
+status_t Surface::writeToParcel(Parcel* parcel) const {
+ return writeToParcel(parcel, false);
+}
+
+status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
+ if (parcel == nullptr) return BAD_VALUE;
+
+ status_t res = OK;
+
+ if (!nameAlreadyWritten) res = parcel->writeString16(name);
+
+ if (res == OK) {
+ res = parcel->writeStrongBinder(
+ IGraphicBufferProducer::asBinder(graphicBufferProducer));
+ }
+ return res;
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel) {
+ return readFromParcel(parcel, false);
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
+ if (parcel == nullptr) return BAD_VALUE;
+
+ if (!nameAlreadyRead) {
+ name = readMaybeEmptyString16(parcel);
+ }
+
+ sp<IBinder> binder;
+
+ status_t res = parcel->readStrongBinder(&binder);
+ if (res != OK) return res;
+
+ graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
+
+ return OK;
+}
+
+String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
+ size_t len;
+ const char16_t* str = parcel->readString16Inplace(&len);
+ if (str != nullptr) {
+ return String16(str, len);
+ } else {
+ return String16();
+ }
+}
+
+} // namespace view
+
}; // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a945358..e1a951c 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -33,6 +33,7 @@
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
+#include <gui/BufferQueueCore.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -81,6 +82,13 @@
destroy();
}
+void SurfaceControl::disconnect() {
+ if (mGraphicBufferProducer != NULL) {
+ mGraphicBufferProducer->disconnect(
+ BufferQueueCore::CURRENTLY_CONNECTED_API);
+ }
+}
+
bool SurfaceControl::isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs)
{
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index b6af166..7790762 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -661,6 +661,57 @@
}
}
+TEST_F(BufferQueueTest, TestSingleBufferModeUsingAlreadyDequeuedBuffer) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, true, &output));
+
+ // Dequeue a buffer
+ int singleSlot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+ // Enable single buffer mode
+ ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+
+ // Queue the buffer
+ IGraphicBufferProducer::QueueBufferInput input(0, false,
+ HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
+ // Repeatedly queue and dequeue a buffer from the producer side, it should
+ // always return the same one. And we won't run out of buffers because it's
+ // always the same one and because async mode gets enabled.
+ int slot;
+ for (int i = 0; i < 5; i++) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(singleSlot, slot);
+ ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+ }
+
+ // acquire the buffer
+ BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(singleSlot, item.mSlot);
+ testBufferItem(input, item);
+ ASSERT_EQ(true, item.mQueuedBuffer);
+ ASSERT_EQ(false, item.mAutoRefresh);
+
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // attempt to acquire a second time should return no buffer available
+ ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
+ mConsumer->acquireBuffer(&item, 0));
+}
+
TEST_F(BufferQueueTest, TestTimeouts) {
createBufferQueue();
sp<DummyConsumer> dc(new DummyConsumer);
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index fad0baa..45b6463 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -727,55 +727,4 @@
ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
}
-struct TestListener : public BnProducerListener {
- virtual void onBufferReleased() {}
- virtual void onSlotFreed(int slot) {
- ASSERT_EQ(1, slot);
- }
-};
-
-TEST_F(IGraphicBufferProducerTest, SlotFreedListenerReturnsCorrectSlot) {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
-
- sp<DummyConsumer> consumerListener = new DummyConsumer;
- ASSERT_OK(mConsumer->consumerConnect(consumerListener, false));
-
- sp<TestListener> producerListener = new TestListener;
- IGraphicBufferProducer::QueueBufferOutput output;
- ASSERT_OK(mProducer->connect(producerListener, TEST_API,
- TEST_CONTROLLED_BY_APP, &output));
-
- ASSERT_OK(mProducer->setMaxDequeuedBufferCount(2));
-
- DequeueBufferResult buffer0;
- sp<GraphicBuffer> buf;
- ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS, &buffer0));
- ASSERT_OK(mProducer->requestBuffer(buffer0.slot, &buf));
-
- DequeueBufferResult buffer1;
- ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS, &buffer1));
-
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- ASSERT_OK(mProducer->queueBuffer(buffer0.slot, input, &output));
-
- DequeueBufferResult buffer2;
- ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS, &buffer2));
-
- ASSERT_OK(mProducer->cancelBuffer(buffer1.slot, Fence::NO_FENCE));
-
- ASSERT_OK(mProducer->setMaxDequeuedBufferCount(1));
-}
-
-
} // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 2356f54..a1578f6 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -564,7 +564,7 @@
ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1));
- thread->run();
+ thread->run("MyThread");
ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1));
//ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2]));
diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
index 14e42ac..2ce20eb 100644
--- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h
+++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
@@ -171,7 +171,7 @@
mProducerThread = producerThread;
producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
mProducerEglContext);
- producerThread->run();
+ producerThread->run("ProducerThread");
}
sp<ProducerThread> mProducerThread;
diff --git a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
index b8a7a90..c28b4d1 100644
--- a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
@@ -192,10 +192,6 @@
ASSERT_EQ(EGL_SUCCESS, eglGetError());
mProducerEglSurface = EGL_NO_SURFACE;
- // sleep for 10ms to allow any asynchronous operations to complete before
- // checking the reference counts
- usleep(10000);
-
// This test should have the only reference to buffer 0.
EXPECT_EQ(1, buffers[0]->getStrongCount());
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 1a904b5..dddcf92 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -295,7 +295,7 @@
};
sp<Thread> pt(new ProducerThread(mANW, testPixels));
- pt->run();
+ pt->run("ProducerThread");
glViewport(0, 0, texWidth, texHeight);
@@ -484,7 +484,7 @@
sp<Thread> pt(new ProducerThread(mANW));
- pt->run();
+ pt->run("ProducerThread");
// eat a frame so GLConsumer will own an at least one slot
dw->waitForFrame();
@@ -681,7 +681,7 @@
};
sp<Thread> pt(new ProducerThread(mANW));
- pt->run();
+ pt->run("ProducerThread");
mFW->waitForFrame();
mFW->waitForFrame();
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 99cbedc..d8702e5 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -127,13 +127,13 @@
if (!(mask & (mask - 1))) {
// power-of-2, i.e.: just one bit is set
if (mask & 1) {
- result.right = exclude.left;
+ result.right = min(result.right, exclude.left);
} else if (mask & 2) {
- result.bottom = exclude.top;
+ result.bottom = min(result.bottom, exclude.top);
} else if (mask & 4) {
- result.left = exclude.right;
+ result.left = max(result.left, exclude.right);
} else if (mask & 8) {
- result.top = exclude.bottom;
+ result.top = max(result.top, exclude.bottom);
}
}
}
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 267f8af..5d838e6 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -611,6 +611,12 @@
#endif
#endif
+#ifndef EGL_ANDROID_front_buffer_auto_refresh
+#define EGL_ANDROID_front_buffer_auto_refresh 1
+#define EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID 0x314C
+#endif
+
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 05700f8..e7703d8 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -72,7 +72,7 @@
* The rest (gExtensionString) depend on support in the EGL driver, and are
* only available if the driver supports them. However, some of these must be
* supported because they are used by the Android system itself; these are
- * listd as mandatory below and are required by the CDD. The system *assumes*
+ * listed as mandatory below and are required by the CDD. The system *assumes*
* the mandatory extensions are present and may not function properly if some
* are missing.
*
@@ -83,6 +83,7 @@
"EGL_ANDROID_presentation_time "
"EGL_KHR_swap_buffers_with_damage "
"EGL_ANDROID_create_native_client_buffer "
+ "EGL_ANDROID_front_buffer_auto_refresh "
;
extern char const * const gExtensionString =
"EGL_KHR_image " // mandatory
@@ -1194,12 +1195,11 @@
egl_surface_t const * const s = get_surface(surface);
- //XXX: temporary hack for the EGL hook-up for single buffer mode
- if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER ||
- value == EGL_SINGLE_BUFFER)) {
- native_window_set_auto_refresh(s->win.get(), true);
- return (native_window_set_single_buffer_mode(s->win.get(),
- value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE;
+ if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+ int err = native_window_set_auto_refresh(s->win.get(),
+ value ? true : false);
+ return (err == NO_ERROR) ? EGL_TRUE :
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
}
if (s->cnx->egl.eglSurfaceAttrib) {
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index f368d75..8c135c8 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -165,7 +165,7 @@
// running, so there's no need to keep a ref around.
sp<Thread> deferredSaveThread(new DeferredSaveThread());
mSavePending = true;
- deferredSaveThread->run();
+ deferredSaveThread->run("DeferredSaveThread");
}
}
}
diff --git a/opengl/specs/EGL_ANDROID_front_buffer_auto_refresh.txt b/opengl/specs/EGL_ANDROID_front_buffer_auto_refresh.txt
new file mode 100644
index 0000000..4c0e64c
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_front_buffer_auto_refresh.txt
@@ -0,0 +1,70 @@
+Name
+
+ ANDROID_front_buffer_auto_refresh
+
+Name Strings
+
+ EGL_ANDROID_front_buffer_auto_refresh
+
+Contributors
+
+ Pablo Ceballos
+
+Contact
+
+ Pablo Ceballos, Google Inc. (pceballos 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, February 3, 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 is intended for latency-sensitive applications that are doing
+ front-buffer rendering. It allows them to indicate to the Android compositor
+ that it should perform composition every time the display refreshes. This
+ removes the overhead of having to notify the compositor that the window
+ surface has been updated, but it comes at the cost of doing potentially
+ unneeded composition work if the window surface has not been updated.
+
+New Types
+
+ None
+
+New Procedures and Functions
+
+ None
+
+New Tokens
+
+ EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID 0x314C
+
+Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
+"Surface Attributes", page 43:
+
+ If attribute is EGL_ANDROID_front_buffer_auto_refresh, then value specifies
+ whether to enable or disable auto-refresh in the Android compositor when
+ doing front-buffer rendering.
+
+Issues
+
+ None
+
+Revision History
+
+#1 (Pablo Ceballos, February 3, 2016)
+ - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
index f4de1b3..8f1eaf3 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -6,10 +6,10 @@
Value Extension
---------------- ----------------------------------
-0x3140 EGL_ANDROID_image_native_buffer
-0x3141 (unused)
+0x3140 EGL_NATIVE_BUFFER_ANDROID (EGL_ANDROID_image_native_buffer)
+0x3141 EGL_PLATFORM_ANDROID_KHR (KHR_platform_android)
0x3142 EGL_RECORDABLE_ANDROID (EGL_ANDROID_recordable)
-0x3143 EGL_VERSION_HW_ANDROID (internal use)
+0x3143 EGL_NATIVE_BUFFER_USAGE_ANDROID (EGL_ANDROID_create_native_client_buffer)
0x3144 EGL_SYNC_NATIVE_FENCE_ANDROID (EGL_ANDROID_native_fence_sync)
0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync)
@@ -18,4 +18,5 @@
0x3149 EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop)
0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
-0x314C - 0x314F (unused)
+0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
+0x314D - 0x314F (unused)
diff --git a/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp b/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp
index e701481..52295a1 100644
--- a/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp
@@ -2,7 +2,67 @@
static jint
android_glCreateShaderProgramv
(JNIEnv *_env, jobject _this, jint type, jobjectArray strings) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ GLsizei _count;
+ const GLchar** _strings = NULL;
+ jstring* _jstrings = NULL;
+ GLuint _returnValue = 0;
- jniThrowException(_env, "java/lang/UnsupportedOperationException", "not yet implemented");
- return 0;
+ if (!strings) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "strings == null";
+ goto exit;
+ }
+
+ _count = _env->GetArrayLength(strings);
+
+ _strings = (const GLchar**) calloc(_count, sizeof(const GLchar*));
+ if (!_strings) {
+ _exception = 1;
+ _exceptionType = "java/lang/OutOfMemoryError";
+ _exceptionMessage = "out of memory";
+ goto exit;
+ }
+
+ _jstrings = (jstring*) calloc(_count, sizeof(jstring));
+ if (!_jstrings) {
+ _exception = 1;
+ _exceptionType = "java/lang/OutOfMemoryError";
+ _exceptionMessage = "out of memory";
+ goto exit;
+ }
+
+ for(int i = 0; i < _count; i++) {
+ _jstrings[i] = (jstring) _env->GetObjectArrayElement(strings, i);
+ if (!_jstrings[i]) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "strings == null";
+ goto exit;
+ }
+ _strings[i] = _env->GetStringUTFChars(_jstrings[i], 0);
+ }
+
+ _returnValue = glCreateShaderProgramv((GLenum)type, _count, _strings);
+exit:
+ if (_strings && _jstrings) {
+ for(int i = 0; i < _count; i++) {
+ if (_strings[i] && _jstrings[i]) {
+ _env->ReleaseStringUTFChars(_jstrings[i], _strings[i]);
+ }
+ }
+ }
+ if (_strings) {
+ free(_strings);
+ }
+ if (_jstrings) {
+ free(_jstrings);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return (jint)_returnValue;
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index f91dac5..66ef4eb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1278,6 +1278,9 @@
}
int SensorService::getNumEventsForSensorType(int sensor_event_type) {
+ if (sensor_event_type >= SENSOR_TYPE_DEVICE_PRIVATE_BASE) {
+ return 16;
+ }
switch (sensor_event_type) {
case SENSOR_TYPE_ROTATION_VECTOR:
case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index e0d7339..5ba387d 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -322,7 +322,6 @@
mNumResyncSamples = 0;
mFirstResyncSample = 0;
mNumResyncSamplesSincePresent = 0;
- mNumPresentWithoutResyncSamples = 0;
resetErrorLocked();
}
@@ -347,15 +346,6 @@
updateErrorLocked();
- // This is a workaround for b/25845510.
- // If we have no resync samples after many presents, something is wrong with
- // HW vsync. Tell SF to disable HW vsync now and re-enable it next time.
- if (mNumResyncSamples == 0 &&
- mNumPresentWithoutResyncSamples++ > MAX_PRESENT_WITHOUT_RESYNC_SAMPLES) {
- mNumPresentWithoutResyncSamples = 0;
- return false;
- }
-
return !mModelUpdated || mError > kErrorThreshold;
}
@@ -364,7 +354,6 @@
mModelUpdated = false;
mNumResyncSamples = 0;
- mNumPresentWithoutResyncSamples = 0;
}
bool DispSync::addResyncSample(nsecs_t timestamp) {
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index 1ee0865..a8524b9 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -140,7 +140,6 @@
enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 };
enum { NUM_PRESENT_SAMPLES = 8 };
enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
- enum { MAX_PRESENT_WITHOUT_RESYNC_SAMPLES = 8 };
// mPeriod is the computed period of the modeled vsync events in
// nanoseconds.
@@ -169,7 +168,6 @@
size_t mFirstResyncSample;
size_t mNumResyncSamples;
int mNumResyncSamplesSincePresent;
- int mNumPresentWithoutResyncSamples;
// These member variables store information about the present fences used
// to validate the currently computed model.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 04256e1..42d0810 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,6 +116,7 @@
mCurrentState.active.w = w;
mCurrentState.active.h = h;
+ mCurrentState.active.transform.set(0, 0);
mCurrentState.active.crop.makeInvalid();
mCurrentState.z = 0;
#ifdef USE_HWC2
@@ -126,7 +127,6 @@
mCurrentState.layerStack = 0;
mCurrentState.flags = layerFlags;
mCurrentState.sequence = 0;
- mCurrentState.transform.set(0, 0);
mCurrentState.requested = mCurrentState.active;
// drawing state & current state are identical
@@ -165,6 +165,11 @@
}
Layer::~Layer() {
+ sp<Client> c(mClientRef.promote());
+ if (c != 0) {
+ c->detachLayer(this);
+ }
+
for (auto& point : mRemoteSyncPoints) {
point->setTransactionApplied();
}
@@ -260,10 +265,6 @@
// the layer has been remove from the current state list (and just before
// it's removed from the drawing state list)
void Layer::onRemoved() {
- sp<Client> c(mClientRef.promote());
- if (c != 0) {
- c->detachLayer(this);
- }
mSurfaceFlingerConsumer->abandon();
}
@@ -399,9 +400,9 @@
activeCrop = s.active.crop;
}
- activeCrop = s.transform.transform(activeCrop);
+ activeCrop = s.active.transform.transform(activeCrop);
activeCrop.intersect(hw->getViewport(), &activeCrop);
- activeCrop = s.transform.inverse().transform(activeCrop);
+ activeCrop = s.active.transform.inverse().transform(activeCrop);
// This needs to be here as transform.transform(Rect) computes the
// transformed rect and then takes the bounding box of the result before
@@ -534,13 +535,13 @@
Region activeTransparentRegion(s.activeTransparentRegion);
if (!s.active.crop.isEmpty()) {
Rect activeCrop(s.active.crop);
- activeCrop = s.transform.transform(activeCrop);
+ activeCrop = s.active.transform.transform(activeCrop);
#ifdef USE_HWC2
activeCrop.intersect(displayDevice->getViewport(), &activeCrop);
#else
activeCrop.intersect(hw->getViewport(), &activeCrop);
#endif
- activeCrop = s.transform.inverse().transform(activeCrop);
+ activeCrop = s.active.transform.inverse().transform(activeCrop);
// This needs to be here as transform.transform(Rect) computes the
// transformed rect and then takes the bounding box of the result before
// returning. This means
@@ -557,7 +558,7 @@
activeTransparentRegion.orSelf(Rect(activeCrop.right, activeCrop.top,
s.active.w, activeCrop.bottom));
}
- Rect frame(s.transform.transform(computeBounds(activeTransparentRegion)));
+ Rect frame(s.active.transform.transform(computeBounds(activeTransparentRegion)));
#ifdef USE_HWC2
frame.intersect(displayDevice->getViewport(), &frame);
const Transform& tr(displayDevice->getTransform());
@@ -603,7 +604,7 @@
*/
const Transform bufferOrientation(mCurrentTransform);
- Transform transform(tr * s.transform * bufferOrientation);
+ Transform transform(tr * s.active.transform * bufferOrientation);
if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
/*
@@ -779,7 +780,7 @@
}
// Subtract the transparent region and snap to the bounds
Rect bounds = reduce(win, s.activeTransparentRegion);
- Rect frame(s.transform.transform(bounds));
+ Rect frame(s.active.transform.transform(bounds));
frame.intersect(displayDevice->getViewport(), &frame);
auto& displayTransform(displayDevice->getTransform());
auto position = displayTransform.transform(frame);
@@ -825,7 +826,7 @@
}
// subtract the transparent region and snap to the bounds
Rect bounds = reduce(win, s.activeTransparentRegion);
- Rect frame(s.transform.transform(bounds));
+ Rect frame(s.active.transform.transform(bounds));
frame.intersect(hw->getViewport(), &frame);
const Transform& tr(hw->getTransform());
return Rect(tr.transform(frame));
@@ -1120,7 +1121,7 @@
{
const Layer::State& s(getDrawingState());
const Transform tr(useIdentityTransform ?
- hw->getTransform() : hw->getTransform() * s.transform);
+ hw->getTransform() : hw->getTransform() * s.active.transform);
const uint32_t hw_h = hw->getHeight();
Rect win(s.active.w, s.active.h);
if (!s.active.crop.isEmpty()) {
@@ -1403,8 +1404,8 @@
this->contentDirty = true;
// we may use linear filtering, if the matrix scales us
- const uint8_t type = c.transform.getType();
- mNeedsFiltering = (!c.transform.preserveRects() ||
+ const uint8_t type = c.active.transform.getType();
+ mNeedsFiltering = (!c.active.transform.preserveRects() ||
(type >= Transform::SCALE));
}
@@ -1426,10 +1427,10 @@
}
bool Layer::setPosition(float x, float y) {
- if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y)
+ if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y)
return false;
mCurrentState.sequence++;
- mCurrentState.transform.set(x, y);
+ mCurrentState.requested.transform.set(x, y);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1467,7 +1468,7 @@
}
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
mCurrentState.sequence++;
- mCurrentState.transform.set(
+ mCurrentState.requested.transform.set(
matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -1632,7 +1633,7 @@
recomputeVisibleRegions = true;
const State& s(getDrawingState());
- return s.transform.transform(Region(Rect(s.active.w, s.active.h)));
+ return s.active.transform.transform(Region(Rect(s.active.w, s.active.h)));
}
Region outDirtyRegion;
@@ -1938,7 +1939,7 @@
Region dirtyRegion(Rect(s.active.w, s.active.h));
// transform the dirty region to window-manager space
- outDirtyRegion = (s.transform.transform(dirtyRegion));
+ outDirtyRegion = (s.active.transform.transform(dirtyRegion));
}
return outDirtyRegion;
}
@@ -2000,13 +2001,13 @@
"alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
#endif
" client=%p\n",
- s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h,
+ s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h,
s.active.crop.left, s.active.crop.top,
s.active.crop.right, s.active.crop.bottom,
isOpaque(s), contentDirty,
s.alpha, s.flags,
- s.transform[0][0], s.transform[0][1],
- s.transform[1][0], s.transform[1][1],
+ s.active.transform[0][0], s.active.transform[0][1],
+ s.active.transform[1][0], s.active.transform[1][1],
client.get());
sp<const GraphicBuffer> buf0(mActiveBuffer);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 26e1adb..b0088e6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -94,6 +94,8 @@
uint32_t w;
uint32_t h;
Rect crop;
+ Transform transform;
+
inline bool operator ==(const Geometry& rhs) const {
return (w == rhs.w && h == rhs.h && crop == rhs.crop);
}
@@ -116,7 +118,6 @@
uint8_t mask;
uint8_t reserved[2];
int32_t sequence; // changes when visible regions can change
- Transform transform;
bool modified;
// If set, defers this state update until the Layer identified by handle
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0a3534f..3a0e21f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1563,7 +1563,7 @@
// compute the actual visible region
// TODO: we could cache the transformed region
const Layer::State& s(layer->getDrawingState());
- Region visibleReg = s.transform.transform(
+ Region visibleReg = s.active.transform.transform(
Region(Rect(s.active.w, s.active.h)));
invalidateLayerStack(s.layerStack, visibleReg);
}
@@ -1666,12 +1666,12 @@
// handle hidden surfaces by setting the visible region to empty
if (CC_LIKELY(layer->isVisible())) {
const bool translucent = !layer->isOpaque(s);
- Rect bounds(s.transform.transform(layer->computeBounds()));
+ Rect bounds(s.active.transform.transform(layer->computeBounds()));
visibleRegion.set(bounds);
if (!visibleRegion.isEmpty()) {
// Remove the transparent area from the visible region
if (translucent) {
- const Transform tr(s.transform);
+ const Transform tr(s.active.transform);
if (tr.transformed()) {
if (tr.preserveRects()) {
// transform the transparent region
@@ -1687,7 +1687,7 @@
}
// compute the opaque region
- const int32_t layerOrientation = s.transform.getOrientation();
+ const int32_t layerOrientation = s.active.transform.getOrientation();
if (s.alpha == 1.0f && !translucent &&
((layerOrientation & Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
@@ -3440,14 +3440,18 @@
} else {
ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
result = INVALID_OPERATION;
+ window->cancelBuffer(window, buffer, syncFd);
+ buffer = NULL;
}
// destroy our image
eglDestroyImageKHR(mEGLDisplay, image);
} else {
result = BAD_VALUE;
}
- // queueBuffer takes ownership of syncFd
- result = window->queueBuffer(window, buffer, syncFd);
+ if (buffer) {
+ // queueBuffer takes ownership of syncFd
+ result = window->queueBuffer(window, buffer, syncFd);
+ }
}
} else {
result = BAD_VALUE;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 737cc82..d864874 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -1593,7 +1593,7 @@
// compute the actual visible region
// TODO: we could cache the transformed region
const Layer::State& s(layer->getDrawingState());
- Region visibleReg = s.transform.transform(
+ Region visibleReg = s.active.transform.transform(
Region(Rect(s.active.w, s.active.h)));
invalidateLayerStack(s.layerStack, visibleReg);
}
@@ -1707,12 +1707,12 @@
// handle hidden surfaces by setting the visible region to empty
if (CC_LIKELY(layer->isVisible())) {
const bool translucent = !layer->isOpaque(s);
- Rect bounds(s.transform.transform(layer->computeBounds()));
+ Rect bounds(s.active.transform.transform(layer->computeBounds()));
visibleRegion.set(bounds);
if (!visibleRegion.isEmpty()) {
// Remove the transparent area from the visible region
if (translucent) {
- const Transform tr(s.transform);
+ const Transform tr(s.active.transform);
if (tr.transformed()) {
if (tr.preserveRects()) {
// transform the transparent region
@@ -1728,7 +1728,7 @@
}
// compute the opaque region
- const int32_t layerOrientation = s.transform.getOrientation();
+ const int32_t layerOrientation = s.active.transform.getOrientation();
if (s.alpha==255 && !translucent &&
((layerOrientation & Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
@@ -3478,14 +3478,18 @@
} else {
ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
result = INVALID_OPERATION;
+ window->cancelBuffer(window, buffer, syncFd);
+ buffer = NULL;
}
// destroy our image
eglDestroyImageKHR(mEGLDisplay, image);
} else {
result = BAD_VALUE;
}
- // queueBuffer takes ownership of syncFd
- result = window->queueBuffer(window, buffer, syncFd);
+ if (buffer) {
+ // queueBuffer takes ownership of syncFd
+ result = window->queueBuffer(window, buffer, syncFd);
+ }
}
} else {
result = BAD_VALUE;
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index e77952a..aec0fd0 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -394,7 +394,7 @@
}
}
-const char* LayerRef::GetName() {
+const char* LayerRef::GetName() const {
return layer_->properties.layerName;
}
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index fc60f35..eba58c6 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -265,6 +265,7 @@
const VkAllocationCallbacks* alloc;
uint32_t num_physical_devices;
+ VkPhysicalDevice physical_devices_top[kMaxPhysicalDevices];
VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
DeviceExtensionSet physical_device_driver_extensions[kMaxPhysicalDevices];
@@ -450,6 +451,51 @@
return VK_SUCCESS;
}
+template <class TCreateInfo, class TObject>
+bool AddLayersToCreateInfo(TCreateInfo& local_create_info,
+ const TObject& object,
+ const VkAllocationCallbacks* alloc,
+ bool& allocatedMemory) {
+ // This should never happen and means there is a likely a bug in layer
+ // tracking
+ if (object->active_layers.size() < local_create_info.enabledLayerCount) {
+ ALOGE("Total number of layers is less than those enabled by the app!");
+ return false;
+ }
+ // Check if the total number of layers enabled is greater than those
+ // enabled by the application. If it is then we have system enabled
+ // layers which need to be added to the list of layers passed in through
+ // create.
+ if (object->active_layers.size() > local_create_info.enabledLayerCount) {
+ void* mem = alloc->pfnAllocation(
+ alloc->pUserData, object->active_layers.size() * sizeof(char*),
+ alignof(char*), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+ if (mem) {
+ local_create_info.enabledLayerCount = 0;
+ const char** names = static_cast<const char**>(mem);
+ for (const auto& layer : object->active_layers) {
+ const char* name = layer.GetName();
+ names[local_create_info.enabledLayerCount++] = name;
+ }
+ local_create_info.ppEnabledLayerNames = names;
+ } else {
+ ALOGE("System layers cannot be enabled: memory allocation failed");
+ return false;
+ }
+ allocatedMemory = true;
+ } else {
+ allocatedMemory = false;
+ }
+ return true;
+}
+
+template <class T>
+void FreeAllocatedLayerCreateInfo(T& local_create_info,
+ const VkAllocationCallbacks* alloc) {
+ alloc->pfnFree(alloc->pUserData,
+ const_cast<char**>(local_create_info.ppEnabledLayerNames));
+}
+
template <class TCreateInfo>
bool AddExtensionToCreateInfo(TCreateInfo& local_create_info,
const char* extension_name,
@@ -459,7 +505,7 @@
void* mem = alloc->pfnAllocation(
alloc->pUserData,
local_create_info.enabledExtensionCount * sizeof(char*), alignof(char*),
- VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+ VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (mem) {
const char** enabled_extensions = static_cast<const char**>(mem);
for (uint32_t i = 0; i < extension_count; ++i) {
@@ -469,17 +515,16 @@
enabled_extensions[extension_count] = extension_name;
local_create_info.ppEnabledExtensionNames = enabled_extensions;
} else {
- ALOGW("%s extension cannot be enabled: memory allocation failed",
+ ALOGE("%s extension cannot be enabled: memory allocation failed",
extension_name);
- local_create_info.enabledExtensionCount--;
return false;
}
return true;
}
template <class T>
-void FreeAllocatedCreateInfo(T& local_create_info,
- const VkAllocationCallbacks* alloc) {
+void FreeAllocatedExtensionCreateInfo(T& local_create_info,
+ const VkAllocationCallbacks* alloc) {
alloc->pfnFree(
alloc->pUserData,
const_cast<char**>(local_create_info.ppEnabledExtensionNames));
@@ -528,17 +573,24 @@
return create_info;
}
-// Separate out cleaning up the layers and instance storage
-// to avoid code duplication in the many failure cases in
-// in CreateInstance_Top
-void TeardownInstance(
- VkInstance vkinstance,
- const VkAllocationCallbacks* /* allocator */) {
- Instance& instance = GetDispatchParent(vkinstance);
- instance.active_layers.clear();
- const VkAllocationCallbacks* alloc = instance.alloc;
- instance.~Instance();
- alloc->pfnFree(alloc->pUserData, &instance);
+// Clean up and deallocate an Instance; called from both the failure paths in
+// CreateInstance_Top as well as from DestroyInstance_Top. This function does
+// not call down the dispatch chain; that should be done before calling this
+// function, iff the lower vkCreateInstance call has been made and returned
+// successfully.
+void DestroyInstance(Instance* instance,
+ const VkAllocationCallbacks* allocator) {
+ if (instance->message) {
+ PFN_vkDestroyDebugReportCallbackEXT destroy_debug_report_callback;
+ destroy_debug_report_callback =
+ reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
+ GetInstanceProcAddr_Top(instance->handle,
+ "vkDestroyDebugReportCallbackEXT"));
+ destroy_debug_report_callback(instance->handle, instance->message,
+ allocator);
+ }
+ instance->~Instance();
+ allocator->pfnFree(allocator->pUserData, instance);
}
} // anonymous namespace
@@ -941,14 +993,7 @@
if (instance.drv.instance != VK_NULL_HANDLE &&
instance.drv.dispatch.DestroyInstance) {
instance.drv.dispatch.DestroyInstance(instance.drv.instance, allocator);
- }
- if (instance.message) {
- PFN_vkDestroyDebugReportCallbackEXT destroy_debug_report_callback;
- destroy_debug_report_callback =
- reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
- vkGetInstanceProcAddr(vkinstance,
- "vkDestroyDebugReportCallbackEXT"));
- destroy_debug_report_callback(vkinstance, instance.message, allocator);
+ instance.drv.instance = VK_NULL_HANDLE;
}
}
@@ -1042,7 +1087,7 @@
ALOGV(" no layer");
Instance& instance = GetDispatchParent(gpu);
size_t gpu_idx = 0;
- while (instance.physical_devices[gpu_idx] != gpu)
+ while (instance.physical_devices_top[gpu_idx] != gpu)
gpu_idx++;
const DeviceExtensionSet driver_extensions =
instance.physical_device_driver_extensions[gpu_idx];
@@ -1093,8 +1138,7 @@
result = ActivateAllLayers(create_info, instance, instance);
if (result != VK_SUCCESS) {
- DestroyInstance_Bottom(instance->handle, allocator);
- TeardownInstance(instance->handle, allocator);
+ DestroyInstance(instance, allocator);
return result;
}
@@ -1115,8 +1159,7 @@
sizeof(VkLayerInstanceLink) * instance->active_layers.size()));
if (!layer_instance_link_info) {
ALOGE("Failed to alloc Instance objects for layers");
- DestroyInstance_Bottom(instance->handle, allocator);
- TeardownInstance(instance->handle, allocator);
+ DestroyInstance(instance, allocator);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
@@ -1143,8 +1186,7 @@
reinterpret_cast<PFN_vkCreateInstance>(
next_gipa(VK_NULL_HANDLE, "vkCreateInstance"));
if (!create_instance) {
- DestroyInstance_Bottom(instance->handle, allocator);
- TeardownInstance(instance->handle, allocator);
+ DestroyInstance(instance, allocator);
return VK_ERROR_INITIALIZATION_FAILED;
}
VkLayerInstanceCreateInfo instance_create_info;
@@ -1164,20 +1206,34 @@
enable_callback =
property_get_bool("debug.vulkan.enable_callback", false);
if (enable_callback) {
- enable_callback = AddExtensionToCreateInfo(
- local_create_info, "VK_EXT_debug_report", instance->alloc);
+ if (!AddExtensionToCreateInfo(local_create_info,
+ "VK_EXT_debug_report", allocator)) {
+ DestroyInstance(instance, allocator);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
}
}
+ bool allocatedLayerMem;
+ if (!AddLayersToCreateInfo(local_create_info, instance, allocator,
+ allocatedLayerMem)) {
+ if (enable_callback) {
+ FreeAllocatedExtensionCreateInfo(local_create_info, allocator);
+ }
+ DestroyInstance(instance, allocator);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
result = create_instance(&local_create_info, allocator, &local_instance);
+ if (allocatedLayerMem) {
+ FreeAllocatedLayerCreateInfo(local_create_info, allocator);
+ }
if (enable_callback) {
- FreeAllocatedCreateInfo(local_create_info, allocator);
+ FreeAllocatedExtensionCreateInfo(local_create_info, allocator);
}
if (result != VK_SUCCESS) {
- DestroyInstance_Bottom(instance->handle, allocator);
- TeardownInstance(instance->handle, allocator);
+ DestroyInstance(instance, allocator);
return result;
}
@@ -1189,14 +1245,31 @@
ALOGV("Failed to initialize instance dispatch table");
PFN_vkDestroyInstance destroy_instance =
reinterpret_cast<PFN_vkDestroyInstance>(
- next_gipa(VK_NULL_HANDLE, "vkDestroyInstance"));
+ next_gipa(local_instance, "vkDestroyInstance"));
if (!destroy_instance) {
ALOGD("Loader unable to find DestroyInstance");
return VK_ERROR_INITIALIZATION_FAILED;
}
destroy_instance(local_instance, allocator);
- DestroyInstance_Bottom(instance->handle, allocator);
- TeardownInstance(instance->handle, allocator);
+ DestroyInstance(instance, allocator);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ // Capture the physical devices from the top of the
+ // chain in case it has been wrapped by a layer.
+ uint32_t num_physical_devices = 0;
+ result = instance_dispatch.EnumeratePhysicalDevices(
+ local_instance, &num_physical_devices, nullptr);
+ if (result != VK_SUCCESS) {
+ DestroyInstance(instance, allocator);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ num_physical_devices = std::min(num_physical_devices, kMaxPhysicalDevices);
+ result = instance_dispatch.EnumeratePhysicalDevices(
+ local_instance, &num_physical_devices,
+ instance->physical_devices_top);
+ if (result != VK_SUCCESS) {
+ DestroyInstance(instance, allocator);
return VK_ERROR_INITIALIZATION_FAILED;
}
*instance_out = local_instance;
@@ -1244,9 +1317,10 @@
const VkAllocationCallbacks* allocator) {
if (!vkinstance)
return;
+ if (!allocator)
+ allocator = &kDefaultAllocCallbacks;
GetDispatchTable(vkinstance).DestroyInstance(vkinstance, allocator);
-
- TeardownInstance(vkinstance, allocator);
+ DestroyInstance(&(GetDispatchParent(vkinstance)), allocator);
}
VKAPI_ATTR
@@ -1287,10 +1361,6 @@
return result;
}
- size_t gpu_idx = 0;
- while (instance.physical_devices[gpu_idx] != gpu)
- gpu_idx++;
-
uint32_t activated_layers = 0;
VkLayerDeviceCreateInfo chain_info;
VkLayerDeviceLink* layer_device_link_info = nullptr;
@@ -1337,7 +1407,7 @@
}
PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>(
- next_gipa(VK_NULL_HANDLE, "vkCreateDevice"));
+ next_gipa(instance.handle, "vkCreateDevice"));
if (!create_device) {
ALOGE("Unable to find vkCreateDevice for driver");
DestroyDevice(device);
@@ -1355,8 +1425,19 @@
device_create_info.pNext = local_create_info.pNext;
local_create_info.pNext = &device_create_info;
+ bool allocatedLayerMem;
+ if (!AddLayersToCreateInfo(local_create_info, device, allocator,
+ allocatedLayerMem)) {
+ DestroyDevice(device);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
result = create_device(gpu, &local_create_info, allocator, &local_device);
+ if (allocatedLayerMem) {
+ FreeAllocatedLayerCreateInfo(local_create_info, allocator);
+ }
+
if (result != VK_SUCCESS) {
DestroyDevice(device);
return result;
@@ -1374,7 +1455,7 @@
ALOGV("Failed to initialize device dispatch table");
PFN_vkDestroyDevice destroy_device =
reinterpret_cast<PFN_vkDestroyDevice>(
- next_gipa(VK_NULL_HANDLE, "vkDestroyDevice"));
+ next_gipa(instance.handle, "vkDestroyDevice"));
ALOG_ASSERT(destroy_device != nullptr,
"Loader unable to find DestroyDevice");
destroy_device(local_device, allocator);
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index 77c8ebe..cec0ff6 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -153,7 +153,7 @@
LayerRef(const LayerRef&) = delete;
LayerRef& operator=(const LayerRef&) = delete;
- const char* GetName();
+ const char* GetName() const;
uint32_t GetSpecVersion();
// provides bool-like behavior