Accept EAGAIN on a binder transaction.
am: 380e74f214 -s ours
Change-Id: I21c67ad6a8f43a48983cd8c48001816f94b656b6
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index b3cbdef..b850390 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -3,6 +3,10 @@
cc_binary {
name: "atrace",
srcs: ["atrace.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbinder",
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 9cdc9e9..9dbbb77 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -226,8 +226,11 @@
static const char* k_traceBufferSizePath =
"buffer_size_kb";
+#if 0
+// TODO: Re-enable after stabilization
static const char* k_traceCmdlineSizePath =
"saved_cmdlines_size";
+#endif
static const char* k_tracingOverwriteEnablePath =
"options/overwrite";
@@ -250,9 +253,6 @@
static const char* k_funcgraphFlatPath =
"options/funcgraph-flat";
-static const char* k_funcgraphDurationPath =
- "options/funcgraph-duration";
-
static const char* k_ftraceFilterPath =
"set_ftrace_filter";
@@ -445,7 +445,6 @@
static bool setTraceBufferSizeKB(int size)
{
char str[32] = "1";
- int len;
if (size < 1) {
size = 1;
}
@@ -453,6 +452,8 @@
return writeStr(k_traceBufferSizePath, str);
}
+#if 0
+// TODO: Re-enable after stabilization
// Set the default size of cmdline hashtable
static bool setCmdlineSize()
{
@@ -461,6 +462,7 @@
}
return true;
}
+#endif
// Set the clock to the best available option while tracing. Use 'boot' if it's
// available; otherwise, use 'mono'. If neither are available use 'global'.
@@ -481,8 +483,8 @@
newClock = "global";
}
- size_t begin = clockStr.find("[") + 1;
- size_t end = clockStr.find("]");
+ size_t begin = clockStr.find('[') + 1;
+ size_t end = clockStr.find(']');
if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) {
return true;
}
@@ -543,7 +545,7 @@
auto listRet = sm->list([&](const auto &interfaces) {
for (size_t i = 0; i < interfaces.size(); i++) {
string fqInstanceName = interfaces[i];
- string::size_type n = fqInstanceName.find("/");
+ string::size_type n = fqInstanceName.find('/');
if (n == std::string::npos || interfaces[i].size() == n+1)
continue;
hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
diff --git a/cmds/bugreport/Android.bp b/cmds/bugreport/Android.bp
index 139e4b2..24044a6 100644
--- a/cmds/bugreport/Android.bp
+++ b/cmds/bugreport/Android.bp
@@ -1,6 +1,6 @@
cc_binary {
name: "bugreport",
srcs: ["bugreport.cpp"],
- cflags: ["-Wall"],
+ cflags: ["-Wall", "-Werror"],
shared_libs: ["libcutils"],
}
diff --git a/cmds/cmd/Android.mk b/cmds/cmd/Android.mk
index d565e57..4868555 100644
--- a/cmds/cmd/Android.mk
+++ b/cmds/cmd/Android.mk
@@ -10,6 +10,8 @@
libselinux \
libbinder
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 014c995..022d3dd 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -78,7 +78,7 @@
if (is_selinux_enabled() && seLinuxContext.size() > 0) {
String8 seLinuxContext8(seLinuxContext);
security_context_t tmp = NULL;
- int ret = getfilecon(fullPath.string(), &tmp);
+ getfilecon(fullPath.string(), &tmp);
Unique_SecurityContext context(tmp);
int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(),
"file", "write", NULL);
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index f908cbf..ce3a6aa 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -24,20 +24,6 @@
],
}
-cc_library_headers {
- name: "dumpstate_headers",
- vendor_available: true,
- export_include_dirs: ["."],
- header_libs: [
- "libbase_headers",
- "libutils_headers",
- ],
- export_header_lib_headers: [
- "libbase_headers",
- "libutils_headers",
- ],
-}
-
cc_library_shared {
name: "libdumpstateutil",
defaults: ["dumpstate_defaults"],
@@ -45,8 +31,6 @@
vndk: {
enabled: true,
},
- header_libs: ["dumpstate_headers"],
- export_header_lib_headers: ["dumpstate_headers"],
srcs: [
"DumpstateInternal.cpp",
"DumpstateUtil.cpp",
@@ -55,6 +39,10 @@
"libbase",
"liblog",
],
+ export_include_dirs: ["."],
+ export_shared_lib_headers: [
+ "libbase",
+ ],
}
cc_library_shared {
@@ -78,7 +66,6 @@
cc_binary {
name: "dumpstate",
defaults: ["dumpstate_defaults"],
- header_libs: ["dumpstate_headers"],
shared_libs: [
"android.hardware.dumpstate@1.0",
"libziparchive",
@@ -106,7 +93,6 @@
cc_test {
name: "dumpstate_test",
defaults: ["dumpstate_defaults"],
- header_libs: ["dumpstate_headers"],
shared_libs: [
"libziparchive",
"libbase",
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index f0b6203..83e30a2 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -18,6 +18,7 @@
#include "DumpstateInternal.h"
+#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdint.h>
@@ -175,7 +176,6 @@
}
}
}
- close(fd);
if (!newline) dprintf(out_fd, "\n");
if (!title.empty()) dprintf(out_fd, "\n");
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index e866b8b..ede4254 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -30,6 +30,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <log/log.h>
#include "DumpstateInternal.h"
@@ -103,6 +104,12 @@
return *this;
}
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() {
+ if (!PropertiesHelper::IsUserBuild())
+ values.account_mode_ = SU_ROOT;
+ return *this;
+}
+
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
values.account_mode_ = DROP_ROOT;
return *this;
@@ -176,8 +183,8 @@
}
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+ if (fd.get() < 0) {
int err = errno;
if (title.empty()) {
dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
@@ -188,7 +195,7 @@
fsync(out_fd);
return -1;
}
- return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
+ return DumpFileFromFdToFd(title, path, fd.get(), out_fd, PropertiesHelper::IsDryRun());
}
int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index 5a8ce5b..698ceff 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -89,6 +89,8 @@
CommandOptionsBuilder& Always();
/* Sets the command's PrivilegeMode as `SU_ROOT` */
CommandOptionsBuilder& AsRoot();
+ /* If !IsUserBuild(), sets the command's PrivilegeMode as `SU_ROOT` */
+ CommandOptionsBuilder& AsRootIfAvailable();
/* Sets the command's PrivilegeMode as `DROP_ROOT` */
CommandOptionsBuilder& DropRoot();
/* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5f93e99..51942dd 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -207,20 +207,36 @@
const std::string& name = it->name;
const int fd = it->fd;
dumped = true;
+
+ // Seek to the beginning of the file before dumping any data. A given
+ // DumpData entry might be dumped multiple times in the report.
+ //
+ // For example, the most recent ANR entry is dumped to the body of the
+ // main entry and it also shows up as a separate entry in the bugreport
+ // ZIP file.
+ if (lseek(fd, 0, SEEK_SET) != static_cast<off_t>(0)) {
+ MYLOGE("Unable to add %s to zip file, lseek failed: %s\n", name.c_str(),
+ strerror(errno));
+ }
+
if (ds.IsZipping() && add_to_zip) {
if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
- MYLOGE("Unable to add %s %s to zip file\n", name.c_str(), type_name);
+ MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str());
}
} else {
dump_file_from_fd(type_name, name.c_str(), fd);
}
-
- close(fd);
}
return dumped;
}
+static void CloseDumpFds(const std::vector<DumpData>* dumps) {
+ for (auto it = dumps->begin(); it != dumps->end(); ++it) {
+ close(it->fd);
+ }
+}
+
// for_each_pid() callback to get mount info about a process.
void do_mountinfo(int pid, const char* name __attribute__((unused))) {
char path[PATH_MAX];
@@ -674,6 +690,7 @@
printf("Kernel: ");
DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+ ds.RunCommand("UPTIME", {"uptime"}, CommandOptions::DEFAULT);
printf("Bugreport format version: %s\n", version_.c_str());
printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
@@ -697,7 +714,7 @@
std::string valid_name = entry_name;
// Rename extension if necessary.
- size_t idx = entry_name.rfind(".");
+ size_t idx = entry_name.rfind('.');
if (idx != std::string::npos) {
std::string extension = entry_name.substr(idx);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
@@ -887,9 +904,9 @@
MYLOGD("AddGlobalAnrTraceFile(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
- int fd = TEMP_FAILURE_RETRY(
- open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
- if (fd < 0) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)));
+ if (fd.get() < 0) {
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_file.c_str(), strerror(errno));
} else {
if (add_to_zip) {
@@ -901,7 +918,7 @@
} else {
MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
anr_traces_file.c_str());
- dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd);
+ dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd.get());
}
}
}
@@ -934,12 +951,12 @@
AddDumps(anr_data->begin(), anr_data->begin() + 1,
"VM TRACES AT LAST ANR", add_to_zip);
- if (anr_data->size() > 1) {
- // NOTE: Historical ANRs are always added as separate entries in the
- // bugreport zip file.
- AddDumps(anr_data->begin() + 1, anr_data->end(),
- "HISTORICAL ANR", true /* add_to_zip */);
- }
+ // The "last" ANR will always be included as separate entry in the zip file. In addition,
+ // it will be present in the body of the main entry if |add_to_zip| == false.
+ //
+ // Historical ANRs are always included as separate entries in the bugreport zip file.
+ AddDumps(anr_data->begin() + ((add_to_zip) ? 1 : 0), anr_data->end(),
+ "HISTORICAL ANR", true /* add_to_zip */);
} else {
printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
}
@@ -1042,6 +1059,7 @@
DurationReporter duration_reporter("DUMPSTATE");
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
+ /* TODO: Remove duplicate uptime call when tools use it from header */
RunCommand("UPTIME", {"uptime"});
DumpBlockStatFiles();
dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
@@ -1069,14 +1087,15 @@
RunCommand(
"HARDWARE HALS",
{"lshal", std::string("--debug=") + kLsHalDebugPath},
- CommandOptions::AS_ROOT);
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath);
unlink(kLsHalDebugPath.c_str());
} else {
RunCommand(
- "HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT);
+ "HARDWARE HALS", {"lshal", "--debug"},
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
}
RunCommand("PRINTENV", {"printenv"});
@@ -1141,19 +1160,6 @@
RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
- printf("------ BACKLIGHTS ------\n");
- printf("LCD brightness=");
- DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
- printf("Button brightness=");
- DumpFile("", "/sys/class/leds/button-backlight/brightness");
- printf("Keyboard brightness=");
- DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
- printf("ALS mode=");
- DumpFile("", "/sys/class/leds/lcd-backlight/als");
- printf("LCD driver registers:\n");
- DumpFile("", "/sys/class/leds/lcd-backlight/registers");
- printf("\n");
-
/* Binder state is expensive to look at as it uses a lot of memory. */
DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
@@ -1438,7 +1444,7 @@
return true;
}
-static std::string SHA256_file_hash(std::string filepath) {
+static std::string SHA256_file_hash(const std::string& filepath) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
| O_CLOEXEC | O_NOFOLLOW)));
if (fd == -1) {
@@ -1998,5 +2004,8 @@
close(ds.control_socket_fd_);
}
+ CloseDumpFds(tombstone_data.get());
+ CloseDumpFds(anr_data.get());
+
return 0;
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a94cf99..92b0c0d 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -477,6 +477,48 @@
EXPECT_THAT(err, StrEq("stderr\n"));
}
+TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n")
+ return;
+ }
+ if (!PropertiesHelper::IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n")
+ return;
+ }
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
EXPECT_THAT(out,
@@ -1053,6 +1095,51 @@
EXPECT_THAT(err, StrEq("stderr\n"));
}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnUserBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandAsRootIfAvailableOnUserBuild.txt");
+ if (!PropertiesHelper::IsUserBuild()) {
+ // Emulates user build if necessarily.
+ SetBuildType("user");
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+ EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnDebugBuild) {
+ if (!IsStandalone()) {
+ // TODO: temporarily disabled because it might cause other tests to fail after dropping
+ // to Shell - need to refactor tests to avoid this problem)
+ MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n")
+ return;
+ }
+ CreateFd("RunCommandAsRootIfAvailableOnDebugBuild.txt");
+ if (PropertiesHelper::IsUserBuild()) {
+ ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+ return;
+ }
+
+ DropRoot();
+
+ EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+ CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+ EXPECT_THAT(out, StrEq("0\nstdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
TEST_F(DumpstateUtilTest, RunCommandDropRoot) {
if (!IsStandalone()) {
// TODO: temporarily disabled because it might cause other tests to fail after dropping
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 93f4c22..a96a69d 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -628,7 +628,7 @@
struct dirent *d;
char *newpath = NULL;
const char *slash = "/";
- int fd, retval = 0;
+ int retval = 0;
if (!title.empty()) {
printf("------ %s (%s) ------\n", title.c_str(), dir);
@@ -670,13 +670,13 @@
}
continue;
}
- fd = TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
- retval = fd;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+ if (fd.get() < 0) {
+ retval = -1;
printf("*** %s: %s\n", newpath, strerror(errno));
continue;
}
- (*dump_from_fd)(NULL, newpath, fd);
+ (*dump_from_fd)(NULL, newpath, fd.get());
}
closedir(dirp);
if (!title.empty()) {
@@ -695,11 +695,9 @@
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
- close(fd);
return -1;
} else if (!(flags & O_NONBLOCK)) {
printf("*** %s: fd must have O_NONBLOCK set.\n", path);
- close(fd);
return -1;
}
return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index fa6f6df..3227749 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -177,7 +177,7 @@
}
for (size_t i = 0; i < N; i++) {
- String16 service_name = std::move(services[i]);
+ const String16& service_name = std::move(services[i]);
if (IsSkipped(skippedServices, service_name)) continue;
sp<IBinder> service = sm_->checkService(service_name);
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
index 127e0f3..39fcb80 100644
--- a/cmds/dumpsys/tests/Android.bp
+++ b/cmds/dumpsys/tests/Android.bp
@@ -4,6 +4,7 @@
test_suites: ["device-tests"],
srcs: ["dumpsys_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
shared_libs: [
"libbase",
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index cfd53e5..16fefe6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -96,7 +96,7 @@
}
int i = 0;
std::ostringstream actual_stream, expected_stream;
- for (String16 actual : arg) {
+ for (const String16& actual : arg) {
std::string actual_str = String8(actual).c_str();
std::string expected_str = expected[i];
actual_stream << "'" << actual_str << "' ";
diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk
index c295167..7aa111c 100644
--- a/cmds/flatland/Android.mk
+++ b/cmds/flatland/Android.mk
@@ -8,6 +8,8 @@
Renderers.cpp \
Main.cpp \
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MODULE:= flatland
LOCAL_MODULE_TAGS := tests
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index ec1e543..3d7cac0 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -284,7 +284,6 @@
public:
Layer() :
- mFirstFrame(true),
mGLHelper(NULL),
mSurface(EGL_NO_SURFACE) {
}
@@ -358,8 +357,6 @@
}
private:
- bool mFirstFrame;
-
LayerDesc mDesc;
GLHelper* mGLHelper;
@@ -389,7 +386,6 @@
ATRACE_CALL();
bool result;
- EGLint resulte;
float scaleFactor = float(mDesc.runHeights[mInstance]) /
float(mDesc.height);
@@ -465,7 +461,6 @@
ATRACE_CALL();
bool result;
- status_t err;
resetColorGenerator();
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 33db6db..7d1537a 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -4,6 +4,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wextra",
],
srcs: [
"CacheItem.cpp",
@@ -12,7 +13,7 @@
"dexopt.cpp",
"globals.cpp",
"utils.cpp",
- "binder/android/os/IInstalld.aidl",
+ ":installd_aidl",
],
shared_libs: [
"libbase",
@@ -25,6 +26,17 @@
],
clang: true,
+
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "clang-analyzer-security*",
+ "cert-*",
+ "-cert-err58-cpp",
+ ],
+ tidy_flags: [
+ "-warnings-as-errors=clang-analyzer-security*,cert-*"
+ ],
}
//
@@ -72,4 +84,11 @@
],
}
+filegroup {
+ name: "installd_aidl",
+ srcs: [
+ "binder/android/os/IInstalld.aidl",
+ ],
+}
+
subdirs = ["tests"]
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index 3eb39b9..ea0cd9e 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -60,7 +60,7 @@
ATRACE_BEGIN("loadStats tree");
cacheUsed = 0;
- for (auto path : mDataPaths) {
+ for (const auto& path : mDataPaths) {
auto cachePath = read_path_inode(path, "cache", kXattrInodeCache);
auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
calculate_tree_size(cachePath, &cacheUsed);
@@ -170,7 +170,7 @@
items.clear();
ATRACE_BEGIN("loadItems");
- for (auto path : mDataPaths) {
+ for (const auto& path : mDataPaths) {
loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache));
loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache));
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 8a79ee1..6877fb7 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -342,6 +342,56 @@
#endif
}
+static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
+ if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
+ return true;
+ }
+
+ int32_t uid = multiuser_get_uid(userId, appId);
+ int shared_app_gid = multiuser_get_shared_gid(userId, appId);
+ if (shared_app_gid == -1) {
+ // TODO(calin): this should no longer be possible but do not continue if we don't get
+ // a valid shared gid.
+ PLOG(WARNING) << "Invalid shared_app_gid for " << packageName;
+ return true;
+ }
+
+ const std::string profile_dir =
+ create_primary_current_profile_package_dir_path(userId, packageName);
+ // read-write-execute only for the app user.
+ if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << profile_dir;
+ return false;
+ }
+ const std::string profile_file = create_current_profile_path(userId, packageName,
+ /*is_secondary_dex*/false);
+ // read-write only for the app user.
+ if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << profile_file;
+ return false;
+ }
+
+ const std::string ref_profile_path =
+ create_primary_reference_profile_package_dir_path(packageName);
+
+ // Prepare the reference profile directory. Note that we use the non strict version of
+ // fs_prepare_dir. This will fix the permission and the ownership to the correct values.
+ // This is particularly important given that in O there were some fixes for how the
+ // shared_app_gid is computed.
+ //
+ // Note that by the time we get here we know that we are using a correct uid (otherwise
+ // prepare_app_dir and the above fs_prepare_file_strict which check the uid). So we
+ // are sure that the gid being used belongs to the owning app and not someone else.
+ //
+ // dex2oat/profman runs under the shared app gid and it needs to read/write reference profiles.
+ if (fs_prepare_dir(ref_profile_path.c_str(), 0770, AID_SYSTEM, shared_app_gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
+ return false;
+ }
+
+ return true;
+}
+
binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
@@ -417,28 +467,8 @@
return error("Failed to set hard quota " + path);
}
- if (property_get_bool("dalvik.vm.usejitprofiles", false)) {
- const std::string profile_dir =
- create_primary_current_profile_package_dir_path(userId, pkgname);
- // read-write-execute only for the app user.
- if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) {
- return error("Failed to prepare " + profile_dir);
- }
- const std::string profile_file = create_current_profile_path(userId, pkgname,
- /*is_secondary_dex*/false);
- // read-write only for the app user.
- if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
- return error("Failed to prepare " + profile_file);
- }
- const std::string ref_profile_path =
- create_primary_reference_profile_package_dir_path(pkgname);
- // dex2oat/profman runs under the shared app gid and it needs to read/write reference
- // profiles.
- int shared_app_gid = multiuser_get_shared_gid(0, appId);
- if ((shared_app_gid != -1) && fs_prepare_dir_strict(
- ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
- return error("Failed to prepare " + ref_profile_path);
- }
+ if (!prepare_app_profile_dir(packageName, appId, userId)) {
+ return error("Failed to prepare profiles for " + packageName);
}
}
return ok();
@@ -525,6 +555,9 @@
if (access(path.c_str(), F_OK) == 0) {
if (delete_dir_contents(path) != 0) {
res = error("Failed to delete contents of " + path);
+ } else if ((flags & (FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE_ONLY)) == 0) {
+ remove_path_xattr(path, kXattrInodeCache);
+ remove_path_xattr(path, kXattrInodeCodeCache);
}
}
}
@@ -711,6 +744,9 @@
// Ignore all other GID transitions, since they're kinda shady
LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected GID " << actual
<< " instead of " << expected;
+ if (!(flags & FLAG_FORCE)) {
+ fts_set(fts, p, FTS_SKIP);
+ }
}
}
}
@@ -1349,7 +1385,7 @@
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- for (auto packageName : packageNames) {
+ for (const auto& packageName : packageNames) {
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
}
// NOTE: Locking is relaxed on this method, since it's limited to
@@ -1388,7 +1424,7 @@
}
ATRACE_BEGIN("obb");
- for (auto packageName : packageNames) {
+ for (const auto& packageName : packageNames) {
auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
calculate_tree_size(obbCodePath, &extStats.codeSize);
}
@@ -1396,7 +1432,7 @@
if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
ATRACE_BEGIN("code");
- for (auto codePath : codePaths) {
+ for (const auto& codePath : codePaths) {
calculate_tree_size(codePath, &stats.codeSize, -1,
multiuser_get_shared_gid(0, appId));
}
@@ -1407,7 +1443,7 @@
ATRACE_END();
} else {
ATRACE_BEGIN("code");
- for (auto codePath : codePaths) {
+ for (const auto& codePath : codePaths) {
calculate_tree_size(codePath, &stats.codeSize);
}
ATRACE_END();
@@ -1827,6 +1863,29 @@
return ok();
}
+binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId,
+ const std::string& packageName, const std::string& codePath, bool* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ *_aidl_return = create_profile_snapshot(appId, packageName, codePath);
+ return ok();
+}
+
+binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& packageName,
+ const std::string& codePath) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ std::string snapshot = create_snapshot_profile_path(packageName, codePath);
+ if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) {
+ return error("Failed to destroy profile snapshot for " + packageName + ":" + codePath);
+ }
+ return ok();
+}
+
binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
@@ -1863,7 +1922,7 @@
char boot_marker_path[PKG_PATH_MAX];
sprintf(boot_marker_path,
"%s/%s/%s/.booting",
- android_data_dir.path,
+ android_data_dir.c_str(),
DALVIK_CACHE,
instruction_set);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index c8db3df..2d22934 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -96,6 +96,11 @@
binder::Status clearAppProfiles(const std::string& packageName);
binder::Status destroyAppProfiles(const std::string& packageName);
+ binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName,
+ const std::string& codePath, bool* _aidl_return);
+ binder::Status destroyProfileSnapshot(const std::string& packageName,
+ const std::string& codePath);
+
binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
int32_t uid);
binder::Status removeIdmap(const std::string& overlayApkPath);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 452a2b1..dbd89f5 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -62,6 +62,10 @@
void clearAppProfiles(@utf8InCpp String packageName);
void destroyAppProfiles(@utf8InCpp String packageName);
+ boolean createProfileSnapshot(int appId, @utf8InCpp String packageName,
+ @utf8InCpp String codePath);
+ void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String codePath);
+
void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
void removeIdmap(@utf8InCpp String overlayApkPath);
void rmPackageDir(@utf8InCpp String packageDir);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f29da17..29d5c32 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -40,6 +41,7 @@
#include <system/thread_defs.h>
#include "dexopt.h"
+#include "globals.h"
#include "installd_deps.h"
#include "otapreopt_utils.h"
#include "utils.h"
@@ -51,6 +53,15 @@
namespace android {
namespace installd {
+// Should minidebug info be included in compiled artifacts? Even if this value is
+// "true," usage might still be conditional to other constraints, e.g., system
+// property overrides.
+static constexpr bool kEnableMinidebugInfo = true;
+
+static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo";
+static constexpr bool kMinidebugInfoSystemPropertyDefault = false;
+static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info";
+
// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
struct FreeDelete {
// NOTE: Deleting a const object is valid but free() takes a non-const pointer.
@@ -67,6 +78,14 @@
return unique_fd(-1);
}
+static bool is_debug_runtime() {
+ return android::base::GetProperty("persist.sys.dalvik.vm.lib.2", "") == "libartd.so";
+}
+
+static bool is_debuggable_build() {
+ return android::base::GetBoolProperty("ro.debuggable", false);
+}
+
static bool clear_profile(const std::string& profile) {
unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
if (ufd.get() < 0) {
@@ -151,7 +170,7 @@
int count = 0;
char buf[kPropertyValueMax];
- strncpy(buf, str, sizeof(buf));
+ strlcpy(buf, str, sizeof(buf));
char *pBuf = buf;
while(strtok_r(pBuf, " ", &ctx) != NULL) {
@@ -191,7 +210,8 @@
static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,
const char* input_file_name, const char* output_file_name, int swap_fd,
const char* instruction_set, const char* compiler_filter,
- bool debuggable, bool post_bootcomplete, int profile_fd, const char* class_loader_context) {
+ bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd,
+ const char* class_loader_context) {
static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
@@ -267,7 +287,17 @@
dex2oat_large_app_threshold);
}
- static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
+ // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
+ const char* dex2oat_bin = "/system/bin/dex2oat";
+ static const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
+ if (is_debug_runtime() || (background_job_compile && is_debuggable_build())) {
+ DCHECK(access(kDex2oatDebugPath, X_OK) == 0);
+ dex2oat_bin = kDex2oatDebugPath;
+ }
+
+ bool generate_minidebug_info = kEnableMinidebugInfo &&
+ android::base::GetBoolProperty(kMinidebugInfoSystemProperty,
+ kMinidebugInfoSystemPropertyDefault);
static const char* RUNTIME_ARG = "--runtime-arg";
@@ -327,7 +357,8 @@
bool have_dex2oat_compiler_filter_flag = false;
if (skip_compilation) {
- strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract");
+ strlcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract",
+ sizeof(dex2oat_compiler_filter_arg));
have_dex2oat_compiler_filter_flag = true;
have_dex2oat_relocation_skip_flag = true;
} else if (compiler_filter != nullptr) {
@@ -376,7 +407,7 @@
}
- ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name);
+ ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
const char* argv[9 // program name, mandatory arguments and the final NULL
+ (have_dex2oat_isa_variant ? 1 : 0)
@@ -395,9 +426,10 @@
+ (profile_fd == -1 ? 0 : 1)
+ (class_loader_context != nullptr ? 1 : 0)
+ (has_base_dir ? 1 : 0)
- + (have_dex2oat_large_app_threshold ? 1 : 0)];
+ + (have_dex2oat_large_app_threshold ? 1 : 0)
+ + (generate_minidebug_info ? 1 : 0)];
int i = 0;
- argv[i++] = DEX2OAT_BIN;
+ argv[i++] = dex2oat_bin;
argv[i++] = zip_fd_arg;
argv[i++] = zip_location_arg;
argv[i++] = input_vdex_fd_arg;
@@ -459,12 +491,15 @@
if (class_loader_context != nullptr) {
argv[i++] = class_loader_context_arg;
}
+ if (generate_minidebug_info) {
+ argv[i++] = kMinidebugDex2oatFlag;
+ }
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
- execv(DEX2OAT_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
+ execv(dex2oat_bin, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", dex2oat_bin, strerror(errno));
}
/*
@@ -526,14 +561,12 @@
}
}
-static bool create_profile(int uid, const std::string& profile) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600)));
+static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600)));
if (fd.get() < 0) {
- if (errno == EEXIST) {
- return true;
- } else {
+ if (errno != EEXIST) {
PLOG(ERROR) << "Failed to create profile " << profile;
- return false;
+ return invalid_unique_fd();
}
}
// Profiles should belong to the app; make sure of that by giving ownership to
@@ -541,27 +574,26 @@
// since dex2oat/profman will fail with SElinux denials.
if (fchown(fd.get(), uid, uid) < 0) {
PLOG(ERROR) << "Could not chwon profile " << profile;
- return false;
+ return invalid_unique_fd();
}
- return true;
+ return fd;
}
-static unique_fd open_profile(int uid, const std::string& profile, bool read_write) {
- // Check if we need to open the profile for a read-write operation. If so, we
- // might need to create the profile since the file might not be there. Reference
- // profiles are created on the fly so they might not exist beforehand.
- if (read_write) {
- if (!create_profile(uid, profile)) {
- return invalid_unique_fd();
- }
- }
- int flags = read_write ? O_RDWR : O_RDONLY;
+static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) {
// Do not follow symlinks when opening a profile:
// - primary profiles should not contain symlinks in their paths
// - secondary dex paths should have been already resolved and validated
flags |= O_NOFOLLOW;
- unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+ // Check if we need to create the profile
+ // Reference profiles and snapshots are created on the fly; so they might not exist beforehand.
+ unique_fd fd;
+ if ((flags & O_CREAT) != 0) {
+ fd = create_profile(uid, profile, flags);
+ } else {
+ fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
+ }
+
if (fd.get() < 0) {
if (errno != ENOENT) {
// Profiles might be missing for various reasons. For example, in a
@@ -581,13 +613,19 @@
static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location,
bool is_secondary_dex) {
std::string profile = create_current_profile_path(user, location, is_secondary_dex);
- return open_profile(uid, profile, /*read_write*/false);
+ return open_profile(uid, profile, O_RDONLY);
}
static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write,
bool is_secondary_dex) {
std::string profile = create_reference_profile_path(location, is_secondary_dex);
- return open_profile(uid, profile, read_write);
+ return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+}
+
+static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
+ const std::string& code_path) {
+ std::string profile = create_snapshot_profile_path(package_name, code_path);
+ return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);
}
static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex,
@@ -643,7 +681,7 @@
static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
const unique_fd& reference_profile_fd) {
static const size_t MAX_INT_LEN = 32;
- static const char* PROFMAN_BIN = "/system/bin/profman";
+ const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
std::vector<std::string> profile_args(profiles_fd.size());
char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
@@ -657,7 +695,7 @@
// program name, reference profile fd, the final NULL and the profile fds
const char* argv[3 + profiles_fd.size()];
int i = 0;
- argv[i++] = PROFMAN_BIN;
+ argv[i++] = profman_bin;
argv[i++] = reference_profile_arg;
for (size_t k = 0; k < profile_args.size(); k++) {
argv[i++] = profile_args[k].c_str();
@@ -665,8 +703,8 @@
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
- execv(PROFMAN_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+ execv(profman_bin, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", profman_bin, strerror(errno));
exit(68); /* only get here on exec failure */
}
@@ -920,7 +958,7 @@
if (EndsWith(oat_path, ".dex")) {
std::string new_path = oat_path;
new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext);
- CHECK(EndsWith(new_path, new_ext.c_str()));
+ CHECK(EndsWith(new_path, new_ext));
return new_path;
}
@@ -949,14 +987,6 @@
return replace_file_extension(oat_path, ".vdex");
}
-static bool add_extension_to_file_name(char* file_name, const char* extension) {
- if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
- return false;
- }
- strcat(file_name, extension);
- return true;
-}
-
static int open_output_file(const char* file_name, bool recreate, int permissions) {
int flags = O_RDWR | O_CREAT;
if (recreate) {
@@ -1192,21 +1222,16 @@
if (!ShouldUseSwapFileForDexopt()) {
return invalid_unique_fd();
}
- // Make sure there really is enough space.
- char swap_file_name[PKG_PATH_MAX];
- strcpy(swap_file_name, out_oat_path);
- if (!add_extension_to_file_name(swap_file_name, ".swap")) {
- return invalid_unique_fd();
- }
+ auto swap_file_name = std::string(out_oat_path) + ".swap";
unique_fd swap_fd(open_output_file(
- swap_file_name, /*recreate*/true, /*permissions*/0600));
+ swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600));
if (swap_fd.get() < 0) {
// Could not create swap file. Optimistically go on and hope that we can compile
// without it.
- ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
+ ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name.c_str());
} else {
// Immediately unlink. We don't really want to hit flash.
- if (unlink(swap_file_name) < 0) {
+ if (unlink(swap_file_name.c_str()) < 0) {
PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
}
}
@@ -1235,7 +1260,7 @@
// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
-bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed,
+bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
Dex2oatFileWrapper* out_vdex_wrapper_fd) {
@@ -1348,6 +1373,39 @@
return wrapper_fd;
}
+// Creates RDONLY fds for oat and vdex files, if exist.
+// Returns false if it fails to create oat out path for the given apk path.
+// Note that the method returns true even if the files could not be opened.
+bool maybe_open_oat_and_vdex_file(const std::string& apk_path,
+ const std::string& oat_dir,
+ const std::string& instruction_set,
+ bool is_secondary_dex,
+ unique_fd* oat_file_fd,
+ unique_fd* vdex_file_fd) {
+ char oat_path[PKG_PATH_MAX];
+ if (!create_oat_out_path(apk_path.c_str(),
+ instruction_set.c_str(),
+ oat_dir.c_str(),
+ is_secondary_dex,
+ oat_path)) {
+ LOG(ERROR) << "Could not create oat out path for "
+ << apk_path << " with oat dir " << oat_dir;
+ return false;
+ }
+ oat_file_fd->reset(open(oat_path, O_RDONLY));
+ if (oat_file_fd->get() < 0) {
+ PLOG(INFO) << "installd cannot open oat file during dexopt" << oat_path;
+ }
+
+ std::string vdex_filename = create_vdex_filename(oat_path);
+ vdex_file_fd->reset(open(vdex_filename.c_str(), O_RDONLY));
+ if (vdex_file_fd->get() < 0) {
+ PLOG(INFO) << "installd cannot open vdex file during dexopt" << vdex_filename;
+ }
+
+ return true;
+}
+
// Updates the access times of out_oat_path based on those from apk_path.
void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) {
struct stat input_stat;
@@ -1369,9 +1427,15 @@
// The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter.
// If this is for a profile guided compilation, profile_was_updated will tell whether or not
// the profile has changed.
-static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& instruction_set,
- const std::string& compiler_filter, bool profile_was_updated, bool downgrade) {
- static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+static void exec_dexoptanalyzer(const std::string& dex_file, int vdex_fd, int oat_fd,
+ int zip_fd, const std::string& instruction_set, const std::string& compiler_filter,
+ bool profile_was_updated, bool downgrade,
+ const char* class_loader_context) {
+ CHECK_GE(zip_fd, 0);
+ const char* dexoptanalyzer_bin =
+ is_debug_runtime()
+ ? "/system/bin/dexoptanalyzerd"
+ : "/system/bin/dexoptanalyzer";
static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
if (instruction_set.size() >= MAX_INSTRUCTION_SET_LEN) {
@@ -1381,36 +1445,56 @@
}
std::string dex_file_arg = "--dex-file=" + dex_file;
+ std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
+ std::string vdex_fd_arg = "--vdex-fd=" + std::to_string(vdex_fd);
+ std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd);
std::string isa_arg = "--isa=" + instruction_set;
std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter;
const char* assume_profile_changed = "--assume-profile-changed";
const char* downgrade_flag = "--downgrade";
+ std::string class_loader_context_arg = "--class-loader-context=";
+ if (class_loader_context != nullptr) {
+ class_loader_context_arg += class_loader_context;
+ }
// program name, dex file, isa, filter, the final NULL
- const int argc = 5 +
+ const int argc = 6 +
(profile_was_updated ? 1 : 0) +
- (downgrade ? 1 : 0);
+ (vdex_fd >= 0 ? 1 : 0) +
+ (oat_fd >= 0 ? 1 : 0) +
+ (downgrade ? 1 : 0) +
+ (class_loader_context != nullptr ? 1 : 0);
const char* argv[argc];
int i = 0;
- argv[i++] = DEXOPTANALYZER_BIN;
+ argv[i++] = dexoptanalyzer_bin;
argv[i++] = dex_file_arg.c_str();
argv[i++] = isa_arg.c_str();
argv[i++] = compiler_filter_arg.c_str();
+ if (oat_fd >= 0) {
+ argv[i++] = oat_fd_arg.c_str();
+ }
+ if (vdex_fd >= 0) {
+ argv[i++] = vdex_fd_arg.c_str();
+ }
+ argv[i++] = zip_fd_arg.c_str();
if (profile_was_updated) {
argv[i++] = assume_profile_changed;
}
if (downgrade) {
argv[i++] = downgrade_flag;
}
+ if (class_loader_context != nullptr) {
+ argv[i++] = class_loader_context_arg.c_str();
+ }
argv[i] = NULL;
- execv(DEXOPTANALYZER_BIN, (char * const *)argv);
- ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+ execv(dexoptanalyzer_bin, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", dexoptanalyzer_bin, strerror(errno));
}
// Prepares the oat dir for the secondary dex files.
static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
- const char* instruction_set, std::string* oat_dir_out) {
+ const char* instruction_set) {
unsigned long dirIndex = dex_path.rfind('/');
if (dirIndex == std::string::npos) {
LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path;
@@ -1427,10 +1511,8 @@
char oat_dir[PKG_PATH_MAX];
snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str());
- oat_dir_out->assign(oat_dir);
- // Create oat/isa output directory.
- if (prepare_app_cache_dir(*oat_dir_out, instruction_set, oat_dir_mode, uid, uid) != 0) {
+ if (prepare_app_cache_dir(oat_dir, instruction_set, oat_dir_mode, uid, uid) != 0) {
LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path;
return false;
}
@@ -1438,39 +1520,128 @@
return true;
}
-static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+// Return codes for identifying the reason why dexoptanalyzer was not invoked when processing
+// secondary dex files. This return codes are returned by the child process created for
+// analyzing secondary dex files in process_secondary_dex_dexopt.
-// Verifies the result of dexoptanalyzer executed for the apk_path.
+// The dexoptanalyzer was not invoked because of validation or IO errors.
+static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED = 200;
+// The dexoptanalyzer was not invoked because the dex file does not exist anymore.
+static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE = 201;
+
+// Verifies the result of analyzing secondary dex files from process_secondary_dex_dexopt.
// If the result is valid returns true and sets dexopt_needed_out to a valid value.
// Returns false for errors or unexpected result values.
-static bool process_dexoptanalyzer_result(const std::string& dex_path, int result,
+// The result is expected to be either one of SECONDARY_DEX_* codes or a valid exit code
+// of dexoptanalyzer.
+static bool process_secondary_dexoptanalyzer_result(const std::string& dex_path, int result,
int* dexopt_needed_out) {
// The result values are defined in dexoptanalyzer.
switch (result) {
- case 0: // no_dexopt_needed
+ case 0: // dexoptanalyzer: no_dexopt_needed
*dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
- case 1: // dex2oat_from_scratch
+ case 1: // dexoptanalyzer: dex2oat_from_scratch
*dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
- case 5: // dex2oat_for_bootimage_odex
+ case 5: // dexoptanalyzer: dex2oat_for_bootimage_odex
*dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
- case 6: // dex2oat_for_filter_odex
+ case 6: // dexoptanalyzer: dex2oat_for_filter_odex
*dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
- case 7: // dex2oat_for_relocation_odex
+ case 7: // dexoptanalyzer: dex2oat_for_relocation_odex
*dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
- case 2: // dex2oat_for_bootimage_oat
- case 3: // dex2oat_for_filter_oat
- case 4: // dex2oat_for_relocation_oat
+ case 2: // dexoptanalyzer: dex2oat_for_bootimage_oat
+ case 3: // dexoptanalyzer: dex2oat_for_filter_oat
+ case 4: // dexoptanalyzer: dex2oat_for_relocation_oat
LOG(ERROR) << "Dexoptnalyzer return the status of an oat file."
<< " Expected odex file status for secondary dex " << dex_path
<< " : dexoptanalyzer result=" << result;
return false;
+ case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE:
+ // If the file does not exist there's no need for dexopt.
+ *dexopt_needed_out = NO_DEXOPT_NEEDED;
+ return true;
+ case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED:
+ return false;
default:
- LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
- << " exec_dexoptanalyzer result=" << result;
+ LOG(ERROR) << "Unexpected result from analyzing secondary dex " << dex_path
+ << " result=" << result;
return false;
}
}
+enum SecondaryDexAccess {
+ kSecondaryDexAccessReadOk = 0,
+ kSecondaryDexAccessDoesNotExist = 1,
+ kSecondaryDexAccessPermissionError = 2,
+ kSecondaryDexAccessIOError = 3
+};
+
+static SecondaryDexAccess check_secondary_dex_access(const std::string& dex_path) {
+ // Check if the path exists and can be read. If not, there's nothing to do.
+ if (access(dex_path.c_str(), R_OK) == 0) {
+ return kSecondaryDexAccessReadOk;
+ } else {
+ if (errno == ENOENT) {
+ LOG(INFO) << "Secondary dex does not exist: " << dex_path;
+ return kSecondaryDexAccessDoesNotExist;
+ } else {
+ PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+ return errno == EACCES
+ ? kSecondaryDexAccessPermissionError
+ : kSecondaryDexAccessIOError;
+ }
+ }
+}
+
+static bool is_file_public(const std::string& filename) {
+ struct stat file_stat;
+ if (stat(filename.c_str(), &file_stat) == 0) {
+ return (file_stat.st_mode & S_IROTH) != 0;
+ }
+ return false;
+}
+
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+ char* out_oat_dir, char* out_oat_isa_dir, char* out_oat_path) {
+ size_t dirIndex = dex_path.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+ return false;
+ }
+ // TODO(calin): we have similar computations in at lest 3 other places
+ // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+ // using string append.
+ std::string apk_dir = dex_path.substr(0, dirIndex);
+ snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+ snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+ if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+ /*is_secondary_dex*/true, out_oat_path)) {
+ LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+ return false;
+ }
+ return true;
+}
+
+// Validate that the dexopt_flags contain a valid storage flag and convert that to an installd
+// recognized storage flags (FLAG_STORAGE_CE or FLAG_STORAGE_DE).
+static bool validate_dexopt_storage_flags(int dexopt_flags, int* out_storage_flag) {
+ if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
+ *out_storage_flag = FLAG_STORAGE_CE;
+ if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+ LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
+ return false;
+ }
+ } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+ *out_storage_flag = FLAG_STORAGE_DE;
+ } else {
+ LOG(ERROR) << "Secondary dex storage flag must be set";
+ return false;
+ }
+ return true;
+}
+
// Processes the dex_path as a secondary dex files and return true if the path dex file should
// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
// successfully.
@@ -1478,102 +1649,107 @@
// - is_public_out: whether or not the oat file should not be made public
// - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
// - oat_dir_out: the oat dir path where the oat file should be stored
-// - dex_path_out: the real path of the dex file
-static bool process_secondary_dex_dexopt(const char* original_dex_path, const char* pkgname,
+static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname,
int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
- std::string* oat_dir_out, std::string* dex_path_out, bool downgrade) {
+ std::string* oat_dir_out, bool downgrade, const char* class_loader_context) {
+ LOG(DEBUG) << "Processing secondary dex path " << dex_path;
int storage_flag;
-
- if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
- storage_flag = FLAG_STORAGE_CE;
- if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
- LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
- return false;
- }
- } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
- storage_flag = FLAG_STORAGE_DE;
- } else {
- LOG(ERROR) << "Secondary dex storage flag must be set";
+ if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag)) {
return false;
}
-
- {
- // As opposed to the primary apk, secondary dex files might contain symlinks.
- // Resolve the path before passing it to the validate method to
- // make sure the verification is done on the real location.
- UniqueCPtr<char> dex_real_path_cstr(realpath(original_dex_path, nullptr));
- if (dex_real_path_cstr == nullptr) {
- PLOG(ERROR) << "Could not get the real path of the secondary dex file "
- << original_dex_path;
- return false;
- } else {
- dex_path_out->assign(dex_real_path_cstr.get());
- }
- }
- const std::string& dex_path = *dex_path_out;
- if (!validate_dex_path_size(dex_path)) {
+ // Compute the oat dir as it's not easy to extract it from the child computation.
+ char oat_path[PKG_PATH_MAX];
+ char oat_dir[PKG_PATH_MAX];
+ char oat_isa_dir[PKG_PATH_MAX];
+ if (!create_secondary_dex_oat_layout(
+ dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path)) {
+ LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
return false;
}
- if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
- return false;
- }
-
- // Check if the path exist. If not, there's nothing to do.
- struct stat dex_path_stat;
- if (stat(dex_path.c_str(), &dex_path_stat) != 0) {
- if (errno == ENOENT) {
- // Secondary dex files might be deleted any time by the app.
- // Nothing to do if that's the case
- ALOGV("Secondary dex does not exist %s", dex_path.c_str());
- return NO_DEXOPT_NEEDED;
- } else {
- PLOG(ERROR) << "Could not access secondary dex " << dex_path;
- }
- }
-
- // Check if we should make the oat file public.
- // Note that if the dex file is not public the compiled code cannot be made public.
- *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) &&
- ((dex_path_stat.st_mode & S_IROTH) != 0);
-
- // Prepare the oat directories.
- if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) {
- return false;
- }
-
- // Analyze profiles.
- bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+ oat_dir_out->assign(oat_dir);
pid_t pid = fork();
if (pid == 0) {
// child -- drop privileges before continuing.
drop_capabilities(uid);
- // Run dexoptanalyzer to get dexopt_needed code.
- exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter, profile_was_updated,
- downgrade);
- exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+
+ // Validate the path structure.
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
+
+ // Open the dex file.
+ unique_fd zip_fd;
+ zip_fd.reset(open(dex_path.c_str(), O_RDONLY));
+ if (zip_fd.get() < 0) {
+ if (errno == ENOENT) {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE);
+ } else {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
+ }
+
+ // Prepare the oat directories.
+ if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
+
+ // Open the vdex/oat files if any.
+ unique_fd oat_file_fd;
+ unique_fd vdex_file_fd;
+ if (!maybe_open_oat_and_vdex_file(dex_path,
+ *oat_dir_out,
+ instruction_set,
+ true /* is_secondary_dex */,
+ &oat_file_fd,
+ &vdex_file_fd)) {
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+ }
+
+ // Analyze profiles.
+ bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+
+ // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return.
+ exec_dexoptanalyzer(dex_path,
+ vdex_file_fd.get(),
+ oat_file_fd.get(),
+ zip_fd.get(),
+ instruction_set,
+ compiler_filter, profile_was_updated,
+ downgrade,
+ class_loader_context);
+ PLOG(ERROR) << "Failed to exec dexoptanalyzer";
+ _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
}
/* parent */
-
int result = wait_child(pid);
if (!WIFEXITED(result)) {
LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result;
return false;
}
result = WEXITSTATUS(result);
- bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+ // Check that we successfully executed dexoptanalyzer.
+ bool success = process_secondary_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+
+ LOG(DEBUG) << "Processed secondary dex file " << dex_path << " result=" << result;
+
// Run dexopt only if needed or forced.
- // Note that dexoptanalyzer is executed even if force compilation is enabled.
- // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
- // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
- // for oat files from dalvik-cache.
- if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+ // Note that dexoptanalyzer is executed even if force compilation is enabled (because it
+ // makes the code simpler; force compilation is only needed during tests).
+ if (success &&
+ (result != SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE) &&
+ ((dexopt_flags & DEXOPT_FORCE) != 0)) {
*dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
}
+ // Check if we should make the oat file public.
+ // Note that if the dex file is not public the compiled code cannot be made public.
+ // It is ok to check this flag outside in the parent process.
+ *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path);
+
return success;
}
@@ -1601,17 +1777,15 @@
bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+ bool background_job_compile = (dexopt_flags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
// Check if we're dealing with a secondary dex file and if we need to compile it.
std::string oat_dir_str;
- std::string dex_real_path;
if (is_secondary_dex) {
if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
- &dex_real_path,
- downgrade)) {
+ downgrade, class_loader_context)) {
oat_dir = oat_dir_str.c_str();
- dex_path = dex_real_path.c_str();
if (dexopt_needed == NO_DEXOPT_NEEDED) {
return 0; // Nothing to do, report success.
}
@@ -1643,8 +1817,8 @@
// Open vdex files.
Dex2oatFileWrapper in_vdex_fd;
Dex2oatFileWrapper out_vdex_fd;
- if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
- is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+ if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set,
+ is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
return -1;
}
@@ -1697,6 +1871,7 @@
compiler_filter,
debuggable,
boot_complete,
+ background_job_compile,
reference_profile_fd.get(),
class_loader_context);
_exit(68); /* only get here on exec failure */
@@ -1748,29 +1923,13 @@
return false;
}
-// Create the oat file structure for the secondary dex 'dex_path' and assign
-// the individual path component to the 'out_' parameters.
-static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
- /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
- size_t dirIndex = dex_path.rfind('/');
- if (dirIndex == std::string::npos) {
- LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
- return false;
- }
- // TODO(calin): we have similar computations in at lest 3 other places
- // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
- // use string append.
- std::string apk_dir = dex_path.substr(0, dirIndex);
- snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
- snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
-
- if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
- /*is_secondary_dex*/true, out_oat_path)) {
- LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
- return false;
- }
- return true;
-}
+enum ReconcileSecondaryDexResult {
+ kReconcileSecondaryDexExists = 0,
+ kReconcileSecondaryDexCleanedUp = 1,
+ kReconcileSecondaryDexValidationError = 2,
+ kReconcileSecondaryDexCleanUpError = 3,
+ kReconcileSecondaryDexAccessIOError = 4,
+};
// Reconcile the secondary dex 'dex_path' and its generated oat files.
// Return true if all the parameters are valid and the secondary dex file was
@@ -1784,36 +1943,15 @@
const std::string& pkgname, int uid, const std::vector<std::string>& isas,
const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
/*out*/bool* out_secondary_dex_exists) {
- // Set out to false to start with, just in case we have validation errors.
- *out_secondary_dex_exists = false;
- if (!validate_dex_path_size(dex_path)) {
- return false;
- }
-
+ *out_secondary_dex_exists = false; // start by assuming the file does not exist.
if (isas.size() == 0) {
LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
return false;
}
- const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
-
- // Note that we cannot validate the package path here because the file might not exist
- // and we cannot call realpath to resolve system symlinks. Since /data/user/0 symlinks to
- // /data/data/ a lot of validations will fail if we attempt to check the package path.
- // It is still ok to be more relaxed because any file removal is done after forking and
- // dropping capabilities.
- if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
- uid, storage_flag, /*validate_package_path*/ false)) {
- LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
- return false;
- }
-
- if (access(dex_path.c_str(), F_OK) == 0) {
- // The path exists, nothing to do. The odex files (if any) will be left untouched.
- *out_secondary_dex_exists = true;
- return true;
- } else if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+ if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
+ LOG(ERROR) << "reconcile_secondary_dex_file called with invalid storage_flag: "
+ << storage_flag;
return false;
}
@@ -1821,23 +1959,39 @@
// of the package user id. So we fork and drop capabilities in the child.
pid_t pid = fork();
if (pid == 0) {
- // The secondary dex does not exist anymore. Clear any generated files.
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+
+ const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ _exit(kReconcileSecondaryDexValidationError);
+ }
+
+ SecondaryDexAccess access_check = check_secondary_dex_access(dex_path);
+ switch (access_check) {
+ case kSecondaryDexAccessDoesNotExist:
+ // File does not exist. Proceed with cleaning.
+ break;
+ case kSecondaryDexAccessReadOk: _exit(kReconcileSecondaryDexExists);
+ case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
+ case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
+ default:
+ LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+ _exit(kReconcileSecondaryDexValidationError);
+ }
+
+ // The secondary dex does not exist anymore or it's. Clear any generated files.
char oat_path[PKG_PATH_MAX];
char oat_dir[PKG_PATH_MAX];
char oat_isa_dir[PKG_PATH_MAX];
bool result = true;
- /* child -- drop privileges before continuing */
- drop_capabilities(uid);
for (size_t i = 0; i < isas.size(); i++) {
- if (!create_secondary_dex_oat_layout(dex_path,
- isas[i],
- oat_dir,
- oat_isa_dir,
- oat_path)) {
- LOG(ERROR) << "Could not create secondary odex layout: "
- << dex_path;
- result = false;
- continue;
+ if (!create_secondary_dex_oat_layout(
+ dex_path,isas[i], oat_dir, oat_isa_dir, oat_path)) {
+ LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
+ _exit(kReconcileSecondaryDexValidationError);
}
// Delete oat/vdex/art files.
@@ -1862,11 +2016,43 @@
result = rmdir_if_empty(oat_isa_dir) && result;
result = rmdir_if_empty(oat_dir) && result;
}
- result ? _exit(0) : _exit(1);
+ if (!result) {
+ PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+ }
+ _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
}
int return_code = wait_child(pid);
- return return_code == 0;
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code;
+ } else {
+ return_code = WEXITSTATUS(return_code);
+ }
+
+ LOG(DEBUG) << "Reconcile secondary dex path " << dex_path << " result=" << return_code;
+
+ switch (return_code) {
+ case kReconcileSecondaryDexCleanedUp:
+ case kReconcileSecondaryDexValidationError:
+ // If we couldn't validate assume the dex file does not exist.
+ // This will purge the entry from the PM records.
+ *out_secondary_dex_exists = false;
+ return true;
+ case kReconcileSecondaryDexExists:
+ *out_secondary_dex_exists = true;
+ return true;
+ case kReconcileSecondaryDexAccessIOError:
+ // We had an access IO error.
+ // Return false so that we can try again.
+ // The value of out_secondary_dex_exists does not matter in this case and by convention
+ // is set to false.
+ *out_secondary_dex_exists = false;
+ return false;
+ default:
+ LOG(ERROR) << "Unexpected code from reconcile_secondary_dex_file: " << return_code;
+ *out_secondary_dex_exists = false;
+ return false;
+ }
}
// Helper for move_ab, so that we can have common failure-case cleanup.
@@ -2031,5 +2217,135 @@
return return_value_oat && return_value_art && return_value_vdex;
}
+static bool is_absolute_path(const std::string& path) {
+ if (path.find('/') != 0 || path.find("..") != std::string::npos) {
+ LOG(ERROR) << "Invalid absolute path " << path;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static bool is_valid_instruction_set(const std::string& instruction_set) {
+ // TODO: add explicit whitelisting of instruction sets
+ if (instruction_set.find('/') != std::string::npos) {
+ LOG(ERROR) << "Invalid instruction set " << instruction_set;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir,
+ const char *apk_path, const char *instruction_set) {
+ std::string oat_dir_ = oat_dir;
+ std::string apk_path_ = apk_path;
+ std::string instruction_set_ = instruction_set;
+
+ if (!is_absolute_path(oat_dir_)) return false;
+ if (!is_absolute_path(apk_path_)) return false;
+ if (!is_valid_instruction_set(instruction_set_)) return false;
+
+ std::string::size_type end = apk_path_.rfind('.');
+ std::string::size_type start = apk_path_.rfind('/', end);
+ if (end == std::string::npos || start == std::string::npos) {
+ LOG(ERROR) << "Invalid apk_path " << apk_path_;
+ return false;
+ }
+
+ std::string res_ = oat_dir_ + '/' + instruction_set + '/'
+ + apk_path_.substr(start + 1, end - start - 1) + ".odex";
+ const char* res = res_.c_str();
+ if (strlen(res) >= PKG_PATH_MAX) {
+ LOG(ERROR) << "Result too large";
+ return false;
+ } else {
+ strlcpy(path, res, PKG_PATH_MAX);
+ return true;
+ }
+}
+
+bool calculate_odex_file_path_default(char path[PKG_PATH_MAX], const char *apk_path,
+ const char *instruction_set) {
+ std::string apk_path_ = apk_path;
+ std::string instruction_set_ = instruction_set;
+
+ if (!is_absolute_path(apk_path_)) return false;
+ if (!is_valid_instruction_set(instruction_set_)) return false;
+
+ std::string::size_type end = apk_path_.rfind('.');
+ std::string::size_type start = apk_path_.rfind('/', end);
+ if (end == std::string::npos || start == std::string::npos) {
+ LOG(ERROR) << "Invalid apk_path " << apk_path_;
+ return false;
+ }
+
+ std::string oat_dir = apk_path_.substr(0, start + 1) + "oat";
+ return calculate_oat_file_path_default(path, oat_dir.c_str(), apk_path, instruction_set);
+}
+
+bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src,
+ const char *instruction_set) {
+ std::string src_ = src;
+ std::string instruction_set_ = instruction_set;
+
+ if (!is_absolute_path(src_)) return false;
+ if (!is_valid_instruction_set(instruction_set_)) return false;
+
+ for (auto it = src_.begin() + 1; it < src_.end(); ++it) {
+ if (*it == '/') {
+ *it = '@';
+ }
+ }
+
+ std::string res_ = android_data_dir + DALVIK_CACHE + '/' + instruction_set_ + src_
+ + DALVIK_CACHE_POSTFIX;
+ const char* res = res_.c_str();
+ if (strlen(res) >= PKG_PATH_MAX) {
+ LOG(ERROR) << "Result too large";
+ return false;
+ } else {
+ strlcpy(path, res, PKG_PATH_MAX);
+ return true;
+ }
+}
+
+bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
+ const std::string& code_path) {
+ int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);
+
+ unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, code_path);
+ if (snapshot_fd < 0) {
+ return false;
+ }
+
+ std::vector<unique_fd> profiles_fd;
+ unique_fd reference_profile_fd;
+ open_profile_files(app_shared_gid, package_name, /*is_secondary_dex*/ false, &profiles_fd,
+ &reference_profile_fd);
+ if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
+ return false;
+ }
+
+ profiles_fd.push_back(std::move(reference_profile_fd));
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(app_shared_gid);
+ run_profman_merge(profiles_fd, snapshot_fd);
+ exit(42); /* only get here on exec failure */
+ }
+
+ /* parent */
+ int return_code = wait_child(pid);
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "profman failed for " << package_name << ":" << code_path;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 23446da..8d81611 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -17,6 +17,8 @@
#ifndef DEXOPT_H_
#define DEXOPT_H_
+#include "installd_constants.h"
+
#include <sys/types.h>
#include <cutils/multiuser.h>
@@ -48,6 +50,18 @@
// the reference profiles accessible with open_reference_profile().
bool analyze_primary_profiles(uid_t uid, const std::string& pkgname);
+// Create a snapshot of the profile information for the given package and code path.
+// The profile snapshot is the aggregation of all existing profiles (all current user
+// profiles & the reference profile) and is meant to capture the all the profile information
+// without performing a merge into the reference profile which might impact future dex2oat
+// compilations.
+// The snapshot is created next to the reference profile of the package and the
+// ownership is assigned to AID_SYSTEM.
+// The snapshot location is reference_profile_location.snapshot. If a snapshot is already
+// there, it will be truncated and overwritten.
+bool create_profile_snapshot(int32_t app_id, const std::string& package,
+ const std::string& code_path);
+
bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);
bool copy_system_profile(const std::string& system_profile,
@@ -66,6 +80,15 @@
const char* volume_uuid, const char* class_loader_context, const char* se_info,
bool downgrade);
+bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir,
+ const char *apk_path, const char *instruction_set);
+
+bool calculate_odex_file_path_default(char path[PKG_PATH_MAX], const char *apk_path,
+ const char *instruction_set);
+
+bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src,
+ const char *instruction_set);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index edcdb6a..b3a6daf 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -16,15 +16,15 @@
#define LOG_TAG "installd"
-#include <stdlib.h>
-#include <string.h>
-
-#include <log/log.h> // TODO: Move everything to base::logging.
-
#include <globals.h>
#include <installd_constants.h>
#include <utils.h>
+#include <android-base/logging.h>
+
+#include <stdlib.h>
+#include <string.h>
+
namespace android {
namespace installd {
@@ -44,106 +44,78 @@
static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under
// ANDROID_DATA
-/* Directory records that are used in execution of commands. */
-dir_rec_t android_app_dir;
-dir_rec_t android_app_ephemeral_dir;
-dir_rec_t android_app_lib_dir;
-dir_rec_t android_app_private_dir;
-dir_rec_t android_asec_dir;
-dir_rec_t android_data_dir;
-dir_rec_t android_media_dir;
-dir_rec_t android_mnt_expand_dir;
-dir_rec_t android_profiles_dir;
+std::string android_app_dir;
+std::string android_app_ephemeral_dir;
+std::string android_app_lib_dir;
+std::string android_app_private_dir;
+std::string android_asec_dir;
+std::string android_data_dir;
+std::string android_media_dir;
+std::string android_mnt_expand_dir;
+std::string android_profiles_dir;
+std::string android_root_dir;
-dir_rec_array_t android_system_dirs;
+std::vector<std::string> android_system_dirs;
-/**
- * Initialize all the global variables that are used elsewhere. Returns 0 upon
- * success and -1 on error.
- */
-void free_globals() {
- size_t i;
-
- for (i = 0; i < android_system_dirs.count; i++) {
- if (android_system_dirs.dirs[i].path != NULL) {
- free(android_system_dirs.dirs[i].path);
- }
+bool init_globals_from_data_and_root() {
+ const char* data_path = getenv("ANDROID_DATA");
+ if (data_path == nullptr) {
+ LOG(ERROR) << "Could not find ANDROID_DATA";
+ return false;
}
+ const char* root_path = getenv("ANDROID_ROOT");
+ if (root_path == nullptr) {
+ LOG(ERROR) << "Could not find ANDROID_ROOT";
+ return false;
+ }
+ return init_globals_from_data_and_root(data_path, root_path);
+}
- free(android_system_dirs.dirs);
+static std::string ensure_trailing_slash(const std::string& path) {
+ if (path.rfind('/') != path.size() - 1) {
+ return path + '/';
+ } else {
+ return path;
+ }
}
bool init_globals_from_data_and_root(const char* data, const char* root) {
// Get the android data directory.
- if (get_path_from_string(&android_data_dir, data) < 0) {
- return false;
- }
+ android_data_dir = ensure_trailing_slash(data);
+
+ // Get the android root directory.
+ android_root_dir = ensure_trailing_slash(root);
// Get the android app directory.
- if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) {
- return false;
- }
+ android_app_dir = android_data_dir + APP_SUBDIR;
// Get the android protected app directory.
- if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) {
- return false;
- }
+ android_app_private_dir = android_data_dir + PRIVATE_APP_SUBDIR;
// Get the android ephemeral app directory.
- if (copy_and_append(&android_app_ephemeral_dir, &android_data_dir, EPHEMERAL_APP_SUBDIR) < 0) {
- return false;
- }
+ android_app_ephemeral_dir = android_data_dir + EPHEMERAL_APP_SUBDIR;
// Get the android app native library directory.
- if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) {
- return false;
- }
+ android_app_lib_dir = android_data_dir + APP_LIB_SUBDIR;
// Get the sd-card ASEC mount point.
- if (get_path_from_env(&android_asec_dir, ASEC_MOUNTPOINT_ENV_NAME) < 0) {
- return false;
- }
+ android_asec_dir = ensure_trailing_slash(getenv(ASEC_MOUNTPOINT_ENV_NAME));
// Get the android media directory.
- if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) {
- return false;
- }
+ android_media_dir = android_data_dir + MEDIA_SUBDIR;
// Get the android external app directory.
- if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand/") < 0) {
- return false;
- }
+ android_mnt_expand_dir = "/mnt/expand/";
// Get the android profiles directory.
- if (copy_and_append(&android_profiles_dir, &android_data_dir, PROFILES_SUBDIR) < 0) {
- return false;
- }
+ android_profiles_dir = android_data_dir + PROFILES_SUBDIR;
// Take note of the system and vendor directories.
- android_system_dirs.count = 4;
-
- android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
- if (android_system_dirs.dirs == NULL) {
- ALOGE("Couldn't allocate array for dirs; aborting\n");
- return false;
- }
-
- dir_rec_t android_root_dir;
- if (get_path_from_string(&android_root_dir, root) < 0) {
- return false;
- }
-
- android_system_dirs.dirs[0].path = build_string2(android_root_dir.path, APP_SUBDIR);
- android_system_dirs.dirs[0].len = strlen(android_system_dirs.dirs[0].path);
-
- android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR);
- android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);
-
- android_system_dirs.dirs[2].path = strdup("/vendor/app/");
- android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path);
-
- android_system_dirs.dirs[3].path = strdup("/oem/app/");
- android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path);
+ android_system_dirs.clear();
+ android_system_dirs.push_back(android_root_dir + APP_SUBDIR);
+ android_system_dirs.push_back(android_root_dir + PRIV_APP_SUBDIR);
+ android_system_dirs.push_back("/vendor/app/");
+ android_system_dirs.push_back("/oem/app/");
return true;
}
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index c90beec..633e33b 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -19,40 +19,29 @@
#define GLOBALS_H_
#include <inttypes.h>
+#include <string>
+#include <vector>
namespace android {
namespace installd {
-/* constants */
-
// Name of the environment variable that contains the asec mountpoint.
static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT";
-/* data structures */
+extern std::string android_app_dir;
+extern std::string android_app_ephemeral_dir;
+extern std::string android_app_lib_dir;
+extern std::string android_app_private_dir;
+extern std::string android_asec_dir;
+extern std::string android_data_dir;
+extern std::string android_media_dir;
+extern std::string android_mnt_expand_dir;
+extern std::string android_profiles_dir;
+extern std::string android_root_dir;
-struct dir_rec_t {
- char* path;
- size_t len;
-};
+extern std::vector<std::string> android_system_dirs;
-struct dir_rec_array_t {
- size_t count;
- dir_rec_t* dirs;
-};
-
-extern dir_rec_t android_app_dir;
-extern dir_rec_t android_app_ephemeral_dir;
-extern dir_rec_t android_app_lib_dir;
-extern dir_rec_t android_app_private_dir;
-extern dir_rec_t android_asec_dir;
-extern dir_rec_t android_data_dir;
-extern dir_rec_t android_media_dir;
-extern dir_rec_t android_mnt_expand_dir;
-extern dir_rec_t android_profiles_dir;
-
-extern dir_rec_array_t android_system_dirs;
-
-void free_globals();
+bool init_globals_from_data_and_root();
bool init_globals_from_data_and_root(const char* data, const char* root);
} // namespace installd
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 35936a2..95ed2ff 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -30,6 +30,7 @@
#include <private/android_filesystem_config.h>
#include "InstalldNativeService.h"
+#include "dexopt.h"
#include "globals.h"
#include "installd_constants.h"
#include "installd_deps.h" // Need to fill in requirements of commands.
@@ -50,133 +51,22 @@
return property_get(key, value, default_value);
}
-// Compute the output path of
-bool calculate_oat_file_path(char path[PKG_PATH_MAX],
- const char *oat_dir,
- const char *apk_path,
- const char *instruction_set) {
- const char *file_name_start;
- const char *file_name_end;
-
- file_name_start = strrchr(apk_path, '/');
- if (file_name_start == NULL) {
- SLOGE("apk_path '%s' has no '/'s in it\n", apk_path);
- return false;
- }
- file_name_end = strrchr(apk_path, '.');
- if (file_name_end < file_name_start) {
- SLOGE("apk_path '%s' has no extension\n", apk_path);
- return false;
- }
-
- // Calculate file_name
- int file_name_len = file_name_end - file_name_start - 1;
- char file_name[file_name_len + 1];
- memcpy(file_name, file_name_start + 1, file_name_len);
- file_name[file_name_len] = '\0';
-
- // <apk_parent_dir>/oat/<isa>/<file_name>.odex
- snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex", oat_dir, instruction_set, file_name);
- return true;
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
+ const char *instruction_set) {
+ return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
}
-/*
- * Computes the odex file for the given apk_path and instruction_set.
- * /system/framework/whatever.jar -> /system/framework/oat/<isa>/whatever.odex
- *
- * Returns false if it failed to determine the odex file path.
- */
-bool calculate_odex_file_path(char path[PKG_PATH_MAX],
- const char *apk_path,
- const char *instruction_set) {
- if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set)
- + strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) {
- SLOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
- return false;
- }
-
- strcpy(path, apk_path);
- char *end = strrchr(path, '/');
- if (end == NULL) {
- SLOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
- return false;
- }
- const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/');
-
- strcpy(end + 1, "oat/"); // path = /system/framework/oat/\0
- strcat(path, instruction_set); // path = /system/framework/oat/<isa>\0
- strcat(path, apk_end); // path = /system/framework/oat/<isa>/whatever.jar\0
- end = strrchr(path, '.');
- if (end == NULL) {
- SLOGE("apk_path '%s' has no extension.\n", apk_path);
- return false;
- }
- strcpy(end + 1, "odex");
- return true;
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
+ const char *instruction_set) {
+ return calculate_odex_file_path_default(path, apk_path, instruction_set);
}
-bool create_cache_path(char path[PKG_PATH_MAX],
- const char *src,
- const char *instruction_set) {
- /* 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;
- }
-
- size_t dstlen =
- android_data_dir.len +
- strlen(DALVIK_CACHE) +
- 1 +
- strlen(instruction_set) +
- srclen +
- strlen(DALVIK_CACHE_POSTFIX) + 2;
-
- if (dstlen > PKG_PATH_MAX) {
- return false;
- }
-
- sprintf(path,"%s%s/%s/%s",
- android_data_dir.path,
- DALVIK_CACHE,
- instruction_set,
- src + 1 /* skip the leading / */);
-
- char* tmp =
- path +
- android_data_dir.len +
- strlen(DALVIK_CACHE) +
- 1 +
- strlen(instruction_set) + 1;
-
- for(; *tmp; tmp++) {
- if (*tmp == '/') {
- *tmp = '@';
- }
- }
-
- strcat(path, DALVIK_CACHE_POSTFIX);
- return true;
+bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) {
+ return create_cache_path_default(path, src, instruction_set);
}
static bool initialize_globals() {
- const char* data_path = getenv("ANDROID_DATA");
- if (data_path == nullptr) {
- SLOGE("Could not find ANDROID_DATA");
- return false;
- }
- const char* root_path = getenv("ANDROID_ROOT");
- if (root_path == nullptr) {
- SLOGE("Could not find ANDROID_ROOT");
- return false;
- }
-
- return init_globals_from_data_and_root(data_path, root_path);
+ return init_globals_from_data_and_root();
}
static int initialize_directories() {
@@ -184,7 +74,7 @@
// Read current filesystem layout version to handle upgrade paths
char version_path[PATH_MAX];
- snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);
+ snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.c_str());
int oldVersion;
if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
@@ -206,7 +96,7 @@
SLOGD("Upgrading to /data/misc/user directories");
char misc_dir[PATH_MAX];
- snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path);
+ snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.c_str());
char keychain_added_dir[PATH_MAX];
snprintf(keychain_added_dir, PATH_MAX, "%s/keychain/cacerts-added", misc_dir);
@@ -227,7 +117,7 @@
if ((name[1] == '.') && (name[2] == 0)) continue;
}
- uint32_t user_id = atoi(name);
+ uint32_t user_id = std::stoi(name);
// /data/misc/user/<user_id>
if (ensure_config_user_dirs(user_id) == -1) {
@@ -281,7 +171,7 @@
return res;
}
-static int log_callback(int type, const char *fmt, ...) {
+static int log_callback(int type, const char *fmt, ...) { // NOLINT
va_list ap;
int priority;
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 2597c79..b49057d 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -49,6 +49,9 @@
constexpr int DEXOPT_FORCE = 1 << 6;
constexpr int DEXOPT_STORAGE_CE = 1 << 7;
constexpr int DEXOPT_STORAGE_DE = 1 << 8;
+// Tells the compiler that it is invoked from the background service. This
+// controls whether extra debugging flags can be used (taking more compile time.)
+constexpr int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
/* all known values for dexopt flags */
constexpr int DEXOPT_MASK =
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 09e1a00..8a56894 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -146,14 +146,13 @@
return 0;
}
// Copy in the default value.
- strncpy(value, default_value, kPropertyValueMax - 1);
+ strlcpy(value, default_value, kPropertyValueMax - 1);
value[kPropertyValueMax - 1] = 0;
return strlen(default_value);// TODO: Need to truncate?
}
- size_t size = std::min(kPropertyValueMax - 1, prop_value->length());
- strncpy(value, prop_value->data(), size);
- value[size] = 0;
- return static_cast<int>(size);
+ size_t size = std::min(kPropertyValueMax - 1, prop_value->length()) + 1;
+ strlcpy(value, prop_value->data(), size);
+ return static_cast<int>(size - 1);
}
std::string GetOTADataDirectory() const {
@@ -883,7 +882,7 @@
// backs to do weird things.)
const char* apk_path = package_parameters_.apk_path;
CHECK(apk_path != nullptr);
- if (StartsWith(apk_path, android_root_.c_str())) {
+ if (StartsWith(apk_path, android_root_)) {
const char* last_slash = strrchr(apk_path, '/');
if (last_slash != nullptr) {
std::string path(apk_path, last_slash - apk_path + 1);
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 630c1f3..1a22992 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -3,15 +3,16 @@
name: "installd_utils_test",
clang: true,
srcs: ["installd_utils_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
shared_libs: [
"libbase",
- "liblog",
"libutils",
"libcutils",
],
static_libs: [
- "libinstalld",
"libdiskusage",
+ "libinstalld",
+ "liblog",
],
}
@@ -19,18 +20,19 @@
name: "installd_cache_test",
clang: true,
srcs: ["installd_cache_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
shared_libs: [
"libbase",
"libbinder",
"libcutils",
- "liblog",
- "liblogwrap",
"libselinux",
"libutils",
],
static_libs: [
- "libinstalld",
"libdiskusage",
+ "libinstalld",
+ "liblog",
+ "liblogwrap",
],
}
@@ -38,17 +40,38 @@
name: "installd_service_test",
clang: true,
srcs: ["installd_service_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
shared_libs: [
"libbase",
"libbinder",
"libcutils",
- "liblog",
- "liblogwrap",
"libselinux",
"libutils",
],
static_libs: [
- "libinstalld",
"libdiskusage",
+ "libinstalld",
+ "liblog",
+ "liblogwrap",
+ ],
+}
+
+cc_test {
+ name: "installd_dexopt_test",
+ clang: true,
+ srcs: ["installd_dexopt_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libselinux",
+ "libutils",
+ ],
+ static_libs: [
+ "libdiskusage",
+ "libinstalld",
+ "liblog",
+ "liblogwrap",
],
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
new file mode 100644
index 0000000..eaf0aa1
--- /dev/null
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2017 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 <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include <cutils/properties.h>
+
+#include <gtest/gtest.h>
+
+#include <selinux/android.h>
+#include <selinux/avc.h>
+
+#include "dexopt.h"
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "tests/test_utils.h"
+#include "utils.h"
+
+using android::base::ReadFully;
+using android::base::unique_fd;
+
+namespace android {
+namespace installd {
+
+// TODO(calin): try to dedup this code.
+#if defined(__arm__)
+static const std::string kRuntimeIsa = "arm";
+#elif defined(__aarch64__)
+static const std::string kRuntimeIsa = "arm64";
+#elif defined(__mips__) && !defined(__LP64__)
+static const std::string kRuntimeIsa = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const std::string kRuntimeIsa = "mips64";
+#elif defined(__i386__)
+static const std::string kRuntimeIsa = "x86";
+#elif defined(__x86_64__)
+static const std::string kRuntimeIsa = "x86_64";
+#else
+static const std::string kRuntimeIsa = "none";
+#endif
+
+int get_property(const char *key, char *value, const char *default_value) {
+ return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
+ const char *instruction_set) {
+ return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
+ const char *instruction_set) {
+ return calculate_odex_file_path_default(path, apk_path, instruction_set);
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) {
+ return create_cache_path_default(path, src, instruction_set);
+}
+
+static void run_cmd(const std::string& cmd) {
+ system(cmd.c_str());
+}
+
+static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
+ ::mkdir(path.c_str(), mode);
+ ::chown(path.c_str(), owner, group);
+ ::chmod(path.c_str(), mode);
+}
+
+static int log_callback(int type, const char *fmt, ...) { // NOLINT
+ va_list ap;
+ int priority;
+
+ switch (type) {
+ case SELINUX_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case SELINUX_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ default:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ }
+ va_start(ap, fmt);
+ LOG_PRI_VA(priority, "SELinux", fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static bool init_selinux() {
+ int selinux_enabled = (is_selinux_enabled() > 0);
+
+ union selinux_callback cb;
+ cb.func_log = log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+
+ if (selinux_enabled && selinux_status_open(true) < 0) {
+ LOG(ERROR) << "Could not open selinux status; exiting";
+ return false;
+ }
+
+ return true;
+}
+
+// Base64 encoding of a simple dex files with 2 methods.
+static const char kDexFile[] =
+ "UEsDBBQAAAAIAOiOYUs9y6BLCgEAABQCAAALABwAY2xhc3Nlcy5kZXhVVAkAA/Ns+lkOHv1ZdXgL"
+ "AAEEI+UCAASIEwAAS0mt4DIwNmX4qpn7j/2wA7v7N+ZvoQpCJRlVx5SWa4YaiDAxMBQwMDBUhJkI"
+ "MUBBDyMDAzsDRJwFxAdioBDDHAYEYAbiFUAM1M5wAIhFGCGKDIDYAogdgNgDiH2BOAiI0xghekDm"
+ "sQIxGxQzM6ACRijNhCbOhCZfyohdPYyuh8szgtVkMkLsLhAAqeCDi+ejibPZZOZlltgxsDnqZSWW"
+ "JTKwOUFoZh9HayDhZM0g5AMS0M9JzEvX90/KSk0usWZgDAMaws5nAyXBzmpoYGlgAjsAyJoBMp0b"
+ "zQ8gGhbOTEhhzYwU3qxIYc2GFN6MClC/AhUyKUDMAYU9M1Qc5F8GKBscVgIQM0FxCwBQSwECHgMU"
+ "AAAACADojmFLPcugSwoBAAAUAgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAA/Ns"
+ "+ll1eAsAAQQj5QIABIgTAABQSwUGAAAAAAEAAQBRAAAATwEAAAAA";
+
+
+class DexoptTest : public testing::Test {
+protected:
+ static constexpr bool kDebug = false;
+ static constexpr uid_t kSystemUid = 1000;
+ static constexpr uid_t kSystemGid = 1000;
+ static constexpr int32_t kOSdkVersion = 25;
+ static constexpr int32_t kAppDataFlags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+ static constexpr int32_t kTestUserId = 0;
+ static constexpr uid_t kTestAppId = 19999;
+
+ const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
+ const uid_t kTestAppGid = multiuser_get_shared_gid(kTestUserId, kTestAppId);
+
+ InstalldNativeService* service_;
+ std::unique_ptr<std::string> volume_uuid_;
+ std::string package_name_;
+ std::string app_apk_dir_;
+ std::string app_private_dir_ce_;
+ std::string app_private_dir_de_;
+ std::string se_info_;
+
+ int64_t ce_data_inode_;
+
+ std::string secondary_dex_ce_;
+ std::string secondary_dex_ce_link_;
+ std::string secondary_dex_de_;
+
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
+ // Initialize the globals holding the file system main paths (/data/, /system/ etc..).
+ // This is needed in order to compute the application and profile paths.
+ ASSERT_TRUE(init_globals_from_data_and_root());
+ // Initialize selinux log callbacks.
+ // This ensures that selinux is up and running and re-directs the selinux messages
+ // to logcat (in order to make it easier to investigate test results).
+ ASSERT_TRUE(init_selinux());
+ service_ = new InstalldNativeService();
+
+ volume_uuid_ = nullptr;
+ package_name_ = "com.installd.test.dexopt";
+ se_info_ = "default";
+ app_apk_dir_ = android_app_dir + package_name_;
+
+ create_mock_app();
+ }
+
+ virtual void TearDown() {
+ if (!kDebug) {
+ service_->destroyAppData(
+ volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_);
+ run_cmd("rm -rf " + app_apk_dir_);
+ run_cmd("rm -rf " + app_private_dir_ce_);
+ run_cmd("rm -rf " + app_private_dir_de_);
+ }
+ delete service_;
+ }
+
+ void create_mock_app() {
+ // Create the oat dir.
+ std::string app_oat_dir = app_apk_dir_ + "/oat";
+ mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755);
+ service_->createOatDir(app_oat_dir, kRuntimeIsa);
+
+ // Copy the primary apk.
+ std::string apk_path = app_apk_dir_ + "/base.jar";
+ ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path, kSystemUid, kSystemGid, 0644));
+
+ // Create the app user data.
+ ASSERT_TRUE(service_->createAppData(
+ volume_uuid_,
+ package_name_,
+ kTestUserId,
+ kAppDataFlags,
+ kTestAppUid,
+ se_info_,
+ kOSdkVersion,
+ &ce_data_inode_).isOk());
+
+ // Create a secondary dex file on CE storage
+ const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str();
+ app_private_dir_ce_ = create_data_user_ce_package_path(
+ volume_uuid_cstr, kTestUserId, package_name_.c_str());
+ secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar";
+ ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_ce_, kTestAppUid, kTestAppGid, 0600));
+ std::string app_private_dir_ce_link = create_data_user_ce_package_path_as_user_link(
+ volume_uuid_cstr, kTestUserId, package_name_.c_str());
+ secondary_dex_ce_link_ = app_private_dir_ce_link + "/secondary_ce.jar";
+
+ // Create a secondary dex file on DE storage.
+ app_private_dir_de_ = create_data_user_de_package_path(
+ volume_uuid_cstr, kTestUserId, package_name_.c_str());
+ secondary_dex_de_ = app_private_dir_de_ + "/secondary_de.jar";
+ ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_de_, kTestAppUid, kTestAppGid, 0600));
+
+ // Fix app data uid.
+ ASSERT_TRUE(service_->fixupAppData(volume_uuid_, kTestUserId).isOk());
+ }
+
+
+ std::string GetSecondaryDexArtifact(const std::string& path, const std::string& type) {
+ std::string::size_type end = path.rfind('.');
+ std::string::size_type start = path.rfind('/', end);
+ return path.substr(0, start) + "/oat/" + kRuntimeIsa + "/" +
+ path.substr(start + 1, end - start) + type;
+ }
+
+ void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag,
+ bool should_binder_call_succeed, bool should_dex_be_compiled = true,
+ int32_t uid = -1) {
+ if (uid == -1) {
+ uid = kTestAppUid;
+ }
+ std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
+ int32_t dexopt_needed = 0; // does not matter;
+ std::unique_ptr<std::string> out_path = nullptr; // does not matter
+ int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
+ std::string compiler_filter = "speed-profile";
+ std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
+ std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+ bool downgrade = false;
+
+ binder::Status result = service_->dexopt(path,
+ uid,
+ package_name_ptr,
+ kRuntimeIsa,
+ dexopt_needed,
+ out_path,
+ dex_flags,
+ compiler_filter,
+ volume_uuid_,
+ class_loader_context_ptr,
+ se_info_ptr,
+ downgrade);
+ ASSERT_EQ(should_binder_call_succeed, result.isOk());
+ int expected_access = should_dex_be_compiled ? 0 : -1;
+ std::string odex = GetSecondaryDexArtifact(path, "odex");
+ std::string vdex = GetSecondaryDexArtifact(path, "vdex");
+ std::string art = GetSecondaryDexArtifact(path, "art");
+ ASSERT_EQ(expected_access, access(odex.c_str(), R_OK));
+ ASSERT_EQ(expected_access, access(vdex.c_str(), R_OK));
+ ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image.
+ }
+
+ void reconcile_secondary_dex(const std::string& path, int32_t storage_flag,
+ bool should_binder_call_succeed, bool should_dex_exist, bool should_dex_be_deleted,
+ int32_t uid = -1, std::string* package_override = nullptr) {
+ if (uid == -1) {
+ uid = kTestAppUid;
+ }
+ std::vector<std::string> isas;
+ isas.push_back(kRuntimeIsa);
+ bool out_secondary_dex_exists = false;
+ binder::Status result = service_->reconcileSecondaryDexFile(
+ path,
+ package_override == nullptr ? package_name_ : *package_override,
+ uid,
+ isas,
+ volume_uuid_,
+ storage_flag,
+ &out_secondary_dex_exists);
+
+ ASSERT_EQ(should_binder_call_succeed, result.isOk());
+ ASSERT_EQ(should_dex_exist, out_secondary_dex_exists);
+
+ int expected_access = should_dex_be_deleted ? -1 : 0;
+ std::string odex = GetSecondaryDexArtifact(path, "odex");
+ std::string vdex = GetSecondaryDexArtifact(path, "vdex");
+ std::string art = GetSecondaryDexArtifact(path, "art");
+ ASSERT_EQ(expected_access, access(odex.c_str(), F_OK));
+ ASSERT_EQ(expected_access, access(vdex.c_str(), F_OK));
+ ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image.
+ }
+
+ void CheckFileAccess(const std::string& file, uid_t uid, gid_t gid, mode_t mode) {
+ struct stat st;
+ ASSERT_EQ(0, stat(file.c_str(), &st));
+ ASSERT_EQ(uid, st.st_uid);
+ ASSERT_EQ(gid, st.st_gid);
+ ASSERT_EQ(mode, st.st_mode);
+ }
+};
+
+
+TEST_F(DexoptTest, DexoptSecondaryCe) {
+ LOG(INFO) << "DexoptSecondaryCe";
+ CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryCeLink) {
+ LOG(INFO) << "DexoptSecondaryCeLink";
+ CompileSecondaryDex(secondary_dex_ce_link_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryDe) {
+ LOG(INFO) << "DexoptSecondaryDe";
+ CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) {
+ LOG(INFO) << "DexoptSecondaryDoesNotExist";
+ // If the file validates but does not exist we do not treat it as an error.
+ CompileSecondaryDex(secondary_dex_ce_ + "not.there", DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryStorageValidationError) {
+ LOG(INFO) << "DexoptSecondaryStorageValidationError";
+ CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ false, /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) {
+ LOG(INFO) << "DexoptSecondaryAppOwnershipValidationError";
+ CompileSecondaryDex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE,
+ /*binder_ok*/ false, /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) {
+ LOG(INFO) << "DexoptSecondaryAcessViaDifferentUidError";
+ CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ false, /*compile_ok*/ false, kSystemUid);
+}
+
+
+class ReconcileTest : public DexoptTest {
+ virtual void SetUp() {
+ DexoptTest::SetUp();
+ CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+ CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ true, /*compile_ok*/ true);
+ }
+};
+
+TEST_F(ReconcileTest, ReconcileSecondaryCeExists) {
+ LOG(INFO) << "ReconcileSecondaryCeExists";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryCeLinkExists) {
+ LOG(INFO) << "ReconcileSecondaryCeLinkExists";
+ reconcile_secondary_dex(secondary_dex_ce_link_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryDeExists) {
+ LOG(INFO) << "ReconcileSecondaryDeExists";
+ reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE,
+ /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryDeDoesNotExist) {
+ LOG(INFO) << "ReconcileSecondaryDeDoesNotExist";
+ run_cmd("rm -rf " + secondary_dex_de_);
+ reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ true);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryStorageValidationError) {
+ // Validation errors will not clean the odex/vdex/art files but will mark
+ // the file as non existent so that the PM knows it should purge it from its
+ // records.
+ LOG(INFO) << "ReconcileSecondaryStorageValidationError";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_DE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryAppOwnershipValidationError) {
+ LOG(INFO) << "ReconcileSecondaryAppOwnershipValidationError";
+ // Attempt to reconcile the dex files of the test app from a different app.
+ std::string another_app = "another.app";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid, &another_app);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryAcessViaDifferentUidError) {
+ LOG(INFO) << "ReconcileSecondaryAcessViaDifferentUidError";
+ reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+ /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid);
+}
+
+class ProfileTest : public DexoptTest {
+ protected:
+ std::string cur_profile_;
+ std::string ref_profile_;
+ std::string snap_profile_;
+
+ virtual void SetUp() {
+ DexoptTest::SetUp();
+ cur_profile_ = create_current_profile_path(
+ kTestUserId, package_name_, /*is_secondary_dex*/ false);
+ ref_profile_ = create_reference_profile_path(package_name_, /*is_secondary_dex*/ false);
+ snap_profile_ = create_snapshot_profile_path(package_name_, "base.jar");
+ }
+
+ void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode, int32_t seed) {
+ run_cmd("profman --generate-test-profile-seed=" + std::to_string(seed) +
+ " --generate-test-profile-num-dex=2 --generate-test-profile=" + path);
+ ::chmod(path.c_str(), mode);
+ ::chown(path.c_str(), uid, gid);
+ }
+
+ void SetupProfiles(bool setup_ref) {
+ SetupProfile(cur_profile_, kTestAppUid, kTestAppGid, 0600, 1);
+ if (setup_ref) {
+ SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0060, 2);
+ }
+ }
+
+ void createProfileSnapshot(int32_t appid, const std::string& package_name,
+ bool expected_result) {
+ bool result;
+ binder::Status binder_result = service_->createProfileSnapshot(
+ appid, package_name, "base.jar", &result);
+ ASSERT_TRUE(binder_result.isOk());
+ ASSERT_EQ(expected_result, result);
+
+ if (!expected_result) {
+ // Do not check the files if we expect to fail.
+ return;
+ }
+
+ // Check that the snapshot was created witht he expected acess flags.
+ CheckFileAccess(snap_profile_, kSystemUid, kSystemGid, 0600 | S_IFREG);
+
+ // The snapshot should be equivalent to the merge of profiles.
+ std::string expected_profile_content = snap_profile_ + ".expected";
+ run_cmd("rm -f " + expected_profile_content);
+ run_cmd("touch " + expected_profile_content);
+ run_cmd("profman --profile-file=" + cur_profile_ +
+ " --profile-file=" + ref_profile_ +
+ " --reference-profile-file=" + expected_profile_content);
+
+ ASSERT_TRUE(AreFilesEqual(expected_profile_content, snap_profile_));
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child */
+ TransitionToSystemServer();
+
+ // System server should be able to open the the spanshot.
+ unique_fd fd(open(snap_profile_.c_str(), O_RDONLY));
+ ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno);
+ _exit(0);
+ }
+ /* parent */
+ ASSERT_TRUE(WIFEXITED(wait_child(pid)));
+ }
+
+ private:
+ void TransitionToSystemServer() {
+ ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid));
+ int32_t res = selinux_android_setcontext(
+ kSystemUid, true, se_info_.c_str(), "system_server");
+ ASSERT_EQ(0, res) << "Failed to setcon " << strerror(errno);
+ }
+
+ bool AreFilesEqual(const std::string& file1, const std::string& file2) {
+ std::vector<uint8_t> content1;
+ std::vector<uint8_t> content2;
+
+ if (!ReadAll(file1, &content1)) return false;
+ if (!ReadAll(file2, &content2)) return false;
+ return content1 == content2;
+ }
+
+ bool ReadAll(const std::string& file, std::vector<uint8_t>* content) {
+ unique_fd fd(open(file.c_str(), O_RDONLY));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open " << file;
+ return false;
+ }
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ PLOG(ERROR) << "Failed to stat " << file;
+ return false;
+ }
+ content->resize(st.st_size);
+ bool result = ReadFully(fd, content->data(), content->size());
+ if (!result) {
+ PLOG(ERROR) << "Failed to read " << file;
+ }
+ return result;
+ }
+};
+
+TEST_F(ProfileTest, ProfileSnapshotOk) {
+ LOG(INFO) << "ProfileSnapshotOk";
+
+ SetupProfiles(/*setup_ref*/ true);
+ createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true);
+}
+
+// The reference profile is created on the fly. We need to be able to
+// snapshot without one.
+TEST_F(ProfileTest, ProfileSnapshotOkNoReference) {
+ LOG(INFO) << "ProfileSnapshotOkNoReference";
+
+ SetupProfiles(/*setup_ref*/ false);
+ createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true);
+}
+
+TEST_F(ProfileTest, ProfileSnapshotFailWrongPackage) {
+ LOG(INFO) << "ProfileSnapshotFailWrongPackage";
+
+ SetupProfiles(/*setup_ref*/ true);
+ createProfileSnapshot(kTestAppId, "not.there", /*expected_result*/ false);
+}
+
+TEST_F(ProfileTest, ProfileSnapshotDestroySnapshot) {
+ LOG(INFO) << "ProfileSnapshotDestroySnapshot";
+
+ SetupProfiles(/*setup_ref*/ true);
+ createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true);
+
+ binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, "base.jar");
+ ASSERT_TRUE(binder_result.isOk());
+ struct stat st;
+ ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+TEST_F(ProfileTest, ProfileDirOk) {
+ LOG(INFO) << "ProfileDirOk";
+
+ std::string cur_profile_dir = create_primary_current_profile_package_dir_path(
+ kTestUserId, package_name_);
+ std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_,
+ /*is_secondary_dex*/false);
+ std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_);
+
+ CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
+ CheckFileAccess(cur_profile_file, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+ CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR);
+}
+
+// Verify that the profile directories are fixed up during an upgrade.
+// (The reference profile directory is prepared lazily).
+TEST_F(ProfileTest, ProfileDirOkAfterFixup) {
+ LOG(INFO) << "ProfileDirOkAfterFixup";
+
+ std::string cur_profile_dir = create_primary_current_profile_package_dir_path(
+ kTestUserId, package_name_);
+ std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_,
+ /*is_secondary_dex*/false);
+ std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_);
+
+ // Simulate a pre-P setup by changing the owner to kTestAppGid and permissions to 0700.
+ ASSERT_EQ(0, chown(ref_profile_dir.c_str(), kTestAppGid, kTestAppGid));
+ ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700));
+
+ // Run createAppData again which will offer to fix-up the profile directories.
+ ASSERT_TRUE(service_->createAppData(
+ volume_uuid_,
+ package_name_,
+ kTestUserId,
+ kAppDataFlags,
+ kTestAppUid,
+ se_info_,
+ kOSdkVersion,
+ &ce_data_inode_).isOk());
+
+ // Check the file access.
+ CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
+ CheckFileAccess(cur_profile_file, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+ CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 34818f6..ca812bd 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -25,6 +25,7 @@
#include <gtest/gtest.h>
#include "InstalldNativeService.h"
+#include "dexopt.h"
#include "globals.h"
#include "utils.h"
@@ -41,25 +42,18 @@
return property_get(key, value, default_value);
}
-bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
- const char *oat_dir ATTRIBUTE_UNUSED,
- const char *apk_path ATTRIBUTE_UNUSED,
- const char *instruction_set ATTRIBUTE_UNUSED) {
- return false;
-}
-
-bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
- const char *apk_path ATTRIBUTE_UNUSED,
- const char *instruction_set ATTRIBUTE_UNUSED) {
- return false;
-}
-
-bool create_cache_path(char path[PKG_PATH_MAX],
- const char *src,
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
const char *instruction_set) {
- // Not really a valid path but it's good enough for testing.
- sprintf(path,"/data/dalvik-cache/%s/%s", instruction_set, src);
- return true;
+ return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
+ const char *instruction_set) {
+ return calculate_odex_file_path_default(path, apk_path, instruction_set);
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) {
+ return create_cache_path_default(path, src, instruction_set);
}
static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
@@ -102,6 +96,8 @@
testUuid = std::make_unique<std::string>();
*testUuid = std::string(kTestUuid);
system("mkdir -p /data/local/tmp/user/0");
+
+ init_globals_from_data_and_root();
}
virtual void TearDown() {
@@ -153,12 +149,28 @@
EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
}
-TEST_F(ServiceTest, RmDexNoDalvikCache) {
- LOG(INFO) << "RmDexNoDalvikCache";
+TEST_F(ServiceTest, CalculateOat) {
+ char buf[PKG_PATH_MAX];
- // Try to remove a non existing dalvik cache dex. The call should be
- // successful because there's nothing to remove.
- EXPECT_TRUE(service->rmdex("com.example", "arm").isOk());
+ EXPECT_TRUE(calculate_oat_file_path(buf, "/path/to/oat", "/path/to/file.apk", "isa"));
+ EXPECT_EQ("/path/to/oat/isa/file.odex", std::string(buf));
+
+ EXPECT_FALSE(calculate_oat_file_path(buf, "/path/to/oat", "/path/to/file", "isa"));
+ EXPECT_FALSE(calculate_oat_file_path(buf, "/path/to/oat", "file", "isa"));
+}
+
+TEST_F(ServiceTest, CalculateOdex) {
+ char buf[PKG_PATH_MAX];
+
+ EXPECT_TRUE(calculate_odex_file_path(buf, "/path/to/file.apk", "isa"));
+ EXPECT_EQ("/path/to/oat/isa/file.odex", std::string(buf));
+}
+
+TEST_F(ServiceTest, CalculateCache) {
+ char buf[PKG_PATH_MAX];
+
+ EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa"));
+ EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf));
}
} // namespace installd
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 46ed85f..49da85d 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <string.h>
+#include <android-base/logging.h>
#include <gtest/gtest.h>
#include "InstalldNativeService.h"
@@ -27,6 +28,7 @@
#define LOG_TAG "utils_test"
#define TEST_DATA_DIR "/data/"
+#define TEST_ROOT_DIR "/system/"
#define TEST_APP_DIR "/data/app/"
#define TEST_APP_PRIVATE_DIR "/data/app-private/"
#define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/"
@@ -44,39 +46,13 @@
class UtilsTest : public testing::Test {
protected:
virtual void SetUp() {
- android_app_dir.path = (char*) TEST_APP_DIR;
- android_app_dir.len = strlen(TEST_APP_DIR);
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
- android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR;
- android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
-
- android_app_ephemeral_dir.path = (char*) TEST_APP_EPHEMERAL_DIR;
- android_app_ephemeral_dir.len = strlen(TEST_APP_EPHEMERAL_DIR);
-
- android_data_dir.path = (char*) TEST_DATA_DIR;
- android_data_dir.len = strlen(TEST_DATA_DIR);
-
- android_asec_dir.path = (char*) TEST_ASEC_DIR;
- android_asec_dir.len = strlen(TEST_ASEC_DIR);
-
- android_mnt_expand_dir.path = (char*) TEST_EXPAND_DIR;
- android_mnt_expand_dir.len = strlen(TEST_EXPAND_DIR);
-
- android_system_dirs.count = 2;
-
- android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
- android_system_dirs.dirs[0].path = (char*) TEST_SYSTEM_DIR1;
- android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1);
-
- android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2;
- android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
-
- android_profiles_dir.path = (char*) TEST_PROFILE_DIR;
- android_profiles_dir.len = strlen(TEST_PROFILE_DIR);
+ init_globals_from_data_and_root(TEST_DATA_DIR, TEST_ROOT_DIR);
}
virtual void TearDown() {
- free(android_system_dirs.dirs);
}
std::string create_too_long_path(const std::string& seed) {
@@ -192,12 +168,6 @@
<< badasec1 << " should be rejected as a invalid path";
}
-TEST_F(UtilsTest, IsValidApkPath_DoubleSlashFail) {
- const char *badasec2 = TEST_ASEC_DIR "com.example.asec//pkg.apk";
- EXPECT_EQ(-1, validate_apk_path(badasec2))
- << badasec2 << " should be rejected as a invalid path";
-}
-
TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeFail) {
const char *badasec3 = TEST_ASEC_DIR "com.example.asec/../../../pkg.apk";
EXPECT_EQ(-1, validate_apk_path(badasec3))
@@ -276,184 +246,6 @@
<< badapp2 << " should be rejected not a system path";
}
-TEST_F(UtilsTest, GetPathFromString_NullPathFail) {
- dir_rec_t test1;
- EXPECT_EQ(-1, get_path_from_string(&test1, (const char *) NULL))
- << "Should not allow NULL as a path.";
-}
-
-TEST_F(UtilsTest, GetPathFromString_EmptyPathFail) {
- dir_rec_t test1;
- EXPECT_EQ(-1, get_path_from_string(&test1, ""))
- << "Should not allow empty paths.";
-}
-
-TEST_F(UtilsTest, GetPathFromString_RelativePathFail) {
- dir_rec_t test1;
- EXPECT_EQ(-1, get_path_from_string(&test1, "mnt/asec"))
- << "Should not allow relative paths.";
-}
-
-TEST_F(UtilsTest, GetPathFromString_NonCanonical) {
- dir_rec_t test1;
-
- EXPECT_EQ(0, get_path_from_string(&test1, "/mnt/asec"))
- << "Should be able to canonicalize directory /mnt/asec";
- EXPECT_STREQ("/mnt/asec/", test1.path)
- << "/mnt/asec should be canonicalized to /mnt/asec/";
- EXPECT_EQ(10, (ssize_t) test1.len)
- << "path len should be equal to the length of /mnt/asec/ (10)";
- free(test1.path);
-}
-
-TEST_F(UtilsTest, GetPathFromString_CanonicalPath) {
- dir_rec_t test3;
- EXPECT_EQ(0, get_path_from_string(&test3, "/data/app/"))
- << "Should be able to canonicalize directory /data/app/";
- EXPECT_STREQ("/data/app/", test3.path)
- << "/data/app/ should be canonicalized to /data/app/";
- EXPECT_EQ(10, (ssize_t) test3.len)
- << "path len should be equal to the length of /data/app/ (10)";
- free(test3.path);
-}
-
-TEST_F(UtilsTest, CreatePkgPath_LongPkgNameSuccess) {
- char path[PKG_PATH_MAX];
-
- // Create long packagename of "aaaaa..."
- size_t pkgnameSize = PKG_NAME_MAX;
- char pkgname[pkgnameSize + 1];
- memset(pkgname, 'a', pkgnameSize);
- pkgname[1] = '.';
- pkgname[pkgnameSize] = '\0';
-
- EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
- << "Should successfully be able to create package name.";
-
- std::string prefix = std::string(TEST_DATA_DIR) + PRIMARY_USER_PREFIX;
- size_t offset = prefix.length();
-
- EXPECT_STREQ(pkgname, path + offset)
- << "Package path should be a really long string of a's";
-}
-
-TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) {
- char path[PKG_PATH_MAX];
-
- // Create long packagename of "aaaaa..."
- size_t postfixSize = PKG_PATH_MAX;
- char postfix[postfixSize + 1];
- memset(postfix, 'a', postfixSize);
- postfix[postfixSize] = '\0';
-
- EXPECT_EQ(-1, create_pkg_path(path, "com.example.package", postfix, 0))
- << "Should return error because postfix is too long.";
-}
-
-TEST_F(UtilsTest, CreatePkgPath_PrimaryUser) {
- char path[PKG_PATH_MAX];
-
- EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 0))
- << "Should return error because postfix is too long.";
-
- std::string p = std::string(TEST_DATA_DIR)
- + PRIMARY_USER_PREFIX
- + "com.example.package";
- EXPECT_STREQ(p.c_str(), path)
- << "Package path should be in /data/data/";
-}
-
-TEST_F(UtilsTest, CreatePkgPath_SecondaryUser) {
- char path[PKG_PATH_MAX];
-
- EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 1))
- << "Should successfully create package path.";
-
- std::string p = std::string(TEST_DATA_DIR)
- + SECONDARY_USER_PREFIX
- + "1/com.example.package";
- EXPECT_STREQ(p.c_str(), path)
- << "Package path should be in /data/user/";
-}
-
-TEST_F(UtilsTest, CreateMovePath_Primary) {
- char path[PKG_PATH_MAX];
-
- EXPECT_EQ(0, create_move_path(path, "com.android.test", "shared_prefs", 0))
- << "Should be able to create move path for primary user";
-
- EXPECT_STREQ("/data/data/com.android.test/shared_prefs", path)
- << "Primary user package directory should be created correctly";
-}
-
-
-TEST_F(UtilsTest, CreateMovePath_Fail_AppTooLong) {
- char path[PKG_PATH_MAX];
- std::string really_long_app_name = create_too_long_path("com.example");
- EXPECT_EQ(-1, create_move_path(path, really_long_app_name.c_str(), "shared_prefs", 0))
- << "Should fail to create move path for primary user";
-}
-
-TEST_F(UtilsTest, CreateMovePath_Fail_LeafTooLong) {
- char path[PKG_PATH_MAX];
- std::string really_long_leaf_name = create_too_long_path("leaf_");
- EXPECT_EQ(-1, create_move_path(path, "com.android.test", really_long_leaf_name.c_str(), 0))
- << "Should fail to create move path for primary user";
-}
-
-TEST_F(UtilsTest, CopyAndAppend_Normal) {
- //int copy_and_append(dir_rec_t* dst, dir_rec_t* src, char* suffix)
- dir_rec_t dst;
- dir_rec_t src;
-
- src.path = (char*) "/data/";
- src.len = strlen(src.path);
-
- EXPECT_EQ(0, copy_and_append(&dst, &src, "app/"))
- << "Should return error because postfix is too long.";
-
- EXPECT_STREQ("/data/app/", dst.path)
- << "Appended path should be correct";
-
- EXPECT_EQ(10, (ssize_t) dst.len)
- << "Appended path should be length of '/data/app/' (10)";
-}
-
-TEST_F(UtilsTest, AppendAndIncrement_Normal) {
- size_t dst_size = 10;
- char dst[dst_size];
- char *dstp = dst;
- const char* src = "FOO";
-
- EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
- << "String should append successfully";
-
- EXPECT_STREQ("FOO", dst)
- << "String should append correctly";
-
- EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
- << "String should append successfully again";
-
- EXPECT_STREQ("FOOFOO", dst)
- << "String should append correctly again";
-}
-
-TEST_F(UtilsTest, AppendAndIncrement_TooBig) {
- size_t dst_size = 5;
- char dst[dst_size];
- char *dstp = dst;
- const char* src = "FOO";
-
- EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
- << "String should append successfully";
-
- EXPECT_STREQ("FOO", dst)
- << "String should append correctly";
-
- EXPECT_EQ(-1, append_and_increment(&dstp, src, &dst_size))
- << "String should fail because it's too large to fit";
-}
-
TEST_F(UtilsTest, CreateDataPath) {
EXPECT_EQ("/data", create_data_path(nullptr));
EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b",
@@ -558,6 +350,12 @@
create_reference_profile_path("com.example", /*is_secondary*/false));
}
+TEST_F(UtilsTest, CreateProfileSnapshot) {
+ std::string expected =
+ create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof.snapshot";
+ EXPECT_EQ(expected, create_snapshot_profile_path("com.example", "base.apk"));
+}
+
TEST_F(UtilsTest, CreateSecondaryCurrentProfile) {
EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof",
create_current_profile_path(/*user*/0,
@@ -587,6 +385,7 @@
TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) {
std::string package_name = "com.test.app";
std::string app_dir_ce_user_0 = "/data/data/" + package_name;
+ std::string app_dir_ce_user_0_link = "/data/user/0/" + package_name;
std::string app_dir_ce_user_10 = "/data/user/10/" + package_name;
std::string app_dir_de_user_0 = "/data/user_de/0/" + package_name;
@@ -608,6 +407,8 @@
// Standard path for user 0 on CE storage.
pass_secondary_dex_validation(
package_name, app_dir_ce_user_0 + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+ pass_secondary_dex_validation(
+ package_name, app_dir_ce_user_0_link + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
// Standard path for user 10 on CE storage.
pass_secondary_dex_validation(
package_name, app_dir_ce_user_10 + "/ce10.dex", app_uid_for_user_10, FLAG_STORAGE_CE);
@@ -653,5 +454,29 @@
package_name, app_dir_ce_user_10 + "/" + too_long, app_uid_for_user_10, FLAG_STORAGE_CE);
}
+TEST_F(UtilsTest, ValidateApkPath) {
+ EXPECT_EQ(0, validate_apk_path("/data/app/com.example"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file"));
+ EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/"));
+ EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file"));
+ EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file"));
+ EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir//file"));
+}
+
+TEST_F(UtilsTest, ValidateApkPathSubdirs) {
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example//file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h
new file mode 100644
index 0000000..b8785c6
--- /dev/null
+++ b/cmds/installd/tests/test_utils.h
@@ -0,0 +1,134 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+
+#include <android-base/logging.h>
+#include <selinux/android.h>
+
+uint8_t kBase64Map[256] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255
+};
+
+uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+ CHECK(dst_size != nullptr);
+ std::vector<uint8_t> tmp;
+ uint32_t t = 0, y = 0;
+ int g = 3;
+ for (size_t i = 0; src[i] != '\0'; ++i) {
+ uint8_t c = kBase64Map[src[i] & 0xFF];
+ if (c == 255) continue;
+ // the final = symbols are read and used to trim the remaining bytes
+ if (c == 254) {
+ c = 0;
+ // prevent g < 0 which would potentially allow an overflow later
+ if (--g < 0) {
+ *dst_size = 0;
+ return nullptr;
+ }
+ } else if (g != 3) {
+ // we only allow = to be at the end
+ *dst_size = 0;
+ return nullptr;
+ }
+ t = (t << 6) | c;
+ if (++y == 4) {
+ tmp.push_back((t >> 16) & 255);
+ if (g > 1) {
+ tmp.push_back((t >> 8) & 255);
+ }
+ if (g > 2) {
+ tmp.push_back(t & 255);
+ }
+ y = t = 0;
+ }
+ }
+ if (y != 0) {
+ *dst_size = 0;
+ return nullptr;
+ }
+ std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
+ *dst_size = tmp.size();
+ std::copy(tmp.begin(), tmp.end(), dst.get());
+ return dst.release();
+}
+
+bool WriteBase64ToFile(const char* base64, const std::string& file,
+ uid_t uid, gid_t gid, int mode) {
+ CHECK(base64 != nullptr);
+ size_t length;
+ std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length));
+ CHECK(bytes != nullptr);
+
+
+ int fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+ if (fd < 0) {
+ PLOG(ERROR) << "Could not open file " << file;
+ return false;
+ }
+
+ size_t wrote = 0;
+ while (wrote < length) {
+ ssize_t cur = write(fd, bytes.get() + wrote, length - wrote);
+ if (cur == -1) {
+ PLOG(ERROR) << "Could not write file " << file;
+ return false;
+ }
+ wrote += cur;
+ }
+
+ if (::chown(file.c_str(), uid, gid) != 0) {
+ PLOG(ERROR) << "Could not chown file " << file;
+ return false;
+ }
+ if (::chmod(file.c_str(), mode) != 0) {
+ PLOG(ERROR) << "Could not chmod file " << file;
+ return false;
+ }
+ return true;
+}
+
+// TODO(calin): fix dexopt drop_capabilities and move to general utils (b/69678790).
+bool DropCapabilities(uid_t uid, gid_t gid) {
+ if (setgid(gid) != 0) {
+ PLOG(ERROR) << "setgid failed: " << gid;
+ return false;
+ }
+ if (setuid(uid) != 0) {
+ PLOG(ERROR) << "setuid failed: " << uid;
+ return false;
+ }
+ // drop capabilities
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ PLOG(ERROR) << "capset failed";
+ return false;
+ }
+
+ return true;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index dd32ac6..61c9c8f 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -87,6 +87,20 @@
create_data_user_ce_path(volume_uuid, user).c_str(), package_name);
}
+/**
+ * Create the path name where package data should be stored for the given
+ * volume UUID, package name, and user ID. An empty UUID is assumed to be
+ * internal storage.
+ * Compared to create_data_user_ce_package_path this method always return the
+ * ".../user/..." directory.
+ */
+std::string create_data_user_ce_package_path_as_user_link(
+ const char* volume_uuid, userid_t userid, const char* package_name) {
+ check_package_name(package_name);
+ std::string data(create_data_path(volume_uuid));
+ return StringPrintf("%s/user/%u/%s", data.c_str(), userid, package_name);
+}
+
std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user,
const char* package_name, ino_t ce_data_inode) {
// For testing purposes, rely on the inode when defined; this could be
@@ -129,24 +143,6 @@
create_data_user_de_path(volume_uuid, user).c_str(), package_name);
}
-int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname,
- const char *postfix, userid_t userid) {
- if (!is_valid_package_name(pkgname)) {
- path[0] = '\0';
- return -1;
- }
-
- std::string _tmp(create_data_user_ce_package_path(nullptr, userid, pkgname) + postfix);
- const char* tmp = _tmp.c_str();
- if (strlen(tmp) >= PKG_PATH_MAX) {
- path[0] = '\0';
- return -1;
- } else {
- strcpy(path, tmp);
- return 0;
- }
-}
-
std::string create_data_path(const char* volume_uuid) {
if (volume_uuid == nullptr) {
return "/data";
@@ -213,7 +209,7 @@
}
std::string create_primary_cur_profile_dir_path(userid_t userid) {
- return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
+ return StringPrintf("%s/cur/%u", android_profiles_dir.c_str(), userid);
}
std::string create_primary_current_profile_package_dir_path(userid_t user,
@@ -224,12 +220,12 @@
}
std::string create_primary_ref_profile_dir_path() {
- return StringPrintf("%s/ref", android_profiles_dir.path);
+ return StringPrintf("%s/ref", android_profiles_dir.c_str());
}
std::string create_primary_reference_profile_package_dir_path(const std::string& package_name) {
check_package_name(package_name.c_str());
- return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name.c_str());
+ return StringPrintf("%s/ref/%s", android_profiles_dir.c_str(), package_name.c_str());
}
std::string create_data_dalvik_cache_path() {
@@ -240,6 +236,7 @@
const std::string PROFILE_EXT = ".prof";
const std::string CURRENT_PROFILE_EXT = ".cur";
const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT;
+const std::string SNAPSHOT_PROFILE_EXT = ".snapshot";
// Gets the parent directory and the file name for the given secondary dex path.
// Returns true on success, false on failure (if the dex_path does not have the expected
@@ -293,6 +290,14 @@
}
}
+std::string create_snapshot_profile_path(const std::string& package,
+ const std::string& code_path ATTRIBUTE_UNUSED) {
+ // TODD(calin): code_path is ignored for now. It will be used when each split gets its own
+ // profile file.
+ std::string ref_profile = create_reference_profile_path(package, /*is_secondary_dex*/ false);
+ return ref_profile + SNAPSHOT_PROFILE_EXT;
+}
+
std::vector<userid_t> get_known_users(const char* volume_uuid) {
std::vector<userid_t> users;
@@ -378,20 +383,6 @@
return 0;
}
-int create_move_path(char path[PKG_PATH_MAX],
- const char* pkgname,
- const char* leaf,
- userid_t userid ATTRIBUTE_UNUSED)
-{
- if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1)
- >= PKG_PATH_MAX) {
- return -1;
- }
-
- sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf);
- return 0;
-}
-
/**
* Checks whether the package name is valid. Returns -1 on error and
* 0 on success.
@@ -756,27 +747,47 @@
}
}
+void remove_path_xattr(const std::string& path, const char* inode_xattr) {
+ if (removexattr(path.c_str(), inode_xattr) && errno != ENODATA) {
+ PLOG(ERROR) << "Failed to remove xattr " << inode_xattr << " at " << path;
+ }
+}
+
/**
* Validate that the path is valid in the context of the provided directory.
* The path is allowed to have at most one subdirectory and no indirections
* to top level directories (i.e. have "..").
*/
-static int validate_path(const dir_rec_t* dir, const char* path, int maxSubdirs) {
- size_t dir_len = dir->len;
- const char* subdir = strchr(path + dir_len, '/');
-
- // Only allow the path to have at most one subdirectory.
- if (subdir != NULL) {
- ++subdir;
- if ((--maxSubdirs == 0) && strchr(subdir, '/') != NULL) {
- ALOGE("invalid apk path '%s' (subdir?)\n", path);
- return -1;
- }
+static int validate_path(const std::string& dir, const std::string& path, int maxSubdirs) {
+ // Argument sanity checking
+ if (dir.find('/') != 0 || dir.rfind('/') != dir.size() - 1
+ || dir.find("..") != std::string::npos) {
+ LOG(ERROR) << "Invalid directory " << dir;
+ return -1;
+ }
+ if (path.find("..") != std::string::npos) {
+ LOG(ERROR) << "Invalid path " << path;
+ return -1;
}
- // Directories can't have a period directly after the directory markers to prevent "..".
- if ((path[dir_len] == '.') || ((subdir != NULL) && (*subdir == '.'))) {
- ALOGE("invalid apk path '%s' (trickery)\n", path);
+ if (path.compare(0, dir.size(), dir) != 0) {
+ // Common case, path isn't under directory
+ return -1;
+ }
+
+ // Count number of subdirectories
+ auto pos = path.find('/', dir.size());
+ int count = 0;
+ while (pos != std::string::npos) {
+ auto next = path.find('/', pos + 1);
+ if (next > pos + 1) {
+ count++;
+ }
+ pos = next;
+ }
+
+ if (count > maxSubdirs) {
+ LOG(ERROR) << "Invalid path depth " << path << " when tested against " << dir;
return -1;
}
@@ -788,20 +799,17 @@
* if it is a system app or -1 if it is not.
*/
int validate_system_app_path(const char* path) {
- size_t i;
-
- for (i = 0; i < android_system_dirs.count; i++) {
- const size_t dir_len = android_system_dirs.dirs[i].len;
- if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) {
- return validate_path(android_system_dirs.dirs + i, path, 1);
+ std::string path_ = path;
+ for (const auto& dir : android_system_dirs) {
+ if (validate_path(dir, path, 1) == 0) {
+ return 0;
}
}
-
return -1;
}
bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
- const char* volume_uuid, int uid, int storage_flag, bool validate_package_path) {
+ const char* volume_uuid, int uid, int storage_flag) {
CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
// Empty paths are not allowed.
@@ -815,16 +823,20 @@
// The path should be at most PKG_PATH_MAX long.
if (dex_path.size() > PKG_PATH_MAX) { return false; }
- if (validate_package_path) {
- // If we are asked to validate the package path check that
- // the dex_path is under the app data directory.
- std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+ // The dex_path should be under the app data directory.
+ std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
? create_data_user_ce_package_path(
volume_uuid, multiuser_get_user_id(uid), pkgname.c_str())
: create_data_user_de_package_path(
volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
- if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+ if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+ // The check above might fail if the dex file is accessed via the /data/user/0 symlink.
+ // If that's the case, attempt to validate against the user data link.
+ std::string app_private_dir_symlink = create_data_user_ce_package_path_as_user_link(
+ volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
+ if (strncmp(dex_path.c_str(), app_private_dir_symlink.c_str(),
+ app_private_dir_symlink.size()) != 0) {
return false;
}
}
@@ -834,116 +846,26 @@
}
/**
- * Get the contents of a environment variable that contains a path. Caller
- * owns the string that is inserted into the directory record. Returns
- * 0 on success and -1 on error.
- */
-int get_path_from_env(dir_rec_t* rec, const char* var) {
- const char* path = getenv(var);
- int ret = get_path_from_string(rec, path);
- if (ret < 0) {
- ALOGW("Problem finding value for environment variable %s\n", var);
- }
- return ret;
-}
-
-/**
- * Puts the string into the record as a directory. Appends '/' to the end
- * of all paths. Caller owns the string that is inserted into the directory
- * record. A null value will result in an error.
- *
- * Returns 0 on success and -1 on error.
- */
-int get_path_from_string(dir_rec_t* rec, const char* path) {
- if (path == NULL) {
- return -1;
- } else {
- const size_t path_len = strlen(path);
- if (path_len <= 0) {
- return -1;
- }
-
- // Make sure path is absolute.
- if (path[0] != '/') {
- return -1;
- }
-
- if (path[path_len - 1] == '/') {
- // Path ends with a forward slash. Make our own copy.
-
- rec->path = strdup(path);
- if (rec->path == NULL) {
- return -1;
- }
-
- rec->len = path_len;
- } else {
- // Path does not end with a slash. Generate a new string.
- char *dst;
-
- // Add space for slash and terminating null.
- size_t dst_size = path_len + 2;
-
- rec->path = (char*) malloc(dst_size);
- if (rec->path == NULL) {
- return -1;
- }
-
- dst = rec->path;
-
- if (append_and_increment(&dst, path, &dst_size) < 0
- || append_and_increment(&dst, "/", &dst_size)) {
- ALOGE("Error canonicalizing path");
- return -1;
- }
-
- rec->len = dst - rec->path;
- }
- }
- return 0;
-}
-
-int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) {
- dst->len = src->len + strlen(suffix);
- const size_t dstSize = dst->len + 1;
- dst->path = (char*) malloc(dstSize);
-
- if (dst->path == NULL
- || snprintf(dst->path, dstSize, "%s%s", src->path, suffix)
- != (ssize_t) dst->len) {
- ALOGE("Could not allocate memory to hold appended path; aborting\n");
- return -1;
- }
-
- return 0;
-}
-
-/**
* Check whether path points to a valid path for an APK file. The path must
* begin with a whitelisted prefix path and must be no deeper than |maxSubdirs| within
* that path. Returns -1 when an invalid path is encountered and 0 when a valid path
* is encountered.
*/
static int validate_apk_path_internal(const char *path, int maxSubdirs) {
- const dir_rec_t* dir = NULL;
- if (!strncmp(path, android_app_dir.path, android_app_dir.len)) {
- dir = &android_app_dir;
- } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) {
- dir = &android_app_private_dir;
- } else if (!strncmp(path, android_app_ephemeral_dir.path, android_app_ephemeral_dir.len)) {
- dir = &android_app_ephemeral_dir;
- } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) {
- dir = &android_asec_dir;
- } else if (!strncmp(path, android_mnt_expand_dir.path, android_mnt_expand_dir.len)) {
- dir = &android_mnt_expand_dir;
- if (maxSubdirs < 2) {
- maxSubdirs = 2;
- }
+ std::string path_ = path;
+ if (validate_path(android_app_dir, path_, maxSubdirs) == 0) {
+ return 0;
+ } else if (validate_path(android_app_private_dir, path_, maxSubdirs) == 0) {
+ return 0;
+ } else if (validate_path(android_app_ephemeral_dir, path_, maxSubdirs) == 0) {
+ return 0;
+ } else if (validate_path(android_asec_dir, path_, maxSubdirs) == 0) {
+ return 0;
+ } else if (validate_path(android_mnt_expand_dir, path_, std::max(maxSubdirs, 2)) == 0) {
+ return 0;
} else {
return -1;
}
-
- return validate_path(dir, path, maxSubdirs);
}
int validate_apk_path(const char* path) {
@@ -954,48 +876,6 @@
return validate_apk_path_internal(path, 3 /* maxSubdirs */);
}
-int append_and_increment(char** dst, const char* src, size_t* dst_size) {
- ssize_t ret = strlcpy(*dst, src, *dst_size);
- if (ret < 0 || (size_t) ret >= *dst_size) {
- return -1;
- }
- *dst += ret;
- *dst_size -= ret;
- return 0;
-}
-
-char *build_string2(const char *s1, const char *s2) {
- if (s1 == NULL || s2 == NULL) return NULL;
-
- int len_s1 = strlen(s1);
- int len_s2 = strlen(s2);
- int len = len_s1 + len_s2 + 1;
- char *result = (char *) malloc(len);
- if (result == NULL) return NULL;
-
- strcpy(result, s1);
- strcpy(result + len_s1, s2);
-
- return result;
-}
-
-char *build_string3(const char *s1, const char *s2, const char *s3) {
- if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL;
-
- int len_s1 = strlen(s1);
- int len_s2 = strlen(s2);
- int len_s3 = strlen(s3);
- int len = len_s1 + len_s2 + len_s3 + 1;
- char *result = (char *) malloc(len);
- if (result == NULL) return NULL;
-
- strcpy(result, s1);
- strcpy(result + len_s1, s2);
- strcpy(result + len_s1 + len_s2, s3);
-
- return result;
-}
-
int ensure_config_user_dirs(userid_t userid) {
// writable by system, readable by any app within the same user
const int uid = multiuser_get_uid(userid, AID_SYSTEM);
@@ -1068,7 +948,7 @@
} else {
// Mismatched GID/mode is recoverable; fall through to update
LOG(DEBUG) << "Mismatched cache GID/mode at " << path << ": found " << st.st_gid
- << " but expected " << gid;
+ << "/" << actual_mode << " but expected " << gid << "/" << target_mode;
}
// Directory is owned correctly, but GID or mode mismatch means it's
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index e938042..5391061 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -41,18 +41,11 @@
namespace android {
namespace installd {
-struct dir_rec_t;
-
constexpr const char* kXattrInodeCache = "user.inode_cache";
constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache";
constexpr const char* kXattrCacheGroup = "user.cache_group";
constexpr const char* kXattrCacheTombstone = "user.cache_tombstone";
-int create_pkg_path(char path[PKG_PATH_MAX],
- const char *pkgname,
- const char *postfix,
- userid_t userid);
-
std::string create_data_path(const char* volume_uuid);
std::string create_data_app_path(const char* volume_uuid);
@@ -67,6 +60,8 @@
userid_t user, const char* package_name, ino_t ce_data_inode);
std::string create_data_user_de_package_path(const char* volume_uuid,
userid_t user, const char* package_name);
+std::string create_data_user_ce_package_path_as_user_link(
+ const char* volume_uuid, userid_t userid, const char* package_name);
std::string create_data_media_path(const char* volume_uuid, userid_t userid);
std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
@@ -88,6 +83,7 @@
userid_t user, const std::string& package_name, bool is_secondary_dex);
std::string create_reference_profile_path(
const std::string& package_name, bool is_secondary_dex);
+std::string create_snapshot_profile_path(const std::string& package, const std::string& code_path);
std::vector<userid_t> get_known_users(const char* volume_uuid);
@@ -96,11 +92,6 @@
int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
-int create_move_path(char path[PKG_PATH_MAX],
- const char* pkgname,
- const char* leaf,
- userid_t userid);
-
bool is_valid_filename(const std::string& name);
bool is_valid_package_name(const std::string& packageName);
@@ -122,25 +113,15 @@
int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
+void remove_path_xattr(const std::string& path, const char* inode_xattr);
int validate_system_app_path(const char* path);
bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
- const char* volume_uuid, int uid, int storage_flag, bool validate_package_path = true);
-
-int get_path_from_env(dir_rec_t* rec, const char* var);
-
-int get_path_from_string(dir_rec_t* rec, const char* path);
-
-int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix);
+ const char* volume_uuid, int uid, int storage_flag);
int validate_apk_path(const char *path);
int validate_apk_path_subdirs(const char *path);
-int append_and_increment(char** dst, const char* src, size_t* dst_size);
-
-char *build_string2(const char *s1, const char *s2);
-char *build_string3(const char *s1, const char *s2, const char *s3);
-
int ensure_config_user_dirs(userid_t userid);
int wait_child(pid_t pid);
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
index 36bbdf5..e1e2204 100644
--- a/cmds/ip-up-vpn/Android.mk
+++ b/cmds/ip-up-vpn/Android.mk
@@ -18,6 +18,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := ip-up-vpn.c
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_SHARED_LIBRARIES := libcutils liblog
LOCAL_MODULE := ip-up-vpn
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 67b5b46..409c206 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -20,16 +20,24 @@
"libutils",
"libhidlbase",
"libhidltransport",
+ "libhidl-gen-hash",
"libhidl-gen-utils",
"libvintf",
],
srcs: [
"DebugCommand.cpp",
+ "HelpCommand.cpp",
"Lshal.cpp",
"ListCommand.cpp",
"PipeRelay.cpp",
+ "TableEntry.cpp",
+ "TextTable.cpp",
"utils.cpp",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_defaults {
@@ -40,7 +48,8 @@
"libhidltransport",
"liblshal",
"libutils",
- ]
+ ],
+ cflags: ["-Wall", "-Werror"],
}
cc_binary {
@@ -59,6 +68,7 @@
"libgmock"
],
shared_libs: [
+ "libvintf",
"android.hardware.tests.baz@1.0"
],
srcs: [
diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h
new file mode 100644
index 0000000..4f128ab
--- /dev/null
+++ b/cmds/lshal/Command.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_
+
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+// Base class for all *Commands
+class Command {
+public:
+ Command(Lshal& lshal) : mLshal(lshal) {}
+ virtual ~Command() = default;
+ // Expect optind to be set by Lshal::main and points to the next argument
+ // to process.
+ virtual Status main(const Arg &arg) = 0;
+
+ virtual void usage() const = 0;
+
+ // e.g. "list"
+ virtual std::string getName() const = 0;
+
+ // e.g. "list HALs"
+ virtual std::string getSimpleDescription() const = 0;
+
+protected:
+ Lshal& mLshal;
+};
+
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
index 672cad6..622f866 100644
--- a/cmds/lshal/DebugCommand.cpp
+++ b/cmds/lshal/DebugCommand.cpp
@@ -21,12 +21,16 @@
namespace android {
namespace lshal {
-DebugCommand::DebugCommand(Lshal &lshal) : mLshal(lshal) {
+std::string DebugCommand::getName() const {
+ return "debug";
}
-Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) {
+std::string DebugCommand::getSimpleDescription() const {
+ return "Debug a specified HAL.";
+}
+
+Status DebugCommand::parseArgs(const Arg &arg) {
if (optind >= arg.argc) {
- mLshal.usage(command);
return USAGE;
}
mInterfaceName = arg.argv[optind];
@@ -37,8 +41,8 @@
return OK;
}
-Status DebugCommand::main(const std::string &command, const Arg &arg) {
- Status status = parseArgs(command, arg);
+Status DebugCommand::main(const Arg &arg) {
+ Status status = parseArgs(arg);
if (status != OK) {
return status;
}
@@ -49,6 +53,19 @@
mLshal.err());
}
+void DebugCommand::usage() const {
+
+ static const std::string debug =
+ "debug:\n"
+ " lshal debug <interface> [options [options [...]]] \n"
+ " Print debug information of a specified interface.\n"
+ " <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n"
+ " If instance name is missing `default` is used.\n"
+ " options: space separated options to IBase::debug.\n";
+
+ mLshal.err() << debug;
+}
+
} // namespace lshal
} // namespace android
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
index fa0f0fa..9b91084 100644
--- a/cmds/lshal/DebugCommand.h
+++ b/cmds/lshal/DebugCommand.h
@@ -21,6 +21,7 @@
#include <android-base/macros.h>
+#include "Command.h"
#include "utils.h"
namespace android {
@@ -28,14 +29,17 @@
class Lshal;
-class DebugCommand {
+class DebugCommand : public Command {
public:
- DebugCommand(Lshal &lshal);
- Status main(const std::string &command, const Arg &arg);
+ DebugCommand(Lshal &lshal) : Command(lshal) {}
+ ~DebugCommand() = default;
+ Status main(const Arg &arg) override;
+ void usage() const override;
+ std::string getSimpleDescription() const override;
+ std::string getName() const override;
private:
- Status parseArgs(const std::string &command, const Arg &arg);
+ Status parseArgs(const Arg &arg);
- Lshal &mLshal;
std::string mInterfaceName;
std::vector<std::string> mOptions;
diff --git a/cmds/lshal/HelpCommand.cpp b/cmds/lshal/HelpCommand.cpp
new file mode 100644
index 0000000..6773ace
--- /dev/null
+++ b/cmds/lshal/HelpCommand.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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 "HelpCommand.h"
+
+#include "Lshal.h"
+
+namespace android {
+namespace lshal {
+
+std::string HelpCommand::GetName() {
+ return "help";
+}
+
+std::string HelpCommand::getSimpleDescription() const {
+ return "Print help message.";
+}
+
+Status HelpCommand::main(const Arg &arg) {
+ if (optind >= arg.argc) {
+ // `lshal help` prints global usage.
+ mLshal.usage();
+ return OK;
+ }
+ (void)usageOfCommand(arg.argv[optind]);
+ return OK;
+}
+
+Status HelpCommand::usageOfCommand(const std::string& c) const {
+ if (c.empty()) {
+ mLshal.usage();
+ return USAGE;
+ }
+ auto command = mLshal.selectCommand(c);
+ if (command == nullptr) {
+ // from HelpCommand::main, `lshal help unknown`
+ mLshal.usage();
+ return USAGE;
+ }
+
+ command->usage();
+ return USAGE;
+
+}
+
+void HelpCommand::usage() const {
+ mLshal.err()
+ << "help:" << std::endl
+ << " lshal -h" << std::endl
+ << " lshal --help" << std::endl
+ << " lshal help" << std::endl
+ << " Print this help message" << std::endl;
+ mLshal.forEachCommand([&](const Command* e) {
+ mLshal.err() << " lshal help " << e->getName() << std::endl
+ << " Print help message for " << e->getName() << std::endl;
+ });
+
+}
+
+} // namespace lshal
+} // namespace android
+
diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h
new file mode 100644
index 0000000..cc709f8
--- /dev/null
+++ b/cmds/lshal/HelpCommand.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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 FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_
+
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "Command.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+class HelpCommand : public Command {
+public:
+ HelpCommand(Lshal &lshal) : Command(lshal) {}
+ ~HelpCommand() = default;
+ Status main(const Arg &arg) override;
+ void usage() const override;
+ std::string getSimpleDescription() const override;
+ std::string getName() const override { return GetName(); }
+ static std::string GetName();
+ Status usageOfCommand(const std::string& c) const;
+};
+
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 7c6cfd9..7399692 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -27,6 +27,7 @@
#include <android-base/parseint.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl-hash/Hash.h>
#include <hidl-util/FQName.h>
#include <private/android_filesystem_config.h>
#include <sys/stat.h>
@@ -39,15 +40,30 @@
#include "utils.h"
using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hidl::base::V1_0::DebugInfo;
+using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
namespace android {
namespace lshal {
-ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) {
+NullableOStream<std::ostream> ListCommand::out() const {
+ return mLshal.out();
}
-std::string getCmdline(pid_t pid) {
+NullableOStream<std::ostream> ListCommand::err() const {
+ return mLshal.err();
+}
+
+std::string ListCommand::GetName() {
+ return "list";
+}
+std::string ListCommand::getSimpleDescription() const {
+ return "List HALs.";
+}
+
+std::string ListCommand::parseCmdline(pid_t pid) const {
std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
std::string cmdline;
if (!ifs.is_open()) {
@@ -62,7 +78,7 @@
if (pair != mCmdlines.end()) {
return pair->second;
}
- mCmdlines[pid] = ::android::lshal::getCmdline(pid);
+ mCmdlines[pid] = parseCmdline(pid);
return mCmdlines[pid];
}
@@ -73,7 +89,7 @@
}), pids->end());
}
-bool scanBinderContext(pid_t pid,
+static bool scanBinderContext(pid_t pid,
const std::string &contextName,
std::function<void(const std::string&)> eachLine) {
std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
@@ -113,7 +129,7 @@
uint64_t ptr;
if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
// Should not reach here, but just be tolerant.
- mErr << "Could not parse number " << ptrString << std::endl;
+ err() << "Could not parse number " << ptrString << std::endl;
return;
}
const std::string proc = " proc ";
@@ -122,7 +138,7 @@
for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
int32_t pid;
if (!::android::base::ParseInt(pidStr, &pid)) {
- mErr << "Could not parse number " << pidStr << std::endl;
+ err() << "Could not parse number " << pidStr << std::endl;
return;
}
pidInfo->refPids[ptr].push_back(pid);
@@ -159,6 +175,16 @@
});
}
+const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
+ auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
+ if (pair.second /* did insertion take place? */) {
+ if (!getPidInfo(serverPid, &pair.first->second)) {
+ return nullptr;
+ }
+ }
+ return &pair.first->second;
+}
+
// Must process hwbinder services first, then passthrough services.
void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
f(mServicesTable);
@@ -204,44 +230,18 @@
}
}
}
-}
-void ListCommand::printLine(
- const std::string &interfaceName,
- const std::string &transport,
- const std::string &arch,
- const std::string &threadUsage,
- const std::string &server,
- const std::string &serverCmdline,
- const std::string &address,
- const std::string &clients,
- const std::string &clientCmdlines) const {
- if (mSelectedColumns & ENABLE_INTERFACE_NAME)
- mOut << std::setw(80) << interfaceName << "\t";
- if (mSelectedColumns & ENABLE_TRANSPORT)
- mOut << std::setw(10) << transport << "\t";
- if (mSelectedColumns & ENABLE_ARCH)
- mOut << std::setw(5) << arch << "\t";
- if (mSelectedColumns & ENABLE_THREADS) {
- mOut << std::setw(8) << threadUsage << "\t";
- }
- if (mSelectedColumns & ENABLE_SERVER_PID) {
- if (mEnableCmdlines) {
- mOut << std::setw(15) << serverCmdline << "\t";
- } else {
- mOut << std::setw(5) << server << "\t";
- }
- }
- if (mSelectedColumns & ENABLE_SERVER_ADDR)
- mOut << std::setw(16) << address << "\t";
- if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
- if (mEnableCmdlines) {
- mOut << std::setw(0) << clientCmdlines;
- } else {
- mOut << std::setw(0) << clients;
- }
- }
- mOut << std::endl;
+ mServicesTable.setDescription(
+ "All binderized services (registered services through hwservicemanager)");
+ mPassthroughRefTable.setDescription(
+ "All interfaces that getService() has ever return as a passthrough interface;\n"
+ "PIDs / processes shown below might be inaccurate because the process\n"
+ "might have relinquished the interface or might have died.\n"
+ "The Server / Server CMD column can be ignored.\n"
+ "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+ "the library and successfully fetched the passthrough implementation.");
+ mImplementationsTable.setDescription(
+ "All available passthrough implementations (all -impl.so files)");
}
static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
@@ -254,9 +254,9 @@
return false;
}
-void ListCommand::dumpVintf() const {
+void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
using vintf::operator|=;
- mOut << "<!-- " << std::endl
+ out << "<!-- " << std::endl
<< " This is a skeleton device manifest. Notes: " << std::endl
<< " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
<< " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
@@ -283,7 +283,7 @@
auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
FQName fqName(splittedFqInstanceName.first);
if (!fqName.isValid()) {
- mErr << "Warning: '" << splittedFqInstanceName.first
+ err() << "Warning: '" << splittedFqInstanceName.first
<< "' is not a valid FQName." << std::endl;
continue;
}
@@ -316,12 +316,12 @@
arch = vintf::Arch::ARCH_32_64; break;
case lshal::ARCH_UNKNOWN: // fallthrough
default:
- mErr << "Warning: '" << fqName.package()
+ err() << "Warning: '" << fqName.package()
<< "' doesn't have bitness info, assuming 32+64." << std::endl;
arch = vintf::Arch::ARCH_32_64;
}
} else {
- mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
+ err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
continue;
}
@@ -329,7 +329,7 @@
for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
if (hal->transport() != transport) {
if (transport != vintf::Transport::PASSTHROUGH) {
- mErr << "Fatal: should not reach here. Generated result may be wrong for '"
+ err() << "Fatal: should not reach here. Generated result may be wrong for '"
<< hal->name << "'."
<< std::endl;
}
@@ -360,29 +360,11 @@
.versions = {version},
.transportArch = {transport, arch},
.interfaces = interfaces})) {
- mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
+ err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
}
}
});
- mOut << vintf::gHalManifestConverter(manifest);
-}
-
-static const std::string &getArchString(Architecture arch) {
- static const std::string sStr64 = "64";
- static const std::string sStr32 = "32";
- static const std::string sStrBoth = "32+64";
- static const std::string sStrUnknown = "";
- switch (arch) {
- case ARCH64:
- return sStr64;
- case ARCH32:
- return sStr32;
- case ARCH_BOTH:
- return sStrBoth;
- case ARCH_UNKNOWN: // fall through
- default:
- return sStrUnknown;
- }
+ out << vintf::gHalManifestConverter(manifest);
}
static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
@@ -397,68 +379,54 @@
}
}
-void ListCommand::dumpTable() {
- mServicesTable.description =
- "All binderized services (registered services through hwservicemanager)";
- mPassthroughRefTable.description =
- "All interfaces that getService() has ever return as a passthrough interface;\n"
- "PIDs / processes shown below might be inaccurate because the process\n"
- "might have relinquished the interface or might have died.\n"
- "The Server / Server CMD column can be ignored.\n"
- "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
- "the library and successfully fetched the passthrough implementation.";
- mImplementationsTable.description =
- "All available passthrough implementations (all -impl.so files)";
- forEachTable([this] (const Table &table) {
- if (!mNeat) {
- mOut << table.description << std::endl;
- }
- mOut << std::left;
- if (!mNeat) {
- printLine("Interface", "Transport", "Arch", "Thread Use", "Server",
- "Server CMD", "PTR", "Clients", "Clients CMD");
- }
+void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
+ if (mNeat) {
+ MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
+ .createTextTable().dump(out.buf());
+ return;
+ }
- for (const auto &entry : table) {
- printLine(entry.interfaceName,
- entry.transport,
- getArchString(entry.arch),
- entry.getThreadUsage(),
- entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
- entry.serverCmdline,
- entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
- join(entry.clientPids, " "),
- join(entry.clientCmdlines, ";"));
+ forEachTable([this, &out](const Table &table) {
- // We're only interested in dumping debug info for already
- // instantiated services. There's little value in dumping the
- // debug info for a service we create on the fly, so we only operate
- // on the "mServicesTable".
- if (mEmitDebugInfo && &table == &mServicesTable) {
- auto pair = splitFirst(entry.interfaceName, '/');
- mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(),
- NullableOStream<std::ostream>(nullptr));
- }
+ // We're only interested in dumping debug info for already
+ // instantiated services. There's little value in dumping the
+ // debug info for a service we create on the fly, so we only operate
+ // on the "mServicesTable".
+ std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
+ if (mEmitDebugInfo && &table == &mServicesTable) {
+ emitDebugInfo = [this](const auto& iName) {
+ std::stringstream ss;
+ auto pair = splitFirst(iName, '/');
+ mLshal.emitDebugInfo(pair.first, pair.second, {}, ss,
+ NullableOStream<std::ostream>(nullptr));
+ return ss.str();
+ };
}
- if (!mNeat) {
- mOut << std::endl;
- }
+ table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
+ out << std::endl;
});
-
}
-void ListCommand::dump() {
- if (mVintf) {
- dumpVintf();
- if (!!mFileOutput) {
- mFileOutput.buf().close();
- delete &mFileOutput.buf();
- mFileOutput = nullptr;
- }
- mOut = std::cout;
- } else {
- dumpTable();
+Status ListCommand::dump() {
+ auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
+
+ if (mFileOutputPath.empty()) {
+ (*this.*dump)(out());
+ return OK;
}
+
+ std::ofstream fileOutput(mFileOutputPath);
+ if (!fileOutput.is_open()) {
+ err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
+ return IO_ERROR;
+ }
+ chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
+
+ (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
+
+ fileOutput.flush();
+ fileOutput.close();
+ return OK;
}
void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
@@ -471,10 +439,10 @@
case LIST_DLLIB :
table = &mImplementationsTable; break;
default:
- mErr << "Error: Unknown source of entry " << source << std::endl;
+ err() << "Error: Unknown source of entry " << source << std::endl;
}
if (table) {
- table->entries.push_back(std::forward<TableEntry>(entry));
+ table->add(std::forward<TableEntry>(entry));
}
}
@@ -491,10 +459,7 @@
entries.emplace(interfaceName, TableEntry{
.interfaceName = interfaceName,
.transport = "passthrough",
- .serverPid = NO_PID,
- .serverObjectAddress = NO_PTR,
.clientPids = info.clientPids,
- .arch = ARCH_UNKNOWN
}).first->second.arch |= fromBaseArchitecture(info.arch);
}
for (auto &&pair : entries) {
@@ -502,7 +467,7 @@
}
});
if (!ret.isOk()) {
- mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
+ err() << "Error: Failed to call list on getPassthroughServiceManager(): "
<< ret.description() << std::endl;
return DUMP_ALL_LIBS_ERROR;
}
@@ -525,14 +490,13 @@
std::string{info.instanceName.c_str()},
.transport = "passthrough",
.serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
- .serverObjectAddress = NO_PTR,
.clientPids = info.clientPids,
.arch = fromBaseArchitecture(info.arch)
});
}
});
if (!ret.isOk()) {
- mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
+ err() << "Error: Failed to call debugDump on defaultServiceManager(): "
<< ret.description() << std::endl;
return DUMP_PASSTHROUGH_ERROR;
}
@@ -540,10 +504,6 @@
}
Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
- using namespace ::std;
- using namespace ::android::hardware;
- using namespace ::android::hidl::manager::V1_0;
- using namespace ::android::hidl::base::V1_0;
const std::string mode = "hwbinder";
hidl_vec<hidl_string> fqInstanceNames;
@@ -552,86 +512,123 @@
fqInstanceNames = names;
});
if (!listRet.isOk()) {
- mErr << "Error: Failed to list services for " << mode << ": "
+ err() << "Error: Failed to list services for " << mode << ": "
<< listRet.description() << std::endl;
return DUMP_BINDERIZED_ERROR;
}
Status status = OK;
- // server pid, .ptr value of binder object, child pids
- std::map<std::string, DebugInfo> allDebugInfos;
- std::map<pid_t, PidInfo> allPids;
+ std::map<std::string, TableEntry> allTableEntries;
for (const auto &fqInstanceName : fqInstanceNames) {
- const auto pair = splitFirst(fqInstanceName, '/');
- const auto &serviceName = pair.first;
- const auto &instanceName = pair.second;
- auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
- if (!getRet.isOk()) {
- mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
- << "cannot be fetched from service manager:"
- << getRet.description() << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
- continue;
- }
- sp<IBase> service = getRet;
- if (service == nullptr) {
- mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
- << "cannot be fetched from service manager (null)"
- << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
- continue;
- }
- auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
- allDebugInfos[fqInstanceName] = debugInfo;
- if (debugInfo.pid >= 0) {
- allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
- }
+ // create entry and default assign all fields.
+ TableEntry& entry = allTableEntries[fqInstanceName];
+ entry.interfaceName = fqInstanceName;
+ entry.transport = mode;
+
+ status |= fetchBinderizedEntry(manager, &entry);
+ }
+
+ for (auto& pair : allTableEntries) {
+ putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second));
+ }
+ return status;
+}
+
+Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager,
+ TableEntry *entry) {
+ Status status = OK;
+ const auto handleError = [&](Status additionalError, const std::string& msg) {
+ err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl;
+ status |= DUMP_BINDERIZED_ERROR | additionalError;
+ };
+
+ const auto pair = splitFirst(entry->interfaceName, '/');
+ const auto &serviceName = pair.first;
+ const auto &instanceName = pair.second;
+ auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+ if (!getRet.isOk()) {
+ handleError(TRANSACTION_ERROR,
+ "cannot be fetched from service manager:" + getRet.description());
+ return status;
+ }
+ sp<IBase> service = getRet;
+ if (service == nullptr) {
+ handleError(NO_INTERFACE, "cannot be fetched from service manager (null)");
+ return status;
+ }
+
+ // getDebugInfo
+ do {
+ DebugInfo debugInfo;
+ auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
+ debugInfo = received;
});
if (!debugRet.isOk()) {
- mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
- << "debugging information cannot be retrieved:"
- << debugRet.description() << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
+ handleError(TRANSACTION_ERROR,
+ "debugging information cannot be retrieved: " + debugRet.description());
+ break; // skip getPidInfo
}
- }
- for (auto &pair : allPids) {
- pid_t serverPid = pair.first;
- if (!getPidInfo(serverPid, &allPids[serverPid])) {
- mErr << "Warning: no information for PID " << serverPid
- << ", are you root?" << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
- }
- }
- for (const auto &fqInstanceName : fqInstanceNames) {
- auto it = allDebugInfos.find(fqInstanceName);
- if (it == allDebugInfos.end()) {
- putEntry(HWSERVICEMANAGER_LIST, {
- .interfaceName = fqInstanceName,
- .transport = mode,
- .serverPid = NO_PID,
- .serverObjectAddress = NO_PTR,
- .clientPids = {},
- .threadUsage = 0,
- .threadCount = 0,
- .arch = ARCH_UNKNOWN
- });
- continue;
- }
- const DebugInfo &info = it->second;
- bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;
+ entry->serverPid = debugInfo.pid;
+ entry->serverObjectAddress = debugInfo.ptr;
+ entry->arch = fromBaseArchitecture(debugInfo.arch);
- putEntry(HWSERVICEMANAGER_LIST, {
- .interfaceName = fqInstanceName,
- .transport = mode,
- .serverPid = info.pid,
- .serverObjectAddress = info.ptr,
- .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
- .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
- .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
- .arch = fromBaseArchitecture(info.arch),
+ if (debugInfo.pid != NO_PID) {
+ const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
+ if (pidInfo == nullptr) {
+ handleError(IO_ERROR,
+ "no information for PID " + std::to_string(debugInfo.pid) +
+ ", are you root?");
+ break;
+ }
+ if (debugInfo.ptr != NO_PTR) {
+ auto it = pidInfo->refPids.find(debugInfo.ptr);
+ if (it != pidInfo->refPids.end()) {
+ entry->clientPids = it->second;
+ }
+ }
+ entry->threadUsage = pidInfo->threadUsage;
+ entry->threadCount = pidInfo->threadCount;
+ }
+ } while (0);
+
+ // hash
+ do {
+ ssize_t hashIndex = -1;
+ auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
+ for (size_t i = 0; i < c.size(); ++i) {
+ if (serviceName == c[i]) {
+ hashIndex = static_cast<ssize_t>(i);
+ break;
+ }
+ }
});
- }
+ if (!ifaceChainRet.isOk()) {
+ handleError(TRANSACTION_ERROR,
+ "interfaceChain fails: " + ifaceChainRet.description());
+ break; // skip getHashChain
+ }
+ if (hashIndex < 0) {
+ handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
+ break; // skip getHashChain
+ }
+ auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
+ if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
+ handleError(BAD_IMPL,
+ "interfaceChain indicates position " + std::to_string(hashIndex) +
+ " but getHashChain returns " + std::to_string(hashChain.size()) +
+ " hashes");
+ return;
+ }
+
+ auto&& hashArray = hashChain[hashIndex];
+ std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
+ entry->hash = Hash::hexString(hashVec);
+ });
+ if (!hashRet.isOk()) {
+ handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
+ }
+ } while (0);
return status;
}
@@ -639,7 +636,7 @@
Status status = OK;
auto bManager = mLshal.serviceManager();
if (bManager == nullptr) {
- mErr << "Failed to get defaultServiceManager()!" << std::endl;
+ err() << "Failed to get defaultServiceManager()!" << std::endl;
status |= NO_BINDERIZED_MANAGER;
} else {
status |= fetchBinderized(bManager);
@@ -649,7 +646,7 @@
auto pManager = mLshal.passthroughManager();
if (pManager == nullptr) {
- mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
+ err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
status |= NO_PASSTHROUGH_MANAGER;
} else {
status |= fetchAllLibraries(pManager);
@@ -657,139 +654,270 @@
return status;
}
-Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
- static struct option longOptions[] = {
- // long options with short alternatives
- {"help", no_argument, 0, 'h' },
- {"interface", no_argument, 0, 'i' },
- {"transport", no_argument, 0, 't' },
- {"arch", no_argument, 0, 'r' },
- {"pid", no_argument, 0, 'p' },
- {"address", no_argument, 0, 'a' },
- {"clients", no_argument, 0, 'c' },
- {"threads", no_argument, 0, 'e' },
- {"cmdline", no_argument, 0, 'm' },
- {"debug", optional_argument, 0, 'd' },
+void ListCommand::registerAllOptions() {
+ int v = mOptions.size();
+ // A list of acceptable command line options
+ // key: value returned by getopt_long
+ // long options with short alternatives
+ mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) {
+ return USAGE;
+ }, ""});
+ mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
+ return OK;
+ }, "print the instance name column"});
+ mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::RELEASED);
+ return OK;
+ }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"});
+ mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
+ return OK;
+ }, "print the transport mode column"});
+ mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::ARCH);
+ return OK;
+ }, "print the bitness column"});
+ mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::HASH);
+ return OK;
+ }, "print hash of the interface"});
+ mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID);
+ return OK;
+ }, "print the server PID, or server cmdline if -m is set"});
+ mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
+ return OK;
+ }, "print the server object address column"});
+ mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
+ return OK;
+ }, "print the client PIDs, or client cmdlines if -m is set"});
+ mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mSelectedColumns.push_back(TableColumnType::THREADS);
+ return OK;
+ }, "print currently used/available threads\n(note, available threads created lazily)"});
+ mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mEnableCmdlines = true;
+ return OK;
+ }, "print cmdline instead of PIDs"});
+ mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) {
+ thiz->mEmitDebugInfo = true;
+ if (arg) thiz->mFileOutputPath = arg;
+ return OK;
+ }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n"
+ "Writes to specified file if 'arg' is provided, otherwise stdout."});
- // long options without short alternatives
- {"sort", required_argument, 0, 's' },
- {"init-vintf",optional_argument, 0, 'v' },
- {"neat", no_argument, 0, 'n' },
- { 0, 0, 0, 0 }
- };
+ // long options without short alternatives
+ mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
+ thiz->mVintf = true;
+ if (arg) thiz->mFileOutputPath = arg;
+ return OK;
+ }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."});
+ mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) {
+ if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) {
+ thiz->mSortColumn = TableEntry::sortByInterfaceName;
+ } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) {
+ thiz->mSortColumn = TableEntry::sortByServerPid;
+ } else {
+ thiz->err() << "Unrecognized sorting column: " << arg << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."});
+ mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) {
+ thiz->mNeat = true;
+ return OK;
+ }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
+}
+
+// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
+// the lifetime of "options" during the usage of the returned array.
+static std::unique_ptr<struct option[]> getLongOptions(
+ const ListCommand::RegisteredOptions& options,
+ int* longOptFlag) {
+ std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
+ int i = 0;
+ for (const auto& e : options) {
+ ret[i].name = e.longOption.c_str();
+ ret[i].has_arg = e.hasArg;
+ ret[i].flag = longOptFlag;
+ ret[i].val = e.val;
+
+ i++;
+ }
+ // getopt_long last option has all zeros
+ ret[i].name = NULL;
+ ret[i].has_arg = 0;
+ ret[i].flag = NULL;
+ ret[i].val = 0;
+
+ return ret;
+}
+
+// Create 'optstring' argument to getopt_long.
+static std::string getShortOptions(const ListCommand::RegisteredOptions& options) {
+ std::stringstream ss;
+ for (const auto& e : options) {
+ if (e.shortOption != '\0') {
+ ss << e.shortOption;
+ }
+ }
+ return ss.str();
+}
+
+Status ListCommand::parseArgs(const Arg &arg) {
+
+ if (mOptions.empty()) {
+ registerAllOptions();
+ }
+ int longOptFlag;
+ std::unique_ptr<struct option[]> longOptions = getLongOptions(mOptions, &longOptFlag);
+ std::string shortOptions = getShortOptions(mOptions);
+
+ // suppress output to std::err for unknown options
+ opterr = 0;
int optionIndex;
int c;
// Lshal::parseArgs has set optind to the next option to parse
for (;;) {
- // using getopt_long in case we want to add other options in the future
c = getopt_long(arg.argc, arg.argv,
- "hitrpacmde", longOptions, &optionIndex);
+ shortOptions.c_str(), longOptions.get(), &optionIndex);
if (c == -1) {
break;
}
- switch (c) {
- case 's': {
- if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
- mSortColumn = TableEntry::sortByInterfaceName;
- } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
- mSortColumn = TableEntry::sortByServerPid;
- } else {
- mErr << "Unrecognized sorting column: " << optarg << std::endl;
- mLshal.usage(command);
- return USAGE;
+ const RegisteredOption* found = nullptr;
+ if (c == 0) {
+ // see long option
+ for (const auto& e : mOptions) {
+ if (longOptFlag == e.val) found = &e;
}
- break;
- }
- case 'v': {
- if (optarg) {
- mFileOutput = new std::ofstream{optarg};
- mOut = mFileOutput;
- if (!mFileOutput.buf().is_open()) {
- mErr << "Could not open file '" << optarg << "'." << std::endl;
- return IO_ERROR;
- }
+ } else {
+ // see short option
+ for (const auto& e : mOptions) {
+ if (c == e.shortOption) found = &e;
}
- mVintf = true;
}
- case 'i': {
- mSelectedColumns |= ENABLE_INTERFACE_NAME;
- break;
- }
- case 't': {
- mSelectedColumns |= ENABLE_TRANSPORT;
- break;
- }
- case 'r': {
- mSelectedColumns |= ENABLE_ARCH;
- break;
- }
- case 'p': {
- mSelectedColumns |= ENABLE_SERVER_PID;
- break;
- }
- case 'a': {
- mSelectedColumns |= ENABLE_SERVER_ADDR;
- break;
- }
- case 'c': {
- mSelectedColumns |= ENABLE_CLIENT_PIDS;
- break;
- }
- case 'e': {
- mSelectedColumns |= ENABLE_THREADS;
- break;
- }
- case 'm': {
- mEnableCmdlines = true;
- break;
- }
- case 'd': {
- mEmitDebugInfo = true;
- if (optarg) {
- mFileOutput = new std::ofstream{optarg};
- mOut = mFileOutput;
- if (!mFileOutput.buf().is_open()) {
- mErr << "Could not open file '" << optarg << "'." << std::endl;
- return IO_ERROR;
- }
- chown(optarg, AID_SHELL, AID_SHELL);
- }
- break;
- }
- case 'n': {
- mNeat = true;
- break;
- }
- case 'h': // falls through
- default: // see unrecognized options
- mLshal.usage(command);
+ if (found == nullptr) {
+ // see unrecognized options
+ err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl;
return USAGE;
}
+
+ Status status = found->op(this, optarg);
+ if (status != OK) {
+ return status;
+ }
}
if (optind < arg.argc) {
// see non option
- mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+ err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl;
+ return USAGE;
}
- if (mSelectedColumns == 0) {
- mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS;
+ if (mNeat && mEmitDebugInfo) {
+ err() << "Error: --neat should not be used with --debug." << std::endl;
+ return USAGE;
}
+
+ if (mSelectedColumns.empty()) {
+ mSelectedColumns = {TableColumnType::RELEASED,
+ TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
+ TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
+ }
+
+ if (mEnableCmdlines) {
+ for (size_t i = 0; i < mSelectedColumns.size(); ++i) {
+ if (mSelectedColumns[i] == TableColumnType::SERVER_PID) {
+ mSelectedColumns[i] = TableColumnType::SERVER_CMD;
+ }
+ if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) {
+ mSelectedColumns[i] = TableColumnType::CLIENT_CMDS;
+ }
+ }
+ }
+
+ forEachTable([this] (Table& table) {
+ table.setSelectedColumns(this->mSelectedColumns);
+ });
+
return OK;
}
-Status ListCommand::main(const std::string &command, const Arg &arg) {
- Status status = parseArgs(command, arg);
+Status ListCommand::main(const Arg &arg) {
+ Status status = parseArgs(arg);
if (status != OK) {
return status;
}
status = fetch();
postprocess();
- dump();
+ status |= dump();
return status;
}
+static std::vector<std::string> splitString(const std::string &s, char c) {
+ std::vector<std::string> components;
+
+ size_t startPos = 0;
+ size_t matchPos;
+ while ((matchPos = s.find(c, startPos)) != std::string::npos) {
+ components.push_back(s.substr(startPos, matchPos - startPos));
+ startPos = matchPos + 1;
+ }
+
+ if (startPos <= s.length()) {
+ components.push_back(s.substr(startPos));
+ }
+ return components;
+}
+
+const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const {
+ static const std::string empty{};
+ static const std::string optional{"[=<arg>]"};
+ static const std::string required{"=<arg>"};
+
+ if (hasArg == optional_argument) {
+ return optional;
+ }
+ if (hasArg == required_argument) {
+ return required;
+ }
+ return empty;
+}
+
+void ListCommand::usage() const {
+
+ err() << "list:" << std::endl
+ << " lshal" << std::endl
+ << " lshal list" << std::endl
+ << " List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl
+ << " lshal list [-h|--help]" << std::endl
+ << " -h, --help: Print help message for list (`lshal help list`)" << std::endl
+ << " lshal [list] [OPTIONS...]" << std::endl;
+ for (const auto& e : mOptions) {
+ if (e.help.empty()) {
+ continue;
+ }
+ err() << " ";
+ if (e.shortOption != '\0')
+ err() << "-" << e.shortOption << e.getHelpMessageForArgument();
+ if (e.shortOption != '\0' && !e.longOption.empty())
+ err() << ", ";
+ if (!e.longOption.empty())
+ err() << "--" << e.longOption << e.getHelpMessageForArgument();
+ err() << ": ";
+ std::vector<std::string> lines = splitString(e.help, '\n');
+ for (const auto& line : lines) {
+ if (&line != &lines.front())
+ err() << " ";
+ err() << line << std::endl;
+ }
+ }
+}
+
} // namespace lshal
} // namespace android
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index a75db04..7e252fc 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -17,6 +17,7 @@
#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
#define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
+#include <getopt.h>
#include <stdint.h>
#include <fstream>
@@ -26,8 +27,10 @@
#include <android-base/macros.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include "Command.h"
#include "NullableOStream.h"
#include "TableEntry.h"
+#include "TextTable.h"
#include "utils.h"
namespace android {
@@ -35,39 +38,71 @@
class Lshal;
-class ListCommand {
+struct PidInfo {
+ std::map<uint64_t, Pids> refPids; // pids that are referenced
+ uint32_t threadUsage; // number of threads in use
+ uint32_t threadCount; // number of threads total
+};
+
+class ListCommand : public Command {
public:
- ListCommand(Lshal &lshal);
- Status main(const std::string &command, const Arg &arg);
-private:
- Status parseArgs(const std::string &command, const Arg &arg);
+ ListCommand(Lshal &lshal) : Command(lshal) {}
+ virtual ~ListCommand() = default;
+ Status main(const Arg &arg) override;
+ void usage() const override;
+ std::string getSimpleDescription() const override;
+ std::string getName() const override { return GetName(); }
+
+ static std::string GetName();
+
+ struct RegisteredOption {
+ // short alternative, e.g. 'v'. If '\0', no short options is available.
+ char shortOption;
+ // long alternative, e.g. 'init-vintf'
+ std::string longOption;
+ // no_argument, required_argument or optional_argument
+ int hasArg;
+ // value written to 'flag' by getopt_long
+ int val;
+ // operation when the argument is present
+ std::function<Status(ListCommand* thiz, const char* arg)> op;
+ // help message
+ std::string help;
+
+ const std::string& getHelpMessageForArgument() const;
+ };
+ // A list of acceptable command line options
+ // key: value returned by getopt_long
+ using RegisteredOptions = std::vector<RegisteredOption>;
+
+protected:
+ Status parseArgs(const Arg &arg);
Status fetch();
- void postprocess();
- void dump();
+ virtual void postprocess();
+ Status dump();
void putEntry(TableEntrySource source, TableEntry &&entry);
Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
- struct PidInfo {
- std::map<uint64_t, Pids> refPids; // pids that are referenced
- uint32_t threadUsage; // number of threads in use
- uint32_t threadCount; // number of threads total
- };
- bool getPidInfo(pid_t serverPid, PidInfo *info) const;
+ Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager,
+ TableEntry *entry);
- void dumpTable();
- void dumpVintf() const;
- void printLine(
- const std::string &interfaceName,
- const std::string &transport,
- const std::string &arch,
- const std::string &threadUsage,
- const std::string &server,
- const std::string &serverCmdline,
- const std::string &address,
- const std::string &clients,
- const std::string &clientCmdlines) const;
+ // Get relevant information for a PID by parsing files under /d/binder.
+ // It is a virtual member function so that it can be mocked.
+ virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const;
+ // Retrieve from mCachedPidInfos and call getPidInfo if necessary.
+ const PidInfo* getPidInfoCached(pid_t serverPid);
+
+ void dumpTable(const NullableOStream<std::ostream>& out) const;
+ void dumpVintf(const NullableOStream<std::ostream>& out) const;
+ void addLine(TextTable *table, const std::string &interfaceName, const std::string &transport,
+ const std::string &arch, const std::string &threadUsage, const std::string &server,
+ const std::string &serverCmdline, const std::string &address,
+ const std::string &clients, const std::string &clientCmdlines) const;
+ void addLine(TextTable *table, const TableEntry &entry);
+ // Read and return /proc/{pid}/cmdline.
+ virtual std::string parseCmdline(pid_t pid) const;
// Return /proc/{pid}/cmdline if it exists, else empty string.
const std::string &getCmdline(pid_t pid);
// Call getCmdline on all pid in pids. If it returns empty string, the process might
@@ -76,21 +111,18 @@
void forEachTable(const std::function<void(Table &)> &f);
void forEachTable(const std::function<void(const Table &)> &f) const;
- Lshal &mLshal;
+ NullableOStream<std::ostream> err() const;
+ NullableOStream<std::ostream> out() const;
+
+ void registerAllOptions();
Table mServicesTable{};
Table mPassthroughRefTable{};
Table mImplementationsTable{};
- NullableOStream<std::ostream> mErr;
- NullableOStream<std::ostream> mOut;
- NullableOStream<std::ofstream> mFileOutput = nullptr;
+ std::string mFileOutputPath;
TableEntryCompare mSortColumn = nullptr;
- TableEntrySelect mSelectedColumns = 0;
- // If true, cmdlines will be printed instead of pid.
- bool mEnableCmdlines = false;
- // If true, calls IBase::debug(...) on each service.
bool mEmitDebugInfo = false;
// If true, output in VINTF format.
@@ -104,6 +136,16 @@
// If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
std::map<pid_t, std::string> mCmdlines;
+ // Cache for getPidInfo.
+ std::map<pid_t, PidInfo> mCachedPidInfos;
+
+ RegisteredOptions mOptions;
+ // All selected columns
+ std::vector<TableColumnType> mSelectedColumns;
+ // If true, emit cmdlines instead of PIDs
+ bool mEnableCmdlines = false;
+
+private:
DISALLOW_COPY_AND_ASSIGN(ListCommand);
};
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index e2d5f6d..c6f28ac 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -34,9 +34,8 @@
using ::android::hidl::manager::V1_0::IServiceManager;
Lshal::Lshal()
- : mOut(std::cout), mErr(std::cerr),
- mServiceManager(::android::hardware::defaultServiceManager()),
- mPassthroughManager(::android::hardware::getPassthroughServiceManager()) {
+ : Lshal(std::cout, std::cerr, ::android::hardware::defaultServiceManager(),
+ ::android::hardware::getPassthroughServiceManager()) {
}
Lshal::Lshal(std::ostream &out, std::ostream &err,
@@ -46,76 +45,39 @@
mServiceManager(serviceManager),
mPassthroughManager(passthroughManager) {
+ mRegisteredCommands.push_back({std::make_unique<ListCommand>(*this)});
+ mRegisteredCommands.push_back({std::make_unique<DebugCommand>(*this)});
+ mRegisteredCommands.push_back({std::make_unique<HelpCommand>(*this)});
}
-void Lshal::usage(const std::string &command) const {
- static const std::string helpSummary =
- "lshal: List and debug HALs.\n"
- "\n"
- "commands:\n"
- " help Print help message\n"
- " list list HALs\n"
- " debug debug a specified HAL\n"
- "\n"
- "If no command is specified, `list` is the default.\n";
+void Lshal::forEachCommand(const std::function<void(const Command* c)>& f) const {
+ for (const auto& e : mRegisteredCommands) f(e.get());
+}
- static const std::string list =
- "list:\n"
- " lshal\n"
- " lshal list\n"
- " List all hals with default ordering and columns (`lshal list -ipc`)\n"
- " lshal list [-h|--help]\n"
- " -h, --help: Print help message for list (`lshal help list`)\n"
- " lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n"
- " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n"
- " [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n"
- " [--debug|-d[=<output file>]]\n"
- " -i, --interface: print the interface name column\n"
- " -n, --instance: print the instance name column\n"
- " -t, --transport: print the transport mode column\n"
- " -r, --arch: print if the HAL is in 64-bit or 32-bit\n"
- " -e, --threads: print currently used/available threads\n"
- " (note, available threads created lazily)\n"
- " -p, --pid: print the server PID, or server cmdline if -m is set\n"
- " -a, --address: print the server object address column\n"
- " -c, --clients: print the client PIDs, or client cmdlines if -m is set\n"
- " -m, --cmdline: print cmdline instead of PIDs\n"
- " -d[=<output file>], --debug[=<output file>]: emit debug info from \n"
- " IBase::debug with empty options\n"
- " --sort=i, --sort=interface: sort by interface name\n"
- " --sort=p, --sort=pid: sort by server pid\n"
- " --init-vintf=<output file>: form a skeleton HAL manifest to specified\n"
- " file, or stdout if no file specified.\n";
+void Lshal::usage() {
+ err() << "lshal: List and debug HALs." << std::endl << std::endl
+ << "commands:" << std::endl;
- static const std::string debug =
- "debug:\n"
- " lshal debug <interface> [options [options [...]]] \n"
- " Print debug information of a specified interface.\n"
- " <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n"
- " If instance name is missing `default` is used.\n"
- " options: space separated options to IBase::debug.\n";
+ size_t nameMaxLength = 0;
+ forEachCommand([&](const Command* e) {
+ nameMaxLength = std::max(nameMaxLength, e->getName().length());
+ });
+ bool first = true;
+ forEachCommand([&](const Command* e) {
+ if (!first) err() << std::endl;
+ first = false;
+ err() << " " << std::left << std::setw(nameMaxLength + 8) << e->getName()
+ << e->getSimpleDescription();
+ });
+ err() << std::endl << "If no command is specified, `" << ListCommand::GetName()
+ << "` is the default." << std::endl << std::endl;
- static const std::string help =
- "help:\n"
- " lshal -h\n"
- " lshal --help\n"
- " lshal help\n"
- " Print this help message\n"
- " lshal help list\n"
- " Print help message for list\n"
- " lshal help debug\n"
- " Print help message for debug\n";
-
- if (command == "list") {
- mErr << list;
- return;
- }
- if (command == "debug") {
- mErr << debug;
- return;
- }
-
- mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help;
+ first = true;
+ forEachCommand([&](const Command* e) {
+ if (!first) err() << std::endl;
+ first = false;
+ e->usage();
+ });
}
// A unique_ptr type using a custom deleter function.
@@ -186,26 +148,24 @@
}
Status Lshal::parseArgs(const Arg &arg) {
- static std::set<std::string> sAllCommands{"list", "debug", "help"};
optind = 1;
if (optind >= arg.argc) {
// no options at all.
return OK;
}
mCommand = arg.argv[optind];
- if (sAllCommands.find(mCommand) != sAllCommands.end()) {
+ if (selectCommand(mCommand) != nullptr) {
++optind;
return OK; // mCommand is set correctly
}
if (mCommand.size() > 0 && mCommand[0] == '-') {
// first argument is an option, set command to "" (which is recognized as "list")
- mCommand = "";
+ mCommand.clear();
return OK;
}
- mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl;
- usage();
+ err() << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "'" << std::endl;
return USAGE;
}
@@ -216,27 +176,43 @@
}
}
+Command* Lshal::selectCommand(const std::string& command) const {
+ if (command.empty()) {
+ return selectCommand(ListCommand::GetName());
+ }
+ for (const auto& e : mRegisteredCommands) {
+ if (e->getName() == command) {
+ return e.get();
+ }
+ }
+ return nullptr;
+}
+
Status Lshal::main(const Arg &arg) {
// Allow SIGINT to terminate all threads.
signal(SIGINT, signalHandler);
Status status = parseArgs(arg);
if (status != OK) {
+ usage();
return status;
}
- if (mCommand == "help") {
- usage(optind < arg.argc ? arg.argv[optind] : "");
+ auto c = selectCommand(mCommand);
+ if (c == nullptr) {
+ // unknown command, print global usage
+ usage();
return USAGE;
}
- // Default command is list
- if (mCommand == "list" || mCommand == "") {
- return ListCommand{*this}.main(mCommand, arg);
+ status = c->main(arg);
+ if (status == USAGE) {
+ // bad options. Run `lshal help ${mCommand}` instead.
+ // For example, `lshal --unknown-option` becomes `lshal help` (prints global help)
+ // and `lshal list --unknown-option` becomes `lshal help list`
+ auto&& help = selectCommand(HelpCommand::GetName());
+ return static_cast<HelpCommand*>(help)->usageOfCommand(mCommand);
}
- if (mCommand == "debug") {
- return DebugCommand{*this}.main(mCommand, arg);
- }
- usage();
- return USAGE;
+
+ return status;
}
NullableOStream<std::ostream> Lshal::err() const {
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 00db5d0..690f30e 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -24,6 +24,8 @@
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <utils/StrongPointer.h>
+#include "Command.h"
+#include "HelpCommand.h"
#include "NullableOStream.h"
#include "utils.h"
@@ -33,13 +35,15 @@
class Lshal {
public:
Lshal();
+ virtual ~Lshal() {}
Lshal(std::ostream &out, std::ostream &err,
sp<hidl::manager::V1_0::IServiceManager> serviceManager,
sp<hidl::manager::V1_0::IServiceManager> passthroughManager);
Status main(const Arg &arg);
- void usage(const std::string &command = "") const;
- NullableOStream<std::ostream> err() const;
- NullableOStream<std::ostream> out() const;
+ // global usage
+ void usage();
+ virtual NullableOStream<std::ostream> err() const;
+ virtual NullableOStream<std::ostream> out() const;
const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const;
const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const;
@@ -49,16 +53,23 @@
const std::vector<std::string> &options,
std::ostream &out,
NullableOStream<std::ostream> err) const;
+
+ Command* selectCommand(const std::string& command) const;
+
+ void forEachCommand(const std::function<void(const Command* c)>& f) const;
+
private:
Status parseArgs(const Arg &arg);
+
std::string mCommand;
- Arg mCmdArgs;
NullableOStream<std::ostream> mOut;
NullableOStream<std::ostream> mErr;
sp<hidl::manager::V1_0::IServiceManager> mServiceManager;
sp<hidl::manager::V1_0::IServiceManager> mPassthroughManager;
+ std::vector<std::unique_ptr<Command>> mRegisteredCommands;
+
DISALLOW_COPY_AND_ASSIGN(Lshal);
};
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 54d19f6..fc40749 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -57,8 +57,7 @@
////////////////////////////////////////////////////////////////////////////////
PipeRelay::PipeRelay(std::ostream &os)
- : mOutStream(os),
- mInitCheck(NO_INIT) {
+ : mInitCheck(NO_INIT) {
int res = socketpair(AF_UNIX, SOCK_STREAM, 0 /* protocol */, mFds);
if (res < 0) {
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index 76b2b23..8dc3093 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -42,7 +42,6 @@
private:
struct RelayThread;
- std::ostream &mOutStream;
status_t mInitCheck;
int mFds[2];
sp<RelayThread> mThread;
diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp
new file mode 100644
index 0000000..e8792a4
--- /dev/null
+++ b/cmds/lshal/TableEntry.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "lshal"
+#include <android-base/logging.h>
+
+#include <hidl-hash/Hash.h>
+
+#include "TableEntry.h"
+
+#include "TextTable.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+static const std::string &getArchString(Architecture arch) {
+ static const std::string sStr64 = "64";
+ static const std::string sStr32 = "32";
+ static const std::string sStrBoth = "32+64";
+ static const std::string sStrUnknown = "";
+ switch (arch) {
+ case ARCH64:
+ return sStr64;
+ case ARCH32:
+ return sStr32;
+ case ARCH_BOTH:
+ return sStrBoth;
+ case ARCH_UNKNOWN: // fall through
+ default:
+ return sStrUnknown;
+ }
+}
+
+static std::string getTitle(TableColumnType type) {
+ switch (type) {
+ case TableColumnType::INTERFACE_NAME: return "Interface";
+ case TableColumnType::TRANSPORT: return "Transport";
+ case TableColumnType::SERVER_PID: return "Server";
+ case TableColumnType::SERVER_CMD: return "Server CMD";
+ case TableColumnType::SERVER_ADDR: return "PTR";
+ case TableColumnType::CLIENT_PIDS: return "Clients";
+ case TableColumnType::CLIENT_CMDS: return "Clients CMD";
+ case TableColumnType::ARCH: return "Arch";
+ case TableColumnType::THREADS: return "Thread Use";
+ case TableColumnType::RELEASED: return "R";
+ case TableColumnType::HASH: return "Hash";
+ default:
+ LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
+ return "";
+ }
+}
+
+std::string TableEntry::getField(TableColumnType type) const {
+ switch (type) {
+ case TableColumnType::INTERFACE_NAME:
+ return interfaceName;
+ case TableColumnType::TRANSPORT:
+ return transport;
+ case TableColumnType::SERVER_PID:
+ return serverPid == NO_PID ? "N/A" : std::to_string(serverPid);
+ case TableColumnType::SERVER_CMD:
+ return serverCmdline;
+ case TableColumnType::SERVER_ADDR:
+ return serverObjectAddress == NO_PTR ? "N/A" : toHexString(serverObjectAddress);
+ case TableColumnType::CLIENT_PIDS:
+ return join(clientPids, " ");
+ case TableColumnType::CLIENT_CMDS:
+ return join(clientCmdlines, ";");
+ case TableColumnType::ARCH:
+ return getArchString(arch);
+ case TableColumnType::THREADS:
+ return getThreadUsage();
+ case TableColumnType::RELEASED:
+ return isReleased();
+ case TableColumnType::HASH:
+ return hash;
+ default:
+ LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
+ return "";
+ }
+}
+
+std::string TableEntry::isReleased() const {
+ static const std::string unreleased = Hash::hexString(Hash::kEmptyHash);
+
+ if (hash.empty() || hash == unreleased) {
+ return " "; // unknown or unreleased
+ }
+ return "Y"; // released
+}
+
+TextTable Table::createTextTable(bool neat,
+ const std::function<std::string(const std::string&)>& emitDebugInfo) const {
+
+ TextTable textTable;
+ std::vector<std::string> row;
+ if (!neat) {
+ textTable.add(mDescription);
+
+ row.clear();
+ for (TableColumnType type : mSelectedColumns) {
+ row.push_back(getTitle(type));
+ }
+ textTable.add(std::move(row));
+ }
+
+ for (const auto& entry : mEntries) {
+ row.clear();
+ for (TableColumnType type : mSelectedColumns) {
+ row.push_back(entry.getField(type));
+ }
+ textTable.add(std::move(row));
+
+ if (emitDebugInfo) {
+ std::string debugInfo = emitDebugInfo(entry.interfaceName);
+ if (!debugInfo.empty()) textTable.add(debugInfo);
+ }
+ }
+ return textTable;
+}
+
+TextTable MergedTable::createTextTable() {
+ TextTable textTable;
+ for (const Table* table : mTables) {
+ textTable.addAll(table->createTextTable());
+ }
+ return textTable;
+}
+
+bool TableEntry::operator==(const TableEntry& other) const {
+ if (this == &other) {
+ return true;
+ }
+ return interfaceName == other.interfaceName && transport == other.transport &&
+ serverPid == other.serverPid && threadUsage == other.threadUsage &&
+ threadCount == other.threadCount && serverCmdline == other.serverCmdline &&
+ serverObjectAddress == other.serverObjectAddress && clientPids == other.clientPids &&
+ clientCmdlines == other.clientCmdlines && arch == other.arch;
+}
+
+std::string TableEntry::to_string() const {
+ std::stringstream ss;
+ ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage()
+ << ";server=" << serverPid
+ << "(" << serverObjectAddress << ";" << serverCmdline << ");clients=["
+ << join(clientPids, ";") << "](" << join(clientCmdlines, ";") << ");arch="
+ << getArchString(arch);
+ return ss.str();
+
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index e04c3ca..69206cc 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -23,6 +23,8 @@
#include <vector>
#include <iostream>
+#include "TextTable.h"
+
namespace android {
namespace lshal {
@@ -43,17 +45,38 @@
};
using Architecture = unsigned int;
+enum class TableColumnType : unsigned int {
+ INTERFACE_NAME,
+ TRANSPORT,
+ SERVER_PID,
+ SERVER_CMD,
+ SERVER_ADDR,
+ CLIENT_PIDS,
+ CLIENT_CMDS,
+ ARCH,
+ THREADS,
+ RELEASED,
+ HASH,
+};
+
+enum {
+ NO_PID = -1,
+ NO_PTR = 0
+};
+
struct TableEntry {
- std::string interfaceName;
- std::string transport;
- int32_t serverPid;
- uint32_t threadUsage;
- uint32_t threadCount;
- std::string serverCmdline;
- uint64_t serverObjectAddress;
- Pids clientPids;
- std::vector<std::string> clientCmdlines;
- Architecture arch;
+ std::string interfaceName{};
+ std::string transport{};
+ int32_t serverPid{NO_PID};
+ uint32_t threadUsage{0};
+ uint32_t threadCount{0};
+ std::string serverCmdline{};
+ uint64_t serverObjectAddress{NO_PTR};
+ Pids clientPids{};
+ std::vector<std::string> clientCmdlines{};
+ Architecture arch{ARCH_UNKNOWN};
+ // empty: unknown, all zeros: unreleased, otherwise: released
+ std::string hash{};
static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
return a.interfaceName < b.interfaceName;
@@ -69,36 +92,52 @@
return std::to_string(threadUsage) + "/" + std::to_string(threadCount);
}
+
+ std::string isReleased() const;
+
+ std::string getField(TableColumnType type) const;
+
+ bool operator==(const TableEntry& other) const;
+ std::string to_string() const;
};
-struct Table {
- using Entries = std::vector<TableEntry>;
- std::string description;
- Entries entries;
+using SelectedColumns = std::vector<TableColumnType>;
- Entries::iterator begin() { return entries.begin(); }
- Entries::const_iterator begin() const { return entries.begin(); }
- Entries::iterator end() { return entries.end(); }
- Entries::const_iterator end() const { return entries.end(); }
+class Table {
+public:
+ using Entries = std::vector<TableEntry>;
+
+ Entries::iterator begin() { return mEntries.begin(); }
+ Entries::const_iterator begin() const { return mEntries.begin(); }
+ Entries::iterator end() { return mEntries.end(); }
+ Entries::const_iterator end() const { return mEntries.end(); }
+ size_t size() const { return mEntries.size(); }
+
+ void add(TableEntry&& entry) { mEntries.push_back(std::move(entry)); }
+
+ void setSelectedColumns(const SelectedColumns& s) { mSelectedColumns = s; }
+ const SelectedColumns& getSelectedColumns() const { return mSelectedColumns; }
+
+ void setDescription(std::string&& d) { mDescription = std::move(d); }
+
+ // Write table content.
+ TextTable createTextTable(bool neat = true,
+ const std::function<std::string(const std::string&)>& emitDebugInfo = nullptr) const;
+
+private:
+ std::string mDescription;
+ Entries mEntries;
+ SelectedColumns mSelectedColumns;
};
using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
-enum : unsigned int {
- ENABLE_INTERFACE_NAME = 1 << 0,
- ENABLE_TRANSPORT = 1 << 1,
- ENABLE_SERVER_PID = 1 << 2,
- ENABLE_SERVER_ADDR = 1 << 3,
- ENABLE_CLIENT_PIDS = 1 << 4,
- ENABLE_ARCH = 1 << 5,
- ENABLE_THREADS = 1 << 6,
-};
-
-using TableEntrySelect = unsigned int;
-
-enum {
- NO_PID = -1,
- NO_PTR = 0
+class MergedTable {
+public:
+ MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {}
+ TextTable createTextTable();
+private:
+ std::vector<const Table*> mTables;
};
} // namespace lshal
diff --git a/cmds/lshal/TextTable.cpp b/cmds/lshal/TextTable.cpp
new file mode 100644
index 0000000..eca9061
--- /dev/null
+++ b/cmds/lshal/TextTable.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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 <algorithm>
+#include <iomanip>
+
+#include "TextTable.h"
+
+namespace android {
+namespace lshal {
+
+void TextTable::computeWidth(const std::vector<std::string>& v) {
+ if (mWidths.size() < v.size()) {
+ mWidths.resize(v.size());
+ }
+ for (size_t i = 0; i < v.size(); ++i) {
+ mWidths[i] = std::max(mWidths[i], v[i].length());
+ }
+}
+
+void TextTable::dump(std::ostream& out) const {
+ out << std::left;
+ for (const auto& row : mTable) {
+ if (!row.isRow()) {
+ out << row.line() << std::endl;
+ continue;
+ }
+
+ for (size_t i = 0; i < row.fields().size(); ++i) {
+ if (i != 0) {
+ out << " ";
+ }
+ // last column does not std::setw to avoid printing unnecessary spaces.
+ if (i < row.fields().size() - 1) {
+ out << std::setw(mWidths[i]);
+ }
+ out << row.fields()[i];
+ }
+ out << std::endl;
+ }
+}
+
+void TextTable::addAll(TextTable&& other) {
+ for (auto&& row : other.mTable) {
+ if (row.isRow()) {
+ computeWidth(row.fields());
+ }
+
+ mTable.emplace_back(std::move(row));
+ }
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h
new file mode 100644
index 0000000..91d522a
--- /dev/null
+++ b/cmds/lshal/TextTable.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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 FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace lshal {
+
+// An element in TextTable. This is either an actual row (an array of cells
+// in this row), or a string of explanatory text.
+// To see if this is an actual row, test fields().empty().
+class TextTableRow {
+public:
+ // An empty line.
+ TextTableRow() {}
+
+ // A row of cells.
+ TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {}
+
+ // A single comment string.
+ TextTableRow(std::string&& s) : mLine(std::move(s)) {}
+ TextTableRow(const std::string& s) : mLine(s) {}
+
+ // Whether this row is an actual row of cells.
+ bool isRow() const { return !fields().empty(); }
+
+ // Get all cells.
+ const std::vector<std::string>& fields() const { return mFields; }
+
+ // Get the single comment string.
+ const std::string& line() const { return mLine; }
+
+private:
+ std::vector<std::string> mFields;
+ std::string mLine;
+};
+
+// A TextTable is a 2D array of strings.
+class TextTable {
+public:
+
+ // Add a TextTableRow.
+ void add() { mTable.emplace_back(); }
+ void add(std::vector<std::string>&& v) {
+ computeWidth(v);
+ mTable.emplace_back(std::move(v));
+ }
+ void add(const std::string& s) { mTable.emplace_back(s); }
+ void add(std::string&& s) { mTable.emplace_back(std::move(s)); }
+
+ void addAll(TextTable&& other);
+
+ // Prints the table to out, with column widths adjusted appropriately according
+ // to the content.
+ void dump(std::ostream& out) const;
+
+private:
+ void computeWidth(const std::vector<std::string>& v);
+ std::vector<size_t> mWidths;
+ std::vector<TextTableRow> mTable;
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 972d508..9220fc0 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -26,21 +26,29 @@
#include <gmock/gmock.h>
#include <android/hardware/tests/baz/1.0/IQuux.h>
#include <hidl/HidlTransportSupport.h>
+#include <vintf/parse_xml.h>
+#include "ListCommand.h"
#include "Lshal.h"
#define NELEMS(array) static_cast<int>(sizeof(array) / sizeof(array[0]))
using namespace testing;
+using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
using ::android::hidl::manager::V1_0::IServiceNotification;
+using ::android::hardware::hidl_array;
using ::android::hardware::hidl_death_recipient;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
+using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;
+
+using hidl_hash = hidl_array<uint8_t, 32>;
+
namespace android {
namespace hardware {
namespace tests {
@@ -76,7 +84,6 @@
namespace lshal {
-
class MockServiceManager : public IServiceManager {
public:
template<typename T>
@@ -107,7 +114,7 @@
};
-class LshalTest : public ::testing::Test {
+class DebugTest : public ::testing::Test {
public:
void SetUp() override {
using ::android::hardware::tests::baz::V1_0::IQuux;
@@ -122,43 +129,545 @@
return new Quux();
return nullptr;
}));
+
+ lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager);
}
void TearDown() override {}
std::stringstream err;
std::stringstream out;
sp<MockServiceManager> serviceManager;
+
+ std::unique_ptr<Lshal> lshal;
};
-TEST_F(LshalTest, Debug) {
- const char *args[] = {
+static Arg createArg(const std::vector<const char*>& args) {
+ return Arg{static_cast<int>(args.size()), const_cast<char**>(args.data())};
+}
+
+template<typename T>
+static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) {
+ return lshal->main(createArg(args));
+}
+
+TEST_F(DebugTest, Debug) {
+ EXPECT_EQ(0u, callMain(lshal, {
"lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
- };
- EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
- .main({NELEMS(args), const_cast<char **>(args)}));
+ }));
EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
EXPECT_THAT(err.str(), IsEmpty());
}
-TEST_F(LshalTest, Debug2) {
- const char *args[] = {
+TEST_F(DebugTest, Debug2) {
+ EXPECT_EQ(0u, callMain(lshal, {
"lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
- };
- EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
- .main({NELEMS(args), const_cast<char **>(args)}));
+ }));
EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
EXPECT_THAT(err.str(), IsEmpty());
}
-TEST_F(LshalTest, Debug3) {
- const char *args[] = {
+TEST_F(DebugTest, Debug3) {
+ EXPECT_NE(0u, callMain(lshal, {
"lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist",
- };
- EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager)
- .main({NELEMS(args), const_cast<char **>(args)}));
+ }));
EXPECT_THAT(err.str(), HasSubstr("does not exist"));
}
+class MockLshal : public Lshal {
+public:
+ MockLshal() {}
+ ~MockLshal() = default;
+ MOCK_CONST_METHOD0(out, NullableOStream<std::ostream>());
+ MOCK_CONST_METHOD0(err, NullableOStream<std::ostream>());
+};
+
+// expose protected fields and methods for ListCommand
+class MockListCommand : public ListCommand {
+public:
+ MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
+
+ Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); }
+ Status main(const Arg& arg) { return ListCommand::main(arg); }
+ void forEachTable(const std::function<void(Table &)> &f) {
+ return ListCommand::forEachTable(f);
+ }
+ void forEachTable(const std::function<void(const Table &)> &f) const {
+ return ListCommand::forEachTable(f);
+ }
+ Status fetch() { return ListCommand::fetch(); }
+ void dumpVintf(const NullableOStream<std::ostream>& out) {
+ return ListCommand::dumpVintf(out);
+ }
+ void internalPostprocess() { ListCommand::postprocess(); }
+ const PidInfo* getPidInfoCached(pid_t serverPid) {
+ return ListCommand::getPidInfoCached(serverPid);
+ }
+
+ MOCK_METHOD0(postprocess, void());
+ MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
+ MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
+};
+
+class ListParseArgsTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ mockLshal = std::make_unique<NiceMock<MockLshal>>();
+ mockList = std::make_unique<MockListCommand>(mockLshal.get());
+ // ListCommand::parseArgs should parse arguments from the second element
+ optind = 1;
+ }
+ std::unique_ptr<MockLshal> mockLshal;
+ std::unique_ptr<MockListCommand> mockList;
+ std::stringstream output;
+};
+
+TEST_F(ListParseArgsTest, Args) {
+ EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"})));
+ mockList->forEachTable([](const Table& table) {
+ EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME,
+ TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}),
+ table.getSelectedColumns());
+ });
+}
+
+TEST_F(ListParseArgsTest, Cmds) {
+ EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"})));
+ mockList->forEachTable([](const Table& table) {
+ EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID)))
+ << "should not print server PID with -m";
+ EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS)))
+ << "should not print client PIDs with -m";
+ EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD))
+ << "should print server cmd with -m";
+ EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS))
+ << "should print client cmds with -m";
+ });
+}
+
+TEST_F(ListParseArgsTest, DebugAndNeat) {
+ ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output)));
+ EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"})));
+ EXPECT_THAT(output.str(), StrNe(""));
+}
+
+/// Fetch Test
+
+// A set of deterministic functions to generate fake debug infos.
+static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; }
+static std::vector<pid_t> getClients(pid_t serverId) {
+ return {serverId + 1, serverId + 3};
+}
+static PidInfo getPidInfoFromId(pid_t serverId) {
+ PidInfo info;
+ info.refPids[getPtr(serverId)] = getClients(serverId);
+ info.threadUsage = 10 + serverId;
+ info.threadCount = 20 + serverId;
+ return info;
+}
+static std::string getInterfaceName(pid_t serverId) {
+ return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo";
+}
+static std::string getInstanceName(pid_t serverId) {
+ return std::to_string(serverId);
+}
+static pid_t getIdFromInstanceName(const hidl_string& instance) {
+ return atoi(instance.c_str());
+}
+static std::string getFqInstanceName(pid_t serverId) {
+ return getInterfaceName(serverId) + "/" + getInstanceName(serverId);
+}
+static std::string getCmdlineFromId(pid_t serverId) {
+ if (serverId == NO_PID) return "";
+ return "command_line_" + std::to_string(serverId);
+}
+static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; }
+static hidl_hash getHashFromId(pid_t serverId) {
+ hidl_hash hash;
+ bool isReleased = getIsReleasedFromId(serverId);
+ for (size_t i = 0; i < hash.size(); ++i) {
+ hash[i] = isReleased ? static_cast<uint8_t>(serverId) : 0u;
+ }
+ return hash;
+}
+
+// Fake service returned by mocked IServiceManager::get.
+class TestService : public IBase {
+public:
+ TestService(pid_t id) : mId(id) {}
+ hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
+ cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT });
+ return hardware::Void();
+ }
+ hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+ cb({getInterfaceName(mId), IBase::descriptor});
+ return hardware::Void();
+ }
+ hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+ cb({getHashFromId(mId), getHashFromId(0xff)});
+ return hardware::Void();
+ }
+private:
+ pid_t mId;
+};
+
+class ListTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ initMockServiceManager();
+ lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager);
+ initMockList();
+ }
+
+ void initMockList() {
+ mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get());
+ ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke(
+ [](pid_t serverPid, PidInfo* info) {
+ *info = getPidInfoFromId(serverPid);
+ return true;
+ }));
+ ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId));
+ ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() {
+ mockList->internalPostprocess();
+ size_t i = 0;
+ mockList->forEachTable([&](Table& table) {
+ table.setDescription("[fake description " + std::to_string(i++) + "]");
+ });
+ }));
+ }
+
+ void initMockServiceManager() {
+ serviceManager = new testing::NiceMock<MockServiceManager>();
+ passthruManager = new testing::NiceMock<MockServiceManager>();
+ using A = DebugInfo::Architecture;
+ ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke(
+ [] (IServiceManager::list_cb cb) {
+ cb({ getFqInstanceName(1), getFqInstanceName(2) });
+ return hardware::Void();
+ }));
+
+ ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
+ [&](const hidl_string&, const hidl_string& instance) {
+ int id = getIdFromInstanceName(instance);
+ return sp<IBase>(new TestService(id));
+ }));
+
+ ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke(
+ [] (IServiceManager::debugDump_cb cb) {
+ cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3,
+ getClients(3), A::IS_32BIT},
+ InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4,
+ getClients(4), A::IS_32BIT}});
+ return hardware::Void();
+ }));
+
+ ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke(
+ [] (IServiceManager::debugDump_cb cb) {
+ cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5,
+ getClients(5), A::IS_32BIT},
+ InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6,
+ getClients(6), A::IS_32BIT}});
+ return hardware::Void();
+ }));
+ }
+
+ std::stringstream err;
+ std::stringstream out;
+ std::unique_ptr<Lshal> lshal;
+ std::unique_ptr<MockListCommand> mockList;
+ sp<MockServiceManager> serviceManager;
+ sp<MockServiceManager> passthruManager;
+};
+
+TEST_F(ListTest, GetPidInfoCached) {
+ EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1);
+
+ EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
+ EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
+}
+
+TEST_F(ListTest, Fetch) {
+ EXPECT_EQ(0u, mockList->fetch());
+ std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough",
+ "passthrough", "passthrough", "passthrough"}};
+ std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}};
+ int id = 1;
+ mockList->forEachTable([&](const Table& table) {
+ ASSERT_EQ(2u, table.size());
+ for (const auto& entry : table) {
+ const auto& transport = transports[id - 1];
+ TableEntry expected{
+ .interfaceName = getFqInstanceName(id),
+ .transport = transport,
+ .serverPid = transport == "hwbinder" ? id : NO_PID,
+ .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0,
+ .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0,
+ .serverCmdline = {},
+ .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR,
+ .clientPids = getClients(id),
+ .clientCmdlines = {},
+ .arch = archs[id - 1],
+ };
+ EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string();
+
+ ++id;
+ }
+ });
+
+}
+
+TEST_F(ListTest, DumpVintf) {
+ const std::string expected =
+ "<!-- \n"
+ " This is a skeleton device manifest. Notes: \n"
+ " 1. android.hidl.*, android.frameworks.*, android.system.* are not included.\n"
+ " 2. If a HAL is supported in both hwbinder and passthrough transport, \n"
+ " only hwbinder is shown.\n"
+ " 3. It is likely that HALs in passthrough transport does not have\n"
+ " <interface> declared; users will have to write them by hand.\n"
+ " 4. A HAL with lower minor version can be overridden by a HAL with\n"
+ " higher minor version if they have the same name and major version.\n"
+ " 5. sepolicy version is set to 0.0. It is recommended that the entry\n"
+ " is removed from the manifest file and written by assemble_vintf\n"
+ " at build time.\n"
+ "-->\n"
+ "<manifest version=\"1.0\" type=\"device\">\n"
+ " <hal format=\"hidl\">\n"
+ " <name>a.h.foo1</name>\n"
+ " <transport>hwbinder</transport>\n"
+ " <version>1.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>1</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\">\n"
+ " <name>a.h.foo2</name>\n"
+ " <transport>hwbinder</transport>\n"
+ " <version>2.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>2</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\">\n"
+ " <name>a.h.foo3</name>\n"
+ " <transport arch=\"32\">passthrough</transport>\n"
+ " <version>3.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>3</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\">\n"
+ " <name>a.h.foo4</name>\n"
+ " <transport arch=\"32\">passthrough</transport>\n"
+ " <version>4.0</version>\n"
+ " <interface>\n"
+ " <name>IFoo</name>\n"
+ " <instance>4</instance>\n"
+ " </interface>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\">\n"
+ " <name>a.h.foo5</name>\n"
+ " <transport arch=\"32\">passthrough</transport>\n"
+ " <version>5.0</version>\n"
+ " </hal>\n"
+ " <hal format=\"hidl\">\n"
+ " <name>a.h.foo6</name>\n"
+ " <transport arch=\"32\">passthrough</transport>\n"
+ " <version>6.0</version>\n"
+ " </hal>\n"
+ " <sepolicy>\n"
+ " <version>0.0</version>\n"
+ " </sepolicy>\n"
+ "</manifest>\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+
+ vintf::HalManifest m;
+ EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
+ << "--init-vintf does not emit valid HAL manifest: "
+ << vintf::gHalManifestConverter.lastError();
+}
+
+// test default columns
+TEST_F(ListTest, DumpDefault) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "R Interface Thread Use Server Clients\n"
+ " a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n"
+ "Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n"
+ "\n"
+ "[fake description 1]\n"
+ "R Interface Thread Use Server Clients\n"
+ " a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n"
+ " a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n"
+ "\n"
+ "[fake description 2]\n"
+ "R Interface Thread Use Server Clients\n"
+ " a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n"
+ " a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpHash) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface R Hash\n"
+ "a.h.foo1@1.0::IFoo/1 0000000000000000000000000000000000000000000000000000000000000000\n"
+ "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface R Hash\n"
+ "a.h.foo3@3.0::IFoo/3 \n"
+ "a.h.foo4@4.0::IFoo/4 \n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface R Hash\n"
+ "a.h.foo5@5.0::IFoo/5 \n"
+ "a.h.foo6@6.0::IFoo/6 \n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, Dump) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpCmdline) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 command_line_2 0000000000002712 command_line_3;command_line_5\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A command_line_5;command_line_7\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A command_line_7;command_line_9\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpNeat) {
+ const std::string expected =
+ "a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n"
+ "a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n"
+ "a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+class HelpTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ lshal = std::make_unique<Lshal>(out, err, new MockServiceManager() /* serviceManager */,
+ new MockServiceManager() /* passthruManager */);
+ }
+
+ std::stringstream err;
+ std::stringstream out;
+ std::unique_ptr<Lshal> lshal;
+};
+
+TEST_F(HelpTest, GlobalUsage) {
+ (void)callMain(lshal, {"lshal", "--help"}); // ignore return
+ std::string errStr = err.str();
+ EXPECT_THAT(errStr, ContainsRegex("(^|\n)commands:($|\n)"))
+ << "`lshal --help` does not contain global usage";
+ EXPECT_THAT(errStr, ContainsRegex("(^|\n)list:($|\n)"))
+ << "`lshal --help` does not contain usage for 'list' command";
+ EXPECT_THAT(errStr, ContainsRegex("(^|\n)debug:($|\n)"))
+ << "`lshal --help` does not contain usage for 'debug' command";
+ EXPECT_THAT(errStr, ContainsRegex("(^|\n)help:($|\n)"))
+ << "`lshal --help` does not contain usage for 'help' command";
+
+ err.str("");
+ (void)callMain(lshal, {"lshal", "help"}); // ignore return
+ EXPECT_EQ(errStr, err.str()) << "`lshal help` should have the same output as `lshal --help`";
+
+ err.str("");
+ EXPECT_NE(0u, callMain(lshal, {"lshal", "--unknown-option"}));
+ EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
+ EXPECT_THAT(err.str(), EndsWith(errStr))
+ << "`lshal --unknown-option` should have the same output as `lshal --help`";
+ EXPECT_EQ("", out.str());
+}
+
+TEST_F(HelpTest, UnknownOptionList1) {
+ (void)callMain(lshal, {"lshal", "help", "list"});
+ EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
+ << "`lshal help list` does not contain usage for 'list' command";
+}
+
+TEST_F(HelpTest, UnknownOptionList2) {
+ EXPECT_NE(0u, callMain(lshal, {"lshal", "list", "--unknown-option"}));
+ EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
+ EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
+ << "`lshal list --unknown-option` does not contain usage for 'list' command";
+ EXPECT_EQ("", out.str());
+}
+
+TEST_F(HelpTest, UnknownOptionHelp1) {
+ (void)callMain(lshal, {"lshal", "help", "help"});
+ EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
+ << "`lshal help help` does not contain usage for 'help' command";
+}
+
+TEST_F(HelpTest, UnknownOptionHelp2) {
+ (void)callMain(lshal, {"lshal", "help", "--unknown-option"});
+ EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
+ << "`lshal help --unknown-option` does not contain usage for 'help' command";
+ EXPECT_EQ("", out.str());
+}
+
} // namespace lshal
} // namespace android
diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h
index 45b922c..c09e8b1 100644
--- a/cmds/lshal/utils.h
+++ b/cmds/lshal/utils.h
@@ -29,15 +29,23 @@
enum : unsigned int {
OK = 0,
+ // Return to Lshal::main to print help info.
USAGE = 1 << 0,
+ // no service managers
NO_BINDERIZED_MANAGER = 1 << 1,
NO_PASSTHROUGH_MANAGER = 1 << 2,
+ // general error in getting information from the three sources
DUMP_BINDERIZED_ERROR = 1 << 3,
DUMP_PASSTHROUGH_ERROR = 1 << 4,
DUMP_ALL_LIBS_ERROR = 1 << 5,
+ // I/O error in reading files
IO_ERROR = 1 << 6,
+ // Interface does not exist (IServiceManager::get fails)
NO_INTERFACE = 1 << 7,
+ // Transaction error from hwbinder transactions
TRANSACTION_ERROR = 1 << 8,
+ // No transaction error, but return value is unexpected.
+ BAD_IMPL = 1 << 9,
};
using Status = unsigned int;
diff --git a/cmds/rawbu/Android.mk b/cmds/rawbu/Android.mk
index b580390..9322151 100644
--- a/cmds/rawbu/Android.mk
+++ b/cmds/rawbu/Android.mk
@@ -5,6 +5,8 @@
LOCAL_SRC_FILES:= backup.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := libcutils libc
LOCAL_MODULE:= rawbu
diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp
index ff6719f..0072281 100644
--- a/cmds/rawbu/backup.cpp
+++ b/cmds/rawbu/backup.cpp
@@ -129,7 +129,6 @@
}
if(S_ISDIR(statBuffer.st_mode)) {
- int i;
char *newpath;
newpath = strdup(nameBuffer);
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
index b703ed4..9513ec1 100644
--- a/cmds/service/Android.bp
+++ b/cmds/service/Android.bp
@@ -8,7 +8,11 @@
"libbinder",
],
- cflags: ["-DXP_UNIX"],
+ cflags: [
+ "-DXP_UNIX",
+ "-Wall",
+ "-Werror",
+ ],
}
cc_binary {
@@ -22,5 +26,10 @@
"libbinder",
],
- cflags: ["-DXP_UNIX", "-DVENDORSERVICES"],
+ cflags: [
+ "-DXP_UNIX",
+ "-DVENDORSERVICES",
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index d3d396f..428561b 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -46,6 +46,6 @@
cflags: [
"-DVENDORSERVICEMANAGER=1",
],
- shared_libs: ["libcutils", "libselinux_vendor"],
+ shared_libs: ["libcutils", "libselinux"],
init_rc: ["vndservicemanager.rc"],
}
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 93a18fc..fade8cf 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -514,7 +514,7 @@
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj->type = BINDER_TYPE_BINDER;
+ obj->hdr.type = BINDER_TYPE_BINDER;
obj->binder = (uintptr_t)ptr;
obj->cookie = 0;
}
@@ -532,7 +532,7 @@
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj->type = BINDER_TYPE_HANDLE;
+ obj->hdr.type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
}
@@ -649,7 +649,7 @@
if (!obj)
return 0;
- if (obj->type == BINDER_TYPE_HANDLE)
+ if (obj->hdr.type == BINDER_TYPE_HANDLE)
return obj->handle;
return 0;
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index d336a43..4d93cb4 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -11,5 +11,7 @@
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
+ onrestart restart keystore
+ onrestart restart gatekeeperd
writepid /dev/cpuset/system-background/tasks
shutdown critical
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
index dda80bb..71a5e23 100644
--- a/cmds/surfacereplayer/proto/Android.bp
+++ b/cmds/surfacereplayer/proto/Android.bp
@@ -3,6 +3,10 @@
srcs: [
"src/trace.proto",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
proto: {
type: "lite",
export_proto_headers: true,
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 35b63ec..2b5389b 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -505,7 +505,7 @@
ALOGV("Setting Transparent Region Hint");
Region re = Region();
- for (auto r : trhc.region()) {
+ for (const auto& r : trhc.region()) {
Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
re.merge(rect);
}
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 7f46087..a88733c 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -388,13 +388,13 @@
#endif
#if __ANDROID_API__ >= __ANDROID_API_O__
-/*
+/**
* Get a reference to the sensor manager. ASensorManager is a singleton
* per package as different packages may have access to different sensors.
*
* Example:
*
- * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
+ * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
*
*/
ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName);
@@ -503,14 +503,12 @@
* {@link ASensor_isDirectChannelTypeSupported}, respectively.
*
* Example:
- * \code{.cpp}
- * ASensorManager *manager = ...;
- * ASensor *sensor = ...;
- * int channelId = ...;
*
- * ASensorManager_configureDirectReport(
- * manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
- * \endcode
+ * ASensorManager *manager = ...;
+ * ASensor *sensor = ...;
+ * int channelId = ...;
+ *
+ * ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
*
* \param manager the {@link ASensorManager} instance obtained from
* {@link ASensorManager_getInstanceForPackage}.
@@ -530,50 +528,86 @@
/*****************************************************************************/
/**
- * Enable the selected sensor with a specified sampling period and max batch report latency.
- * Returns a negative error code on failure.
- * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
+ * Enable the selected sensor with sampling and report parameters
+ *
+ * Enable the selected sensor at a specified sampling period and max batch report latency.
+ * To disable sensor, use {@link ASensorEventQueue_disableSensor}.
+ *
+ * \param queue {@link ASensorEventQueue} for sensor event to be report to.
+ * \param sensor {@link ASensor} to be enabled.
+ * \param samplingPeriodUs sampling period of sensor in microseconds.
+ * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are
+ * delievered in microseconds. For sensor streaming, set to 0.
+ * \return 0 on success or a negative error code on failure.
*/
int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs);
/**
- * Enable the selected sensor. Returns a negative error code on failure.
+ * Enable the selected sensor at default sampling rate.
+ *
+ * Start event reports of a sensor to specified sensor event queue at a default rate.
+ *
+ * \param queue {@link ASensorEventQueue} for sensor event to be report to.
+ * \param sensor {@link ASensor} to be enabled.
+ *
+ * \return 0 on success or a negative error code on failure.
*/
int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor);
/**
- * Disable the selected sensor. Returns a negative error code on failure.
+ * Disable the selected sensor.
+ *
+ * Stop event reports from the sensor to specified sensor event queue.
+ *
+ * \param queue {@link ASensorEventQueue} to be changed
+ * \param sensor {@link ASensor} to be disabled
+ * \return 0 on success or a negative error code on failure.
*/
int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor);
/**
* Sets the delivery rate of events in microseconds for the given sensor.
+ *
+ * This function has to be called after {@link ASensorEventQueue_enableSensor}.
* Note that this is a hint only, generally event will arrive at a higher
* rate. It is an error to set a rate inferior to the value returned by
* ASensor_getMinDelay().
- * Returns a negative error code on failure.
+ *
+ * \param queue {@link ASensorEventQueue} to which sensor event is delivered.
+ * \param sensor {@link ASensor} of which sampling rate to be updated.
+ * \param usec sensor sampling period (1/sampling rate) in microseconds
+ * \return 0 on sucess or a negative error code on failure.
*/
int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec);
/**
- * Returns true if there are one or more events available in the
- * sensor queue. Returns 1 if the queue has events; 0 if
- * it does not have events; and a negative value if there is an error.
+ * Determine if a sensor event queue has pending event to be processed.
+ *
+ * \param queue {@link ASensorEventQueue} to be queried
+ * \return 1 if the queue has events; 0 if it does not have events;
+ * or a negative value if there is an error.
*/
int ASensorEventQueue_hasEvents(ASensorEventQueue* queue);
/**
- * Returns the next available events from the queue. Returns a negative
- * value if no events are available or an error has occurred, otherwise
- * the number of events returned.
+ * Retrieve pending events in sensor event queue
+ *
+ * Retrieve next available events from the queue to a specified event array.
+ *
+ * \param queue {@link ASensorEventQueue} to get events from
+ * \param events pointer to an array of {@link ASensorEvents}.
+ * \param count max number of event that can be filled into array event.
+ * \return number of events returned on success; negative error code when
+ * no events are pending or an error has occurred.
*
* Examples:
- * ASensorEvent event;
- * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
*
- * ASensorEvent eventBuffer[8];
- * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
+ * ASensorEvent event;
+ * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
+ *
+ * ASensorEvent eventBuffer[8];
+ * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
*
*/
ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
index 380e745..0ec3e94 100644
--- a/include/private/ui/RegionHelper.h
+++ b/include/private/ui/RegionHelper.h
@@ -81,7 +81,6 @@
int inside = spanner.next(current.top, current.bottom);
spannerInner.prepare(inside);
do {
- TYPE left, right;
int inner_inside = spannerInner.next(current.left, current.right);
if ((op_mask >> inner_inside) & 1) {
if (current.left < current.right &&
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 83b8021..c130087 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -69,7 +69,7 @@
"TextOutput.cpp",
"IpPrefix.cpp",
"Value.cpp",
- "aidl/android/content/pm/IPackageManagerNative.aidl",
+ ":libbinder_aidl",
],
aidl: {
@@ -108,4 +108,12 @@
},
}
+// AIDL interface between libbinder and framework.jar
+filegroup {
+ name: "libbinder_aidl",
+ srcs: [
+ "aidl/android/content/pm/IPackageManagerNative.aidl",
+ ],
+}
+
subdirs = ["tests"]
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 890ef30..a81f44e 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -249,6 +249,8 @@
if (resultReceiver != NULL) {
resultReceiver->send(INVALID_OPERATION);
}
+
+ return NO_ERROR;
}
case SYSPROPS_TRANSACTION: {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index e832961..ba9bf61 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -470,22 +470,33 @@
void IPCThreadState::processPendingDerefs()
{
if (mIn.dataPosition() >= mIn.dataSize()) {
- size_t numPending = mPendingWeakDerefs.size();
- if (numPending > 0) {
- for (size_t i = 0; i < numPending; i++) {
- RefBase::weakref_type* refs = mPendingWeakDerefs[i];
+ /*
+ * The decWeak()/decStrong() calls may cause a destructor to run,
+ * which in turn could have initiated an outgoing transaction,
+ * which in turn could cause us to add to the pending refs
+ * vectors; so instead of simply iterating, loop until they're empty.
+ *
+ * We do this in an outer loop, because calling decStrong()
+ * may result in something being added to mPendingWeakDerefs,
+ * which could be delayed until the next incoming command
+ * from the driver if we don't process it now.
+ */
+ while (mPendingWeakDerefs.size() > 0 || mPendingStrongDerefs.size() > 0) {
+ while (mPendingWeakDerefs.size() > 0) {
+ RefBase::weakref_type* refs = mPendingWeakDerefs[0];
+ mPendingWeakDerefs.removeAt(0);
refs->decWeak(mProcess.get());
}
- mPendingWeakDerefs.clear();
- }
- numPending = mPendingStrongDerefs.size();
- if (numPending > 0) {
- for (size_t i = 0; i < numPending; i++) {
- BBinder* obj = mPendingStrongDerefs[i];
+ if (mPendingStrongDerefs.size() > 0) {
+ // We don't use while() here because we don't want to re-order
+ // strong and weak decs at all; if this decStrong() causes both a
+ // decWeak() and a decStrong() to be queued, we want to process
+ // the decWeak() first.
+ BBinder* obj = mPendingStrongDerefs[0];
+ mPendingStrongDerefs.removeAt(0);
obj->decStrong(mProcess.get());
}
- mPendingStrongDerefs.clear();
}
}
}
@@ -560,7 +571,7 @@
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
- status_t err = data.errorCheck();
+ status_t err;
flags |= TF_ACCEPT_FDS;
@@ -571,11 +582,9 @@
<< indent << data << dedent << endl;
}
- if (err == NO_ERROR) {
- LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
- (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
- err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
- }
+ LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
+ (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
+ err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
@@ -675,7 +684,7 @@
#if LOG_REFCOUNTS
ALOGV("IPCThreadState::expungeHandle(%ld)\n", handle);
#endif
- self()->mProcess->expungeHandle(handle, binder);
+ self()->mProcess->expungeHandle(handle, binder); // NOLINT
}
status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 2a15773..1cfe02a 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -289,7 +289,15 @@
SimpleBestFitAllocator::~SimpleBestFitAllocator()
{
while(!mList.isEmpty()) {
- delete mList.remove(mList.head());
+ chunk_t* removed = mList.remove(mList.head());
+#ifdef __clang_analyzer__
+ // Clang static analyzer gets confused in this loop
+ // and generates a false positive warning about accessing
+ // memory that is already freed.
+ // Add an "assert" to avoid the confusion.
+ LOG_ALWAYS_FATAL_IF(mList.head() == removed);
+#endif
+ delete removed;
}
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e22179b..f739f07 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -104,7 +104,7 @@
void acquire_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
- switch (obj.type) {
+ switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
@@ -140,7 +140,7 @@
}
}
- ALOGD("Invalid object type 0x%08x", obj.type);
+ ALOGD("Invalid object type 0x%08x", obj.hdr.type);
}
void acquire_object(const sp<ProcessState>& proc,
@@ -152,7 +152,7 @@
static void release_object(const sp<ProcessState>& proc,
const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
{
- switch (obj.type) {
+ switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie);
@@ -191,7 +191,7 @@
}
}
- ALOGE("Invalid object type 0x%08x", obj.type);
+ ALOGE("Invalid object type 0x%08x", obj.hdr.type);
}
void release_object(const sp<ProcessState>& proc,
@@ -227,17 +227,17 @@
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
- obj.type = BINDER_TYPE_HANDLE;
+ obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
@@ -261,12 +261,12 @@
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
- obj.type = BINDER_TYPE_WEAK_HANDLE;
+ obj.hdr.type = BINDER_TYPE_WEAK_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
- obj.type = BINDER_TYPE_WEAK_BINDER;
+ obj.hdr.type = BINDER_TYPE_WEAK_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs());
obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get());
}
@@ -281,13 +281,13 @@
// but we can't do that with the different reference counting
// implementation we are using.
ALOGE("Unable to unflatten Binder weak reference!");
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
return finish_flatten_binder(NULL, obj, out);
} else {
- obj.type = BINDER_TYPE_BINDER;
+ obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
return finish_flatten_binder(NULL, obj, out);
@@ -307,7 +307,7 @@
const flat_binder_object* flat = in.readObject(false);
if (flat) {
- switch (flat->type) {
+ switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
@@ -326,7 +326,7 @@
const flat_binder_object* flat = in.readObject(false);
if (flat) {
- switch (flat->type) {
+ switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
@@ -543,7 +543,7 @@
= reinterpret_cast<flat_binder_object*>(mData + off);
acquire_object(proc, *flat, this, &mOpenAshmemSize);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
// If this is a file descriptor, we need to dup it so the
// new Parcel now owns its own fd, and can declare that we
// officially know we have fds.
@@ -1152,7 +1152,7 @@
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
{
flat_binder_object obj;
- obj.type = BINDER_TYPE_FD;
+ obj.hdr.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = fd;
@@ -1310,7 +1310,7 @@
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// remember if it's a file descriptor
- if (val.type == BINDER_TYPE_FD) {
+ if (val.hdr.type == BINDER_TYPE_FD) {
if (!mAllowFds) {
// fail before modifying our object index
return FDS_NOT_ALLOWED;
@@ -2132,7 +2132,7 @@
{
const flat_binder_object* flat = readObject(true);
- if (flat && flat->type == BINDER_TYPE_FD) {
+ if (flat && flat->hdr.type == BINDER_TYPE_FD) {
return flat->handle;
}
@@ -2325,7 +2325,7 @@
i--;
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
//ALOGI("Closing fd: %ld", flat->handle);
close(flat->handle);
}
@@ -2397,7 +2397,7 @@
const flat_binder_object* flat
= reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);
to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
- << TypeCode(flat->type & 0x7f7f7f00)
+ << TypeCode(flat->hdr.type & 0x7f7f7f00)
<< " = " << flat->binder;
}
} else {
@@ -2618,7 +2618,7 @@
for (size_t i=objectsSize; i<mObjectsSize; i++) {
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
// will need to rescan because we may have lopped off the only FDs
mFdsKnown = false;
}
@@ -2645,7 +2645,7 @@
pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
- } else if (desired > mDataCapacity) {
+ } else {
mError = NO_MEMORY;
return NO_MEMORY;
}
@@ -2728,7 +2728,7 @@
for (size_t i=0; i<mObjectsSize; i++) {
const flat_binder_object* flat
= reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
+ if (flat->hdr.type == BINDER_TYPE_FD) {
hasFds = true;
break;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 11dd525..da806aa 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -86,6 +86,12 @@
}
LOG_ALWAYS_FATAL("ProcessState was already initialized.");
}
+
+ if (access(driver, R_OK) == -1) {
+ ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
+ driver = "/dev/binder";
+ }
+
gProcess = new ProcessState(driver);
return gProcess;
}
@@ -420,7 +426,7 @@
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
- ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
+ ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp
index fd1dfd5..85cd739 100644
--- a/libs/binder/Value.cpp
+++ b/libs/binder/Value.cpp
@@ -182,10 +182,12 @@
Value& Value::operator=(const Value& rhs)
{
- delete mContent;
- mContent = rhs.mContent
- ? rhs.mContent->clone()
- : NULL;
+ if (this != &rhs) {
+ delete mContent;
+ mContent = rhs.mContent
+ ? rhs.mContent->clone()
+ : NULL;
+ }
return *this;
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 3071408..c451780 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -14,9 +14,20 @@
// limitations under the License.
//
+cc_defaults {
+ name: "binder_test_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-private-field",
+ "-Wno-unused-variable",
+ ],
+}
+
cc_test {
name: "binderDriverInterfaceTest_IPC_32",
srcs: ["binderDriverInterfaceTest.cpp"],
+ defaults: ["binder_test_defaults"],
compile_multilib: "32",
cflags: ["-DBINDER_IPC_32BIT=1"],
}
@@ -30,11 +41,13 @@
name: "binderDriverInterfaceTest",
srcs: ["binderDriverInterfaceTest.cpp"],
+ defaults: ["binder_test_defaults"],
}
cc_test {
name: "binderValueTypeTest",
srcs: ["binderValueTypeTest.cpp"],
+ defaults: ["binder_test_defaults"],
shared_libs: [
"libbinder",
"libutils",
@@ -44,6 +57,7 @@
cc_test {
name: "binderLibTest_IPC_32",
srcs: ["binderLibTest.cpp"],
+ defaults: ["binder_test_defaults"],
shared_libs: [
"libbinder",
"libutils",
@@ -59,6 +73,7 @@
},
},
+ defaults: ["binder_test_defaults"],
name: "binderLibTest",
srcs: ["binderLibTest.cpp"],
shared_libs: [
@@ -70,6 +85,7 @@
cc_test {
name: "binderThroughputTest",
srcs: ["binderThroughputTest.cpp"],
+ defaults: ["binder_test_defaults"],
shared_libs: [
"libbinder",
"libutils",
@@ -77,8 +93,6 @@
clang: true,
cflags: [
"-g",
- "-Wall",
- "-Werror",
"-Wno-missing-field-initializers",
"-Wno-sign-compare",
"-O3",
@@ -88,6 +102,7 @@
cc_test {
name: "binderTextOutputTest",
srcs: ["binderTextOutputTest.cpp"],
+ defaults: ["binder_test_defaults"],
shared_libs: [
"libbinder",
"libutils",
@@ -98,6 +113,7 @@
cc_test {
name: "schd-dbg",
srcs: ["schd-dbg.cpp"],
+ defaults: ["binder_test_defaults"],
shared_libs: [
"libbinder",
"libutils",
@@ -108,9 +124,9 @@
cc_test {
name: "binderSafeInterfaceTest",
srcs: ["binderSafeInterfaceTest.cpp"],
+ defaults: ["binder_test_defaults"],
cppflags: [
- "-Werror",
"-Weverything",
"-Wno-c++98-compat",
"-Wno-c++98-compat-pedantic",
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 9b289c0..4f00bc1 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -149,6 +149,12 @@
ASSERT_EQ(BINDER_CURRENT_PROTOCOL_VERSION, version.protocol_version);
}
+TEST_F(BinderDriverInterfaceTest, OpenNoMmap) {
+ int binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ ASSERT_GE(binderFd, 0);
+ close(binderFd);
+}
+
TEST_F(BinderDriverInterfaceTest, WriteReadNull) {
binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT);
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index a04869a..1611e11 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -28,10 +28,19 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <sys/epoll.h>
+
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
+static ::testing::AssertionResult IsPageAligned(void *buf) {
+ if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
+ return ::testing::AssertionSuccess();
+ else
+ return ::testing::AssertionFailure() << buf << " is not page aligned";
+}
+
static testing::Environment* binder_env;
static char *binderservername;
static char *binderserversuffix;
@@ -43,7 +52,10 @@
BINDER_LIB_TEST_NOP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
BINDER_LIB_TEST_REGISTER_SERVER,
BINDER_LIB_TEST_ADD_SERVER,
+ BINDER_LIB_TEST_ADD_POLL_SERVER,
BINDER_LIB_TEST_CALL_BACK,
+ BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
+ BINDER_LIB_TEST_DELAYED_CALL_BACK,
BINDER_LIB_TEST_NOP_CALL_BACK,
BINDER_LIB_TEST_GET_SELF_TRANSACTION,
BINDER_LIB_TEST_GET_ID_TRANSACTION,
@@ -60,7 +72,7 @@
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
};
-pid_t start_server_process(int arg2)
+pid_t start_server_process(int arg2, bool usePoll = false)
{
int ret;
pid_t pid;
@@ -68,11 +80,13 @@
int pipefd[2];
char stri[16];
char strpipefd1[16];
+ char usepoll[2];
char *childargv[] = {
binderservername,
binderserverarg,
stri,
strpipefd1,
+ usepoll,
binderserversuffix,
NULL
};
@@ -83,6 +97,7 @@
snprintf(stri, sizeof(stri), "%d", arg2);
snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]);
+ snprintf(usepoll, sizeof(usepoll), "%d", usePoll ? 1 : 0);
pid = fork();
if (pid == -1)
@@ -167,14 +182,14 @@
virtual void TearDown() {
}
protected:
- sp<IBinder> addServer(int32_t *idPtr = NULL)
+ sp<IBinder> addServerEtc(int32_t *idPtr, int code)
{
int ret;
int32_t id;
Parcel data, reply;
sp<IBinder> binder;
- ret = m_server->transact(BINDER_LIB_TEST_ADD_SERVER, data, &reply);
+ ret = m_server->transact(code, data, &reply);
EXPECT_EQ(NO_ERROR, ret);
EXPECT_FALSE(binder != NULL);
@@ -186,6 +201,17 @@
*idPtr = id;
return binder;
}
+
+ sp<IBinder> addServer(int32_t *idPtr = NULL)
+ {
+ return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_SERVER);
+ }
+
+ sp<IBinder> addPollServer(int32_t *idPtr = NULL)
+ {
+ return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_POLL_SERVER);
+ }
+
void waitForReadData(int fd, int timeout_ms) {
int ret;
pollfd pfd = pollfd();
@@ -265,17 +291,23 @@
pthread_mutex_unlock(&m_waitMutex);
return ret;
}
+ pthread_t getTriggeringThread()
+ {
+ return m_triggeringThread;
+ }
protected:
void triggerEvent(void) {
pthread_mutex_lock(&m_waitMutex);
pthread_cond_signal(&m_waitCond);
m_eventTriggered = true;
+ m_triggeringThread = pthread_self();
pthread_mutex_unlock(&m_waitMutex);
};
private:
pthread_mutex_t m_waitMutex;
pthread_cond_t m_waitCond;
bool m_eventTriggered;
+ pthread_t m_triggeringThread;
};
class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent
@@ -283,6 +315,7 @@
public:
BinderLibTestCallBack()
: m_result(NOT_ENOUGH_DATA)
+ , m_prev_end(NULL)
{
}
status_t getResult(void)
@@ -298,16 +331,43 @@
(void)reply;
(void)flags;
switch(code) {
- case BINDER_LIB_TEST_CALL_BACK:
- m_result = data.readInt32();
+ case BINDER_LIB_TEST_CALL_BACK: {
+ status_t status = data.readInt32(&m_result);
+ if (status != NO_ERROR) {
+ m_result = status;
+ }
triggerEvent();
return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF: {
+ sp<IBinder> server;
+ int ret;
+ const uint8_t *buf = data.data();
+ size_t size = data.dataSize();
+ if (m_prev_end) {
+ /* 64-bit kernel needs at most 8 bytes to align buffer end */
+ EXPECT_LE((size_t)(buf - m_prev_end), (size_t)8);
+ } else {
+ EXPECT_TRUE(IsPageAligned((void *)buf));
+ }
+
+ m_prev_end = buf + size + data.objectsCount() * sizeof(binder_size_t);
+
+ if (size > 0) {
+ server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+ ret = server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION,
+ data, reply);
+ EXPECT_EQ(NO_ERROR, ret);
+ }
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
}
}
status_t m_result;
+ const uint8_t *m_prev_end;
};
class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent
@@ -606,6 +666,65 @@
}
}
+TEST_F(BinderLibTest, DeathNotificationThread)
+{
+ status_t ret;
+ sp<BinderLibTestCallBack> callback;
+ sp<IBinder> target = addServer();
+ ASSERT_TRUE(target != NULL);
+ sp<IBinder> client = addServer();
+ ASSERT_TRUE(client != NULL);
+
+ sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
+
+ ret = target->linkToDeath(testDeathRecipient);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ {
+ Parcel data, reply;
+ ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
+ EXPECT_EQ(0, ret);
+ }
+
+ /* Make sure it's dead */
+ testDeathRecipient->waitEvent(5);
+
+ /* Now, pass the ref to another process and ask that process to
+ * call linkToDeath() on it, and wait for a response. This tests
+ * two things:
+ * 1) You still get death notifications when calling linkToDeath()
+ * on a ref that is already dead when it was passed to you.
+ * 2) That death notifications are not directly pushed to the thread
+ * registering them, but to the threadpool (proc workqueue) instead.
+ *
+ * 2) is tested because the thread handling BINDER_LIB_TEST_DEATH_TRANSACTION
+ * is blocked on a condition variable waiting for the death notification to be
+ * called; therefore, that thread is not available for handling proc work.
+ * So, if the death notification was pushed to the thread workqueue, the callback
+ * would never be called, and the test would timeout and fail.
+ *
+ * Note that we can't do this part of the test from this thread itself, because
+ * the binder driver would only push death notifications to the thread if
+ * it is a looper thread, which this thread is not.
+ *
+ * See b/23525545 for details.
+ */
+ {
+ Parcel data, reply;
+
+ callback = new BinderLibTestCallBack();
+ data.writeStrongBinder(target);
+ data.writeStrongBinder(callback);
+ ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+ }
+
+ ret = callback->waitEvent(5);
+ EXPECT_EQ(NO_ERROR, ret);
+ ret = callback->getResult();
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
TEST_F(BinderLibTest, PassFile) {
int ret;
int pipefd[2];
@@ -681,7 +800,7 @@
const flat_binder_object *fb = reply.readObject(false);
ASSERT_TRUE(fb != NULL);
- EXPECT_EQ(BINDER_TYPE_HANDLE, fb->type);
+ EXPECT_EQ(BINDER_TYPE_HANDLE, fb->hdr.type);
EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle));
EXPECT_EQ((binder_uintptr_t)0, fb->cookie);
EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32);
@@ -728,6 +847,61 @@
}
}
+TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
+ status_t ret;
+ Parcel data, reply;
+ sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
+ for (int i = 0; i < 2; i++) {
+ BinderLibTestBundle datai;
+ datai.appendFrom(&data, 0, data.dataSize());
+
+ data.freeData();
+ data.writeInt32(1);
+ data.writeStrongBinder(callBack);
+ data.writeInt32(BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF);
+
+ datai.appendTo(&data);
+ }
+ ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, OnewayQueueing)
+{
+ status_t ret;
+ Parcel data, data2;
+
+ sp<IBinder> pollServer = addPollServer();
+
+ sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
+ data.writeStrongBinder(callBack);
+ data.writeInt32(500000); // delay in us before calling back
+
+ sp<BinderLibTestCallBack> callBack2 = new BinderLibTestCallBack();
+ data2.writeStrongBinder(callBack2);
+ data2.writeInt32(0); // delay in us
+
+ ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, NULL, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ // The delay ensures that this second transaction will end up on the async_todo list
+ // (for a single-threaded server)
+ ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, NULL, TF_ONE_WAY);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ // The server will ensure that the two transactions are handled in the expected order;
+ // If the ordering is not as expected, an error will be returned through the callbacks.
+ ret = callBack->waitEvent(2);
+ EXPECT_EQ(NO_ERROR, ret);
+ ret = callBack->getResult();
+ EXPECT_EQ(NO_ERROR, ret);
+
+ ret = callBack2->waitEvent(2);
+ EXPECT_EQ(NO_ERROR, ret);
+ ret = callBack2->getResult();
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -735,6 +909,7 @@
: m_id(id)
, m_nextServerId(id + 1)
, m_serverStartRequested(false)
+ , m_callback(NULL)
{
pthread_mutex_init(&m_serverWaitMutex, NULL);
pthread_cond_init(&m_serverWaitCond, NULL);
@@ -743,6 +918,16 @@
{
exit(EXIT_SUCCESS);
}
+
+ void processPendingCall() {
+ if (m_callback != NULL) {
+ Parcel data;
+ data.writeInt32(NO_ERROR);
+ m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
+ m_callback = NULL;
+ }
+ }
+
virtual status_t onTransact(uint32_t code,
const Parcel& data, Parcel* reply,
uint32_t flags = 0) {
@@ -774,6 +959,7 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_ADD_POLL_SERVER:
case BINDER_LIB_TEST_ADD_SERVER: {
int ret;
uint8_t buf[1] = { 0 };
@@ -788,9 +974,10 @@
} else {
serverid = m_nextServerId++;
m_serverStartRequested = true;
+ bool usePoll = code == BINDER_LIB_TEST_ADD_POLL_SERVER;
pthread_mutex_unlock(&m_serverWaitMutex);
- ret = start_server_process(serverid);
+ ret = start_server_process(serverid, usePoll);
pthread_mutex_lock(&m_serverWaitMutex);
}
if (ret > 0) {
@@ -818,6 +1005,42 @@
}
case BINDER_LIB_TEST_NOP_TRANSACTION:
return NO_ERROR;
+ case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
+ // Note: this transaction is only designed for use with a
+ // poll() server. See comments around epoll_wait().
+ if (m_callback != NULL) {
+ // A callback was already pending; this means that
+ // we received a second call while still processing
+ // the first one. Fail the test.
+ sp<IBinder> callback = data.readStrongBinder();
+ Parcel data2;
+ data2.writeInt32(UNKNOWN_ERROR);
+
+ callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, NULL, TF_ONE_WAY);
+ } else {
+ m_callback = data.readStrongBinder();
+ int32_t delayUs = data.readInt32();
+ /*
+ * It's necessary that we sleep here, so the next
+ * transaction the caller makes will be queued to
+ * the async queue.
+ */
+ usleep(delayUs);
+
+ /*
+ * Now when we return, libbinder will tell the kernel
+ * we are done with this transaction, and the kernel
+ * can move the queued transaction to either the
+ * thread todo worklist (for kernels without the fix),
+ * or the proc todo worklist. In case of the former,
+ * the next outbound call will pick up the pending
+ * transaction, which leads to undesired reentrant
+ * behavior. This is caught in the if() branch above.
+ */
+ }
+
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_NOP_CALL_BACK: {
Parcel data2, reply2;
sp<IBinder> binder;
@@ -825,7 +1048,7 @@
if (binder == NULL) {
return BAD_VALUE;
}
- reply2.writeInt32(NO_ERROR);
+ data2.writeInt32(NO_ERROR);
binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
return NO_ERROR;
}
@@ -967,16 +1190,26 @@
bool m_serverStartRequested;
sp<IBinder> m_serverStarted;
sp<IBinder> m_strongRef;
+ bool m_callbackPending;
+ sp<IBinder> m_callback;
};
-int run_server(int index, int readypipefd)
+int run_server(int index, int readypipefd, bool usePoll)
{
binderLibTestServiceName += String16(binderserversuffix);
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
+ BinderLibTestService* testServicePtr;
{
sp<BinderLibTestService> testService = new BinderLibTestService(index);
+ /*
+ * We need this below, but can't hold a sp<> because it prevents the
+ * node from being cleaned up automatically. It's safe in this case
+ * because of how the tests are written.
+ */
+ testServicePtr = testService.get();
+
if (index == 0) {
ret = sm->addService(binderLibTestServiceName, testService);
} else {
@@ -994,8 +1227,53 @@
if (ret)
return 1;
//printf("%s: joinThreadPool\n", __func__);
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
+ if (usePoll) {
+ int fd;
+ struct epoll_event ev;
+ int epoll_fd;
+ IPCThreadState::self()->setupPolling(&fd);
+ if (fd < 0) {
+ return 1;
+ }
+ IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER
+
+ epoll_fd = epoll_create1(0);
+ if (epoll_fd == -1) {
+ return 1;
+ }
+
+ ev.events = EPOLLIN;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ return 1;
+ }
+
+ while (1) {
+ /*
+ * We simulate a single-threaded process using the binder poll
+ * interface; besides handling binder commands, it can also
+ * issue outgoing transactions, by storing a callback in
+ * m_callback and setting m_callbackPending.
+ *
+ * processPendingCall() will then issue that transaction.
+ */
+ struct epoll_event events[1];
+ int numEvents = epoll_wait(epoll_fd, events, 1, 1000);
+ if (numEvents < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ return 1;
+ }
+ if (numEvents > 0) {
+ IPCThreadState::self()->handlePolledCommands();
+ IPCThreadState::self()->flushCommands(); // flush BC_FREE_BUFFER
+ testServicePtr->processPendingCall();
+ }
+ }
+ } else {
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ }
//printf("%s: joinThreadPool returned\n", __func__);
return 1; /* joinThreadPool should not return */
}
@@ -1009,9 +1287,9 @@
binderservername = argv[0];
}
- if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
- binderserversuffix = argv[4];
- return run_server(atoi(argv[2]), atoi(argv[3]));
+ if (argc == 6 && !strcmp(argv[1], binderserverarg)) {
+ binderserversuffix = argv[5];
+ return run_server(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) == 1);
}
binderserversuffix = new char[16];
snprintf(binderserversuffix, 16, "%d", getpid());
diff --git a/libs/diskusage/Android.bp b/libs/diskusage/Android.bp
index 156ddff..a826306 100644
--- a/libs/diskusage/Android.bp
+++ b/libs/diskusage/Android.bp
@@ -15,4 +15,5 @@
cc_library_static {
name: "libdiskusage",
srcs: ["dirsize.c"],
+ cflags: ["-Wall", "-Werror"],
}
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index f2686d5..9f99538 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -19,6 +19,8 @@
"GraphicsEnv.cpp",
],
+ cflags: ["-Wall", "-Werror"],
+
shared_libs: [
"libnativeloader",
"liblog",
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3996305..cf72d55 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -25,9 +25,12 @@
},
clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
cppflags: [
"-Weverything",
- "-Werror",
// The static constructors and destructors in this library have not been noted to
// introduce significant overheads
@@ -96,6 +99,7 @@
"IProducerListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
+ "LayerDebugInfo.cpp",
"LayerState.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 625dc5b..c5cab2d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -764,7 +764,7 @@
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
- Region surfaceDamage = input.getSurfaceDamage();
+ const Region& surfaceDamage = input.getSurfaceDamage();
if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a0d112..8e7f814 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -28,6 +28,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
+#include <gui/LayerDebugInfo.h>
#include <private/gui/LayerState.h>
@@ -469,6 +470,36 @@
return result;
}
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
+ {
+ if (!outLayers) {
+ return UNEXPECTED_NULL;
+ }
+
+ Parcel data, reply;
+
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int32_t result = 0;
+ err = reply.readInt32(&result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ outLayers->clear();
+ return reply.readParcelableVector(outLayers);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -763,6 +794,17 @@
}
return injectVSync(when);
}
+ case GET_LAYER_DEBUG_INFO: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ std::vector<LayerDebugInfo> outLayers;
+ status_t result = getLayerDebugInfo(&outLayers);
+ reply->writeInt32(result);
+ if (result == NO_ERROR)
+ {
+ result = reply->writeParcelableVector(outLayers);
+ }
+ return result;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
new file mode 100644
index 0000000..57ddde0
--- /dev/null
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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 <gui/LayerDebugInfo.h>
+
+#include <ui/DebugUtils.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/String8.h>
+
+using namespace android;
+
+#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)
+
+namespace android {
+
+status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const {
+ RETURN_ON_ERROR(parcel->writeCString(mName.c_str()));
+ RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str()));
+ RETURN_ON_ERROR(parcel->writeCString(mType.c_str()));
+ RETURN_ON_ERROR(parcel->write(mTransparentRegion));
+ RETURN_ON_ERROR(parcel->write(mVisibleRegion));
+ RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion));
+ RETURN_ON_ERROR(parcel->writeUint32(mLayerStack));
+ RETURN_ON_ERROR(parcel->writeFloat(mX));
+ RETURN_ON_ERROR(parcel->writeFloat(mY));
+ RETURN_ON_ERROR(parcel->writeUint32(mZ));
+ RETURN_ON_ERROR(parcel->writeInt32(mWidth));
+ RETURN_ON_ERROR(parcel->writeInt32(mHeight));
+ RETURN_ON_ERROR(parcel->write(mCrop));
+ RETURN_ON_ERROR(parcel->write(mFinalCrop));
+ RETURN_ON_ERROR(parcel->writeFloat(mAlpha));
+ RETURN_ON_ERROR(parcel->writeUint32(mFlags));
+ RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat));
+ RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace)));
+ for (size_t index = 0; index < 4; index++) {
+ RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2]));
+ }
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth));
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight));
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride));
+ RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat));
+ RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames));
+ RETURN_ON_ERROR(parcel->writeBool(mRefreshPending));
+ RETURN_ON_ERROR(parcel->writeBool(mIsOpaque));
+ RETURN_ON_ERROR(parcel->writeBool(mContentDirty));
+ return NO_ERROR;
+}
+
+status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) {
+ mName = parcel->readCString();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mParentName = parcel->readCString();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mType = parcel->readCString();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ RETURN_ON_ERROR(parcel->read(mTransparentRegion));
+ RETURN_ON_ERROR(parcel->read(mVisibleRegion));
+ RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion));
+ RETURN_ON_ERROR(parcel->readUint32(&mLayerStack));
+ RETURN_ON_ERROR(parcel->readFloat(&mX));
+ RETURN_ON_ERROR(parcel->readFloat(&mY));
+ RETURN_ON_ERROR(parcel->readUint32(&mZ));
+ RETURN_ON_ERROR(parcel->readInt32(&mWidth));
+ RETURN_ON_ERROR(parcel->readInt32(&mHeight));
+ RETURN_ON_ERROR(parcel->read(mCrop));
+ RETURN_ON_ERROR(parcel->read(mFinalCrop));
+ RETURN_ON_ERROR(parcel->readFloat(&mAlpha));
+ RETURN_ON_ERROR(parcel->readUint32(&mFlags));
+ RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat));
+ // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways?
+ mDataSpace = static_cast<android_dataspace>(parcel->readUint32());
+ RETURN_ON_ERROR(parcel->errorCheck());
+ for (size_t index = 0; index < 4; index++) {
+ RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2]));
+ }
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth));
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight));
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride));
+ RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat));
+ RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames));
+ RETURN_ON_ERROR(parcel->readBool(&mRefreshPending));
+ RETURN_ON_ERROR(parcel->readBool(&mIsOpaque));
+ RETURN_ON_ERROR(parcel->readBool(&mContentDirty));
+ return NO_ERROR;
+}
+
+std::string to_string(const LayerDebugInfo& info) {
+ String8 result;
+
+ result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
+ info.mTransparentRegion.dump(result, "TransparentRegion");
+ info.mVisibleRegion.dump(result, "VisibleRegion");
+ info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
+
+ result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+ info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY),
+ info.mWidth, info.mHeight);
+
+ result.appendFormat("crop=%s, finalCrop=%s, ",
+ to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str());
+ result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
+ result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
+ result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
+ result.appendFormat("alpha=%.3f, flags=0x%08x, ",
+ static_cast<double>(info.mAlpha), info.mFlags);
+ result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]",
+ static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]),
+ static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1]));
+ result.append("\n");
+ result.appendFormat(" parent=%s\n", info.mParentName.c_str());
+ result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],",
+ info.mActiveBufferWidth, info.mActiveBufferHeight,
+ info.mActiveBufferStride,
+ decodePixelFormat(info.mActiveBufferFormat).c_str());
+ result.appendFormat(" queued-frames=%d, mRefreshPending=%d",
+ info.mNumQueuedFrames, info.mRefreshPending);
+ result.append("\n");
+ return std::string(result.c_str());
+}
+
+} // android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 5b1c599..d9d945d 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -153,7 +153,10 @@
ATRACE_CALL();
DisplayStatInfo stats;
- status_t err = composerService()->getDisplayStats(NULL, &stats);
+ status_t result = composerService()->getDisplayStats(NULL, &stats);
+ if (result != NO_ERROR) {
+ return result;
+ }
*outRefreshDuration = stats.vsyncPeriod;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index f80ba00..b226742 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -39,6 +39,7 @@
struct DisplayState;
struct DisplayInfo;
struct DisplayStatInfo;
+class LayerDebugInfo;
class HdrCapabilities;
class IDisplayEventConnection;
class IGraphicBufferProducer;
@@ -195,6 +196,12 @@
virtual status_t enableVSyncInjections(bool enable) = 0;
virtual status_t injectVSync(nsecs_t when) = 0;
+
+ /* Gets the list of active layers in Z order for debugging purposes
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
};
// ----------------------------------------------------------------------------
@@ -229,6 +236,7 @@
SET_ACTIVE_COLOR_MODE,
ENABLE_VSYNC_INJECTIONS,
INJECT_VSYNC,
+ GET_LAYER_DEBUG_INFO,
CREATE_SCOPED_CONNECTION
};
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
new file mode 100644
index 0000000..8453e04
--- /dev/null
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/Region.h>
+
+#include <string>
+
+namespace android {
+
+/* Class for transporting debug info from SurfaceFlinger to authorized
+ * recipients. The class is intended to be a data container. There are
+ * no getters or setters.
+ */
+class LayerDebugInfo : public Parcelable {
+public:
+ LayerDebugInfo() = default;
+ LayerDebugInfo(const LayerDebugInfo&) = default;
+ virtual ~LayerDebugInfo() = default;
+
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
+
+ std::string mName = std::string("NOT FILLED");
+ std::string mParentName = std::string("NOT FILLED");
+ std::string mType = std::string("NOT FILLED");
+ Region mTransparentRegion = Region::INVALID_REGION;
+ Region mVisibleRegion = Region::INVALID_REGION;
+ Region mSurfaceDamageRegion = Region::INVALID_REGION;
+ uint32_t mLayerStack = 0;
+ float mX = 0.f;
+ float mY = 0.f;
+ uint32_t mZ = 0 ;
+ int32_t mWidth = -1;
+ int32_t mHeight = -1;
+ Rect mCrop = Rect::INVALID_RECT;
+ Rect mFinalCrop = Rect::INVALID_RECT;
+ float mAlpha = 0.f;
+ uint32_t mFlags = 0;
+ PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
+ android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN;
+ // Row-major transform matrix (SurfaceControl::setMatrix())
+ float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}};
+ int32_t mActiveBufferWidth = -1;
+ int32_t mActiveBufferHeight = -1;
+ int32_t mActiveBufferStride = 0;
+ PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE;
+ int32_t mNumQueuedFrames = -1;
+ bool mRefreshPending = false;
+ bool mIsOpaque = false;
+ bool mContentDirty = false;
+};
+
+std::string to_string(const LayerDebugInfo& info);
+
+} // namespace android
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 8bb705c..c15209d 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -90,6 +90,16 @@
status_t setFlags(uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(const Region& transparent);
status_t setAlpha(float alpha=1.0f);
+
+ // Experimentarily it appears that the matrix transforms the
+ // on-screen rectangle and it's contents before the position is
+ // applied.
+ //
+ // TODO: Test with other combinations to find approximate transformation rules.
+ //
+ // For example:
+ // Layer sized (W,H) set to position (x,y) with matrix M=[-1, 0, 0, 1] (Horizontal flip) gives
+ // [((0, 0), (W, H)) x M] + (x,y) = ((-W, 0), (0, H)) + (x,y) = ((-W + x, y), (x, H+y))
status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy);
status_t setCrop(const Rect& crop);
status_t setFinalCrop(const Rect& crop);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index fa87f29..908959c 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -7,6 +7,10 @@
test_suites: ["device-tests"],
clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
srcs: [
"BufferItemConsumer_test.cpp",
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 0982d7e..588e541 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -310,8 +310,6 @@
uint32_t h = buf.height;
const int blockWidth = w > 16 ? w / 16 : 1;
const int blockHeight = h > 16 ? h / 16 : 1;
- const int blockRows = h / blockHeight;
- const int blockCols = w / blockWidth;
// Top-left square is bright
checkPixel(buf, 0, 0, 191);
@@ -349,8 +347,6 @@
uint32_t h = buf.height;
const int blockWidth = w > 16 ? w / 16 : 1;
const int blockHeight = h > 16 ? h / 16 : 1;
- const int blockRows = h / blockHeight;
- const int blockCols = w / blockWidth;
// Top-left square is bright red
checkPixel(buf, 0, 0, 191, 63, 63);
@@ -392,8 +388,6 @@
uint32_t h = buf.height;
const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1;
const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1;
- const int blockRows = h / blockHeight;
- const int blockCols = w / blockWidth;
// Top-left square is red
checkPixel(buf, 0, 0, 1000, 200, 200);
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index c6745d0..5639286 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -323,7 +323,6 @@
for (int j = 0; j < numTestPixels; j++) {
int x = testPixels[j].x;
int y = testPixels[j].y;
- uint8_t value = 0;
if (j == (i % numTestPixels)) {
// We must y-invert the texture coords
EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255));
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e18af17..ca43c68 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -539,6 +539,9 @@
return NO_ERROR;
}
status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -1075,7 +1078,6 @@
EXPECT_EQ(initialCompositorTiming.presentLatency,
compositeToPresentLatency);
- const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
addFrameEvents(true, NO_FRAME_INDEX, 0);
@@ -1089,7 +1091,6 @@
EXPECT_EQ(initialCompositorTiming.presentLatency,
compositeToPresentLatency);
- const uint64_t fId2 = getNextFrameId();
dequeueAndQueue(1);
addFrameEvents(true, 0, 1);
@@ -1162,7 +1163,6 @@
nsecs_t expectedDeadline = initialCompositorTiming.deadline;
EXPECT_EQ(expectedDeadline, compositeDeadline);
- const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
addFrameEvents(true, NO_FRAME_INDEX, 0);
@@ -1175,7 +1175,6 @@
initialCompositorTiming.deadline +initialCompositorTiming.interval;
EXPECT_EQ(expectedDeadline, compositeDeadline);
- const uint64_t fId2 = getNextFrameId();
dequeueAndQueue(1);
addFrameEvents(true, 0, 1);
diff --git a/libs/hwc2on1adapter/Android.bp b/libs/hwc2on1adapter/Android.bp
index ec9cbf8..420a1f6 100644
--- a/libs/hwc2on1adapter/Android.bp
+++ b/libs/hwc2on1adapter/Android.bp
@@ -17,9 +17,13 @@
vendor: true,
clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-user-defined-warnings",
+ ],
cppflags: [
"-Weverything",
- "-Wall",
"-Wunused",
"-Wunreachable-code",
diff --git a/libs/hwc2onfbadapter/Android.bp b/libs/hwc2onfbadapter/Android.bp
new file mode 100644
index 0000000..73a41f7
--- /dev/null
+++ b/libs/hwc2onfbadapter/Android.bp
@@ -0,0 +1,33 @@
+// Copyright 2010 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.
+
+cc_library_shared {
+ name: "libhwc2onfbadapter",
+ vendor: true,
+
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+
+ srcs: [
+ "HWC2OnFbAdapter.cpp",
+ ],
+
+ header_libs: ["libhardware_headers"],
+ shared_libs: ["liblog", "libsync"],
+ export_include_dirs: ["include"],
+}
diff --git a/libs/hwc2onfbadapter/HWC2OnFbAdapter.cpp b/libs/hwc2onfbadapter/HWC2OnFbAdapter.cpp
new file mode 100644
index 0000000..7c9e651
--- /dev/null
+++ b/libs/hwc2onfbadapter/HWC2OnFbAdapter.cpp
@@ -0,0 +1,887 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HWC2OnFbAdapter"
+
+//#define LOG_NDEBUG 0
+
+#include "hwc2onfbadapter/HWC2OnFbAdapter.h"
+
+#include <algorithm>
+#include <type_traits>
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/prctl.h>
+#include <unistd.h> // for close
+
+#include <hardware/fb.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+namespace android {
+
+namespace {
+
+void dumpHook(hwc2_device_t* device, uint32_t* outSize, char* outBuffer) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (outBuffer) {
+ *outSize = adapter.getDebugString().copy(outBuffer, *outSize);
+ } else {
+ adapter.updateDebugString();
+ *outSize = adapter.getDebugString().size();
+ }
+}
+
+int32_t registerCallbackHook(hwc2_device_t* device, int32_t descriptor,
+ hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ switch (descriptor) {
+ case HWC2_CALLBACK_HOTPLUG:
+ if (pointer) {
+ reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer)(callbackData, adapter.getDisplayId(),
+ HWC2_CONNECTION_CONNECTED);
+ }
+ break;
+ case HWC2_CALLBACK_REFRESH:
+ break;
+ case HWC2_CALLBACK_VSYNC:
+ adapter.setVsyncCallback(reinterpret_cast<HWC2_PFN_VSYNC>(pointer), callbackData);
+ break;
+ default:
+ return HWC2_ERROR_BAD_PARAMETER;
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* /*device*/) {
+ return 0;
+}
+
+int32_t createVirtualDisplayHook(hwc2_device_t* /*device*/, uint32_t /*width*/, uint32_t /*height*/,
+ int32_t* /*format*/, hwc2_display_t* /*outDisplay*/) {
+ return HWC2_ERROR_NO_RESOURCES;
+}
+
+int32_t destroyVirtualDisplayHook(hwc2_device_t* /*device*/, hwc2_display_t /*display*/) {
+ return HWC2_ERROR_BAD_DISPLAY;
+}
+
+int32_t setOutputBufferHook(hwc2_device_t* /*device*/, hwc2_display_t /*display*/,
+ buffer_handle_t /*buffer*/, int32_t /*releaseFence*/) {
+ return HWC2_ERROR_BAD_DISPLAY;
+}
+
+int32_t getDisplayNameHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outSize,
+ char* outName) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ const auto& info = adapter.getInfo();
+ if (outName) {
+ *outSize = info.name.copy(outName, *outSize);
+ } else {
+ *outSize = info.name.size();
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayTypeHook(hwc2_device_t* device, hwc2_display_t display, int32_t* outType) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ *outType = HWC2_DISPLAY_TYPE_PHYSICAL;
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getDozeSupportHook(hwc2_device_t* device, hwc2_display_t display, int32_t* outSupport) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ *outSupport = 0;
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getHdrCapabilitiesHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumTypes,
+ int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
+ float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ *outNumTypes = 0;
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setPowerModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t /*mode*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ // pretend that it works
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setVsyncEnabledHook(hwc2_device_t* device, hwc2_display_t display, int32_t enabled) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ adapter.enableVsync(enabled == HWC2_VSYNC_ENABLE);
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getColorModesHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumModes,
+ int32_t* outModes) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ if (outModes) {
+ if (*outNumModes > 0) {
+ outModes[0] = HAL_COLOR_MODE_NATIVE;
+ *outNumModes = 1;
+ }
+ } else {
+ *outNumModes = 1;
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setColorModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t mode) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (mode != HAL_COLOR_MODE_NATIVE) {
+ return HWC2_ERROR_BAD_PARAMETER;
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setColorTransformHook(hwc2_device_t* device, hwc2_display_t display,
+ const float* /*matrix*/, int32_t /*hint*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ // we always force client composition
+ adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getClientTargetSupportHook(hwc2_device_t* device, hwc2_display_t display, uint32_t width,
+ uint32_t height, int32_t format, int32_t dataspace) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (dataspace != HAL_DATASPACE_UNKNOWN) {
+ return HWC2_ERROR_UNSUPPORTED;
+ }
+
+ const auto& info = adapter.getInfo();
+ return (info.width == width && info.height == height && info.format == format)
+ ? HWC2_ERROR_NONE
+ : HWC2_ERROR_UNSUPPORTED;
+}
+
+int32_t setClientTargetHook(hwc2_device_t* device, hwc2_display_t display, buffer_handle_t target,
+ int32_t acquireFence, int32_t dataspace, hwc_region_t /*damage*/) {
+ if (acquireFence >= 0) {
+ sync_wait(acquireFence, -1);
+ close(acquireFence);
+ }
+
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (dataspace != HAL_DATASPACE_UNKNOWN) {
+ return HWC2_ERROR_BAD_PARAMETER;
+ }
+
+ // no state change
+ adapter.setBuffer(target);
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayConfigsHook(hwc2_device_t* device, hwc2_display_t display,
+ uint32_t* outNumConfigs, hwc2_config_t* outConfigs) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ if (outConfigs) {
+ if (*outNumConfigs > 0) {
+ outConfigs[0] = adapter.getConfigId();
+ *outNumConfigs = 1;
+ }
+ } else {
+ *outNumConfigs = 1;
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayAttributeHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config,
+ int32_t attribute, int32_t* outValue) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (adapter.getConfigId() != config) {
+ return HWC2_ERROR_BAD_CONFIG;
+ }
+
+ const auto& info = adapter.getInfo();
+ switch (attribute) {
+ case HWC2_ATTRIBUTE_WIDTH:
+ *outValue = int32_t(info.width);
+ break;
+ case HWC2_ATTRIBUTE_HEIGHT:
+ *outValue = int32_t(info.height);
+ break;
+ case HWC2_ATTRIBUTE_VSYNC_PERIOD:
+ *outValue = int32_t(info.vsync_period_ns);
+ break;
+ case HWC2_ATTRIBUTE_DPI_X:
+ *outValue = int32_t(info.xdpi_scaled);
+ break;
+ case HWC2_ATTRIBUTE_DPI_Y:
+ *outValue = int32_t(info.ydpi_scaled);
+ break;
+ default:
+ return HWC2_ERROR_BAD_PARAMETER;
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getActiveConfigHook(hwc2_device_t* device, hwc2_display_t display,
+ hwc2_config_t* outConfig) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ *outConfig = adapter.getConfigId();
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setActiveConfigHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (adapter.getConfigId() != config) {
+ return HWC2_ERROR_BAD_CONFIG;
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t validateDisplayHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ const auto& dirtyLayers = adapter.getDirtyLayers();
+ *outNumTypes = dirtyLayers.size();
+ *outNumRequests = 0;
+
+ if (*outNumTypes > 0) {
+ adapter.setState(HWC2OnFbAdapter::State::VALIDATED_WITH_CHANGES);
+ return HWC2_ERROR_HAS_CHANGES;
+ } else {
+ adapter.setState(HWC2OnFbAdapter::State::VALIDATED);
+ return HWC2_ERROR_NONE;
+ }
+}
+
+int32_t getChangedCompositionTypesHook(hwc2_device_t* device, hwc2_display_t display,
+ uint32_t* outNumElements, hwc2_layer_t* outLayers,
+ int32_t* outTypes) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) {
+ return HWC2_ERROR_NOT_VALIDATED;
+ }
+
+ // request client composition for all layers
+ const auto& dirtyLayers = adapter.getDirtyLayers();
+ if (outLayers && outTypes) {
+ *outNumElements = std::min(*outNumElements, uint32_t(dirtyLayers.size()));
+ auto iter = dirtyLayers.cbegin();
+ for (uint32_t i = 0; i < *outNumElements; i++) {
+ outLayers[i] = *iter++;
+ outTypes[i] = HWC2_COMPOSITION_CLIENT;
+ }
+ } else {
+ *outNumElements = dirtyLayers.size();
+ }
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayRequestsHook(hwc2_device_t* device, hwc2_display_t display,
+ int32_t* outDisplayRequests, uint32_t* outNumElements,
+ hwc2_layer_t* /*outLayers*/, int32_t* /*outLayerRequests*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) {
+ return HWC2_ERROR_NOT_VALIDATED;
+ }
+
+ *outDisplayRequests = 0;
+ *outNumElements = 0;
+ return HWC2_ERROR_NONE;
+}
+
+int32_t acceptDisplayChangesHook(hwc2_device_t* device, hwc2_display_t display) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) {
+ return HWC2_ERROR_NOT_VALIDATED;
+ }
+
+ adapter.clearDirtyLayers();
+ adapter.setState(HWC2OnFbAdapter::State::VALIDATED);
+ return HWC2_ERROR_NONE;
+}
+
+int32_t presentDisplayHook(hwc2_device_t* device, hwc2_display_t display,
+ int32_t* outPresentFence) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (adapter.getState() != HWC2OnFbAdapter::State::VALIDATED) {
+ return HWC2_ERROR_NOT_VALIDATED;
+ }
+
+ adapter.postBuffer();
+ *outPresentFence = -1;
+
+ return HWC2_ERROR_NONE;
+}
+
+int32_t getReleaseFencesHook(hwc2_device_t* device, hwc2_display_t display,
+ uint32_t* outNumElements, hwc2_layer_t* /*outLayers*/,
+ int32_t* /*outFences*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ *outNumElements = 0;
+ return HWC2_ERROR_NONE;
+}
+
+int32_t createLayerHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t* outLayer) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ *outLayer = adapter.addLayer();
+ adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+ return HWC2_ERROR_NONE;
+}
+
+int32_t destroyLayerHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ if (adapter.removeLayer(layer)) {
+ adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+ return HWC2_ERROR_NONE;
+ } else {
+ return HWC2_ERROR_BAD_LAYER;
+ }
+}
+
+int32_t setCursorPositionHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t /*layer*/,
+ int32_t /*x*/, int32_t /*y*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+
+ // always an error
+ return HWC2_ERROR_BAD_LAYER;
+}
+
+int32_t setLayerBufferHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer,
+ buffer_handle_t /*buffer*/, int32_t acquireFence) {
+ if (acquireFence >= 0) {
+ sync_wait(acquireFence, -1);
+ close(acquireFence);
+ }
+
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (!adapter.hasLayer(layer)) {
+ return HWC2_ERROR_BAD_LAYER;
+ }
+
+ // no state change
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setLayerSurfaceDamageHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer,
+ hwc_region_t /*damage*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (!adapter.hasLayer(layer)) {
+ return HWC2_ERROR_BAD_LAYER;
+ }
+
+ // no state change
+ return HWC2_ERROR_NONE;
+}
+
+int32_t setLayerCompositionTypeHook(hwc2_device_t* device, hwc2_display_t display,
+ hwc2_layer_t layer, int32_t type) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (!adapter.markLayerDirty(layer, type != HWC2_COMPOSITION_CLIENT)) {
+ return HWC2_ERROR_BAD_LAYER;
+ }
+
+ adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+ return HWC2_ERROR_NONE;
+}
+
+template <typename... Args>
+int32_t setLayerStateHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer,
+ Args... /*args*/) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ if (adapter.getDisplayId() != display) {
+ return HWC2_ERROR_BAD_DISPLAY;
+ }
+ if (!adapter.hasLayer(layer)) {
+ return HWC2_ERROR_BAD_LAYER;
+ }
+
+ adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+ return HWC2_ERROR_NONE;
+}
+
+template <typename PFN, typename T>
+static hwc2_function_pointer_t asFP(T function) {
+ static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
+ return reinterpret_cast<hwc2_function_pointer_t>(function);
+}
+
+hwc2_function_pointer_t getFunctionHook(hwc2_device_t* /*device*/, int32_t descriptor) {
+ switch (descriptor) {
+ // global functions
+ case HWC2_FUNCTION_DUMP:
+ return asFP<HWC2_PFN_DUMP>(dumpHook);
+ case HWC2_FUNCTION_REGISTER_CALLBACK:
+ return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
+
+ // virtual display functions
+ case HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT:
+ return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(getMaxVirtualDisplayCountHook);
+ case HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY:
+ return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(createVirtualDisplayHook);
+ case HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY:
+ return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(destroyVirtualDisplayHook);
+ case HWC2_FUNCTION_SET_OUTPUT_BUFFER:
+ return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(setOutputBufferHook);
+
+ // display functions
+ case HWC2_FUNCTION_GET_DISPLAY_NAME:
+ return asFP<HWC2_PFN_GET_DISPLAY_NAME>(getDisplayNameHook);
+ case HWC2_FUNCTION_GET_DISPLAY_TYPE:
+ return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(getDisplayTypeHook);
+ case HWC2_FUNCTION_GET_DOZE_SUPPORT:
+ return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(getDozeSupportHook);
+ case HWC2_FUNCTION_GET_HDR_CAPABILITIES:
+ return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(getHdrCapabilitiesHook);
+ case HWC2_FUNCTION_SET_POWER_MODE:
+ return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
+ case HWC2_FUNCTION_SET_VSYNC_ENABLED:
+ return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
+ case HWC2_FUNCTION_GET_COLOR_MODES:
+ return asFP<HWC2_PFN_GET_COLOR_MODES>(getColorModesHook);
+ case HWC2_FUNCTION_SET_COLOR_MODE:
+ return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
+ case HWC2_FUNCTION_SET_COLOR_TRANSFORM:
+ return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
+ case HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT:
+ return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(getClientTargetSupportHook);
+ case HWC2_FUNCTION_SET_CLIENT_TARGET:
+ return asFP<HWC2_PFN_SET_CLIENT_TARGET>(setClientTargetHook);
+
+ // config functions
+ case HWC2_FUNCTION_GET_DISPLAY_CONFIGS:
+ return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(getDisplayConfigsHook);
+ case HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE:
+ return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(getDisplayAttributeHook);
+ case HWC2_FUNCTION_GET_ACTIVE_CONFIG:
+ return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(getActiveConfigHook);
+ case HWC2_FUNCTION_SET_ACTIVE_CONFIG:
+ return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(setActiveConfigHook);
+
+ // validate/present functions
+ case HWC2_FUNCTION_VALIDATE_DISPLAY:
+ return asFP<HWC2_PFN_VALIDATE_DISPLAY>(validateDisplayHook);
+ case HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES:
+ return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(getChangedCompositionTypesHook);
+ case HWC2_FUNCTION_GET_DISPLAY_REQUESTS:
+ return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(getDisplayRequestsHook);
+ case HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES:
+ return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(acceptDisplayChangesHook);
+ case HWC2_FUNCTION_PRESENT_DISPLAY:
+ return asFP<HWC2_PFN_PRESENT_DISPLAY>(presentDisplayHook);
+ case HWC2_FUNCTION_GET_RELEASE_FENCES:
+ return asFP<HWC2_PFN_GET_RELEASE_FENCES>(getReleaseFencesHook);
+
+ // layer create/destroy
+ case HWC2_FUNCTION_CREATE_LAYER:
+ return asFP<HWC2_PFN_CREATE_LAYER>(createLayerHook);
+ case HWC2_FUNCTION_DESTROY_LAYER:
+ return asFP<HWC2_PFN_DESTROY_LAYER>(destroyLayerHook);
+
+ // layer functions; validateDisplay not required
+ case HWC2_FUNCTION_SET_CURSOR_POSITION:
+ return asFP<HWC2_PFN_SET_CURSOR_POSITION>(setCursorPositionHook);
+ case HWC2_FUNCTION_SET_LAYER_BUFFER:
+ return asFP<HWC2_PFN_SET_LAYER_BUFFER>(setLayerBufferHook);
+ case HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE:
+ return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(setLayerSurfaceDamageHook);
+
+ // layer state functions; validateDisplay required
+ case HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE:
+ return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(setLayerCompositionTypeHook);
+ case HWC2_FUNCTION_SET_LAYER_BLEND_MODE:
+ return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(setLayerStateHook<int32_t>);
+ case HWC2_FUNCTION_SET_LAYER_COLOR:
+ return asFP<HWC2_PFN_SET_LAYER_COLOR>(setLayerStateHook<hwc_color_t>);
+ case HWC2_FUNCTION_SET_LAYER_DATASPACE:
+ return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerStateHook<int32_t>);
+ case HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME:
+ return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(setLayerStateHook<hwc_rect_t>);
+ case HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA:
+ return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(setLayerStateHook<float>);
+ case HWC2_FUNCTION_SET_LAYER_SIDEBAND_STREAM:
+ return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(setLayerStateHook<buffer_handle_t>);
+ case HWC2_FUNCTION_SET_LAYER_SOURCE_CROP:
+ return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(setLayerStateHook<hwc_frect_t>);
+ case HWC2_FUNCTION_SET_LAYER_TRANSFORM:
+ return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerStateHook<int32_t>);
+ case HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION:
+ return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(setLayerStateHook<hwc_region_t>);
+ case HWC2_FUNCTION_SET_LAYER_Z_ORDER:
+ return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerStateHook<uint32_t>);
+
+ default:
+ ALOGE("unknown function descriptor %d", descriptor);
+ return nullptr;
+ }
+}
+
+void getCapabilitiesHook(hwc2_device_t* /*device*/, uint32_t* outCount,
+ int32_t* /*outCapabilities*/) {
+ *outCount = 0;
+}
+
+int closeHook(hw_device_t* device) {
+ auto& adapter = HWC2OnFbAdapter::cast(device);
+ adapter.close();
+ return 0;
+}
+
+} // anonymous namespace
+
+HWC2OnFbAdapter::HWC2OnFbAdapter(framebuffer_device_t* fbDevice)
+ : hwc2_device_t(), mFbDevice(fbDevice) {
+ common.close = closeHook;
+ hwc2_device::getCapabilities = getCapabilitiesHook;
+ hwc2_device::getFunction = getFunctionHook;
+
+ mFbInfo.name = "fbdev";
+ mFbInfo.width = mFbDevice->width;
+ mFbInfo.height = mFbDevice->height;
+ mFbInfo.format = mFbDevice->format;
+ mFbInfo.vsync_period_ns = int(1e9 / mFbDevice->fps);
+ mFbInfo.xdpi_scaled = int(mFbDevice->xdpi * 1000.0f);
+ mFbInfo.ydpi_scaled = int(mFbDevice->ydpi * 1000.0f);
+
+ mVsyncThread.start(0, mFbInfo.vsync_period_ns);
+}
+
+HWC2OnFbAdapter& HWC2OnFbAdapter::cast(hw_device_t* device) {
+ return *reinterpret_cast<HWC2OnFbAdapter*>(device);
+}
+
+HWC2OnFbAdapter& HWC2OnFbAdapter::cast(hwc2_device_t* device) {
+ return *reinterpret_cast<HWC2OnFbAdapter*>(device);
+}
+
+hwc2_display_t HWC2OnFbAdapter::getDisplayId() {
+ return 0;
+}
+
+hwc2_config_t HWC2OnFbAdapter::getConfigId() {
+ return 0;
+}
+
+void HWC2OnFbAdapter::close() {
+ mVsyncThread.stop();
+ framebuffer_close(mFbDevice);
+}
+
+const HWC2OnFbAdapter::Info& HWC2OnFbAdapter::getInfo() const {
+ return mFbInfo;
+}
+
+void HWC2OnFbAdapter::updateDebugString() {
+ if (mFbDevice->common.version >= 1 && mFbDevice->dump) {
+ char buffer[4096];
+ mFbDevice->dump(mFbDevice, buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+
+ mDebugString = buffer;
+ }
+}
+
+const std::string& HWC2OnFbAdapter::getDebugString() const {
+ return mDebugString;
+}
+
+void HWC2OnFbAdapter::setState(State state) {
+ mState = state;
+}
+
+HWC2OnFbAdapter::State HWC2OnFbAdapter::getState() const {
+ return mState;
+}
+
+hwc2_layer_t HWC2OnFbAdapter::addLayer() {
+ hwc2_layer_t id = ++mNextLayerId;
+
+ mLayers.insert(id);
+ mDirtyLayers.insert(id);
+
+ return id;
+}
+
+bool HWC2OnFbAdapter::removeLayer(hwc2_layer_t layer) {
+ mDirtyLayers.erase(layer);
+ return mLayers.erase(layer);
+}
+
+bool HWC2OnFbAdapter::hasLayer(hwc2_layer_t layer) const {
+ return mLayers.count(layer) > 0;
+}
+
+bool HWC2OnFbAdapter::markLayerDirty(hwc2_layer_t layer, bool dirty) {
+ if (mLayers.count(layer) == 0) {
+ return false;
+ }
+
+ if (dirty) {
+ mDirtyLayers.insert(layer);
+ } else {
+ mDirtyLayers.erase(layer);
+ }
+
+ return true;
+}
+
+const std::unordered_set<hwc2_layer_t>& HWC2OnFbAdapter::getDirtyLayers() const {
+ return mDirtyLayers;
+}
+
+void HWC2OnFbAdapter::clearDirtyLayers() {
+ mDirtyLayers.clear();
+}
+
+/*
+ * For each frame, SurfaceFlinger
+ *
+ * - peforms GLES composition
+ * - calls eglSwapBuffers
+ * - calls setClientTarget, which maps to setBuffer below
+ * - calls presentDisplay, which maps to postBuffer below
+ *
+ * setBuffer should be a good place to call compositionComplete.
+ *
+ * As for post, it
+ *
+ * - schedules the buffer for presentation on the next vsync
+ * - locks the buffer and blocks all other users trying to lock it
+ *
+ * It does not give us a way to return a present fence, and we need to live
+ * with that. The implication is that, when we are double-buffered,
+ * SurfaceFlinger assumes the front buffer is available for rendering again
+ * immediately after the back buffer is posted. The locking semantics
+ * hopefully are strong enough that the rendering will be blocked.
+ */
+void HWC2OnFbAdapter::setBuffer(buffer_handle_t buffer) {
+ if (mFbDevice->compositionComplete) {
+ mFbDevice->compositionComplete(mFbDevice);
+ }
+ mBuffer = buffer;
+}
+
+bool HWC2OnFbAdapter::postBuffer() {
+ int error = 0;
+ if (mBuffer) {
+ error = mFbDevice->post(mFbDevice, mBuffer);
+ }
+
+ return error == 0;
+}
+
+void HWC2OnFbAdapter::setVsyncCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) {
+ mVsyncThread.setCallback(callback, data);
+}
+
+void HWC2OnFbAdapter::enableVsync(bool enable) {
+ mVsyncThread.enableCallback(enable);
+}
+
+int64_t HWC2OnFbAdapter::VsyncThread::now() {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return int64_t(ts.tv_sec) * 1'000'000'000 + ts.tv_nsec;
+}
+
+bool HWC2OnFbAdapter::VsyncThread::sleepUntil(int64_t t) {
+ struct timespec ts;
+ ts.tv_sec = t / 1'000'000'000;
+ ts.tv_nsec = t % 1'000'000'000;
+
+ while (true) {
+ int error = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
+ if (error) {
+ if (error == EINTR) {
+ continue;
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+
+void HWC2OnFbAdapter::VsyncThread::start(int64_t firstVsync, int64_t period) {
+ mNextVsync = firstVsync;
+ mPeriod = period;
+ mStarted = true;
+ mThread = std::thread(&VsyncThread::vsyncLoop, this);
+}
+
+void HWC2OnFbAdapter::VsyncThread::stop() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStarted = false;
+ }
+ mCondition.notify_all();
+ mThread.join();
+}
+
+void HWC2OnFbAdapter::VsyncThread::setCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCallback = callback;
+ mCallbackData = data;
+}
+
+void HWC2OnFbAdapter::VsyncThread::enableCallback(bool enable) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCallbackEnabled = enable;
+ }
+ mCondition.notify_all();
+}
+
+void HWC2OnFbAdapter::VsyncThread::vsyncLoop() {
+ prctl(PR_SET_NAME, "VsyncThread", 0, 0, 0);
+
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (!mStarted) {
+ return;
+ }
+
+ while (true) {
+ if (!mCallbackEnabled) {
+ mCondition.wait(lock, [this] { return mCallbackEnabled || !mStarted; });
+ if (!mStarted) {
+ break;
+ }
+ }
+
+ lock.unlock();
+
+ // adjust mNextVsync if necessary
+ int64_t t = now();
+ if (mNextVsync < t) {
+ int64_t n = (t - mNextVsync + mPeriod - 1) / mPeriod;
+ mNextVsync += mPeriod * n;
+ }
+ bool fire = sleepUntil(mNextVsync);
+
+ lock.lock();
+
+ if (fire) {
+ ALOGV("VsyncThread(%" PRId64 ")", mNextVsync);
+ if (mCallback) {
+ mCallback(mCallbackData, getDisplayId(), mNextVsync);
+ }
+ mNextVsync += mPeriod;
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/hwc2onfbadapter/include/hwc2onfbadapter/HWC2OnFbAdapter.h b/libs/hwc2onfbadapter/include/hwc2onfbadapter/HWC2OnFbAdapter.h
new file mode 100644
index 0000000..d6272fd
--- /dev/null
+++ b/libs/hwc2onfbadapter/include/hwc2onfbadapter/HWC2OnFbAdapter.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_HWC2_ON_FB_ADAPTER_H
+#define ANDROID_SF_HWC2_ON_FB_ADAPTER_H
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_set>
+
+#include <hardware/hwcomposer2.h>
+
+struct framebuffer_device_t;
+
+namespace android {
+
+class HWC2OnFbAdapter : public hwc2_device_t {
+public:
+ HWC2OnFbAdapter(framebuffer_device_t* fbDevice);
+
+ static HWC2OnFbAdapter& cast(hw_device_t* device);
+ static HWC2OnFbAdapter& cast(hwc2_device_t* device);
+
+ static hwc2_display_t getDisplayId();
+ static hwc2_config_t getConfigId();
+
+ void close();
+
+ struct Info {
+ std::string name;
+ uint32_t width;
+ uint32_t height;
+ int format;
+ int vsync_period_ns;
+ int xdpi_scaled;
+ int ydpi_scaled;
+ };
+ const Info& getInfo() const;
+
+ void updateDebugString();
+ const std::string& getDebugString() const;
+
+ enum class State {
+ MODIFIED,
+ VALIDATED_WITH_CHANGES,
+ VALIDATED,
+ };
+ void setState(State state);
+ State getState() const;
+
+ hwc2_layer_t addLayer();
+ bool removeLayer(hwc2_layer_t layer);
+ bool hasLayer(hwc2_layer_t layer) const;
+ bool markLayerDirty(hwc2_layer_t layer, bool dirty);
+ const std::unordered_set<hwc2_layer_t>& getDirtyLayers() const;
+ void clearDirtyLayers();
+
+ void setBuffer(buffer_handle_t buffer);
+ bool postBuffer();
+
+ void setVsyncCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data);
+ void enableVsync(bool enable);
+
+private:
+ framebuffer_device_t* mFbDevice{nullptr};
+ Info mFbInfo{};
+
+ std::string mDebugString;
+
+ State mState{State::MODIFIED};
+
+ uint64_t mNextLayerId{0};
+ std::unordered_set<hwc2_layer_t> mLayers;
+ std::unordered_set<hwc2_layer_t> mDirtyLayers;
+
+ buffer_handle_t mBuffer{nullptr};
+
+ class VsyncThread {
+ public:
+ static int64_t now();
+ static bool sleepUntil(int64_t t);
+
+ void start(int64_t first, int64_t period);
+ void stop();
+ void setCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data);
+ void enableCallback(bool enable);
+
+ private:
+ void vsyncLoop();
+ bool waitUntilNextVsync();
+
+ std::thread mThread;
+ int64_t mNextVsync{0};
+ int64_t mPeriod{0};
+
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mStarted{false};
+ HWC2_PFN_VSYNC mCallback{nullptr};
+ hwc2_callback_data_t mCallbackData{nullptr};
+ bool mCallbackEnabled{false};
+ };
+ VsyncThread mVsyncThread;
+};
+
+} // namespace android
+
+#endif // ANDROID_SF_HWC2_ON_FB_ADAPTER_H
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d8dc957..9abd04c 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -450,7 +450,7 @@
break;
}
- case AINPUT_EVENT_TYPE_MOTION: {
+ case InputMessage::TYPE_MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
Batch& batch = mBatches.editItemAt(batchIndex);
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index b174fa8..62acea3 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -556,6 +556,46 @@
return true;
}
+/*
+ * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
+ * the default implementation
+ */
+static float solveUnweightedLeastSquaresDeg2(const float* x, const float* y, size_t count) {
+ float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
+
+ for (size_t i = 0; i < count; i++) {
+ float xi = x[i];
+ float yi = y[i];
+ float xi2 = xi*xi;
+ float xi3 = xi2*xi;
+ float xi4 = xi3*xi;
+ float xi2yi = xi2*yi;
+ float xiyi = xi*yi;
+
+ sxi += xi;
+ sxi2 += xi2;
+ sxiyi += xiyi;
+ sxi2yi += xi2yi;
+ syi += yi;
+ sxi3 += xi3;
+ sxi4 += xi4;
+ }
+
+ float Sxx = sxi2 - sxi*sxi / count;
+ float Sxy = sxiyi - sxi*syi / count;
+ float Sxx2 = sxi3 - sxi*sxi2 / count;
+ float Sx2y = sxi2yi - sxi2*syi / count;
+ float Sx2x2 = sxi4 - sxi2*sxi2 / count;
+
+ float numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
+ float denominator = Sxx*Sx2x2 - Sxx2*Sxx2;
+ if (denominator == 0) {
+ ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
+ return 0;
+ }
+ return numerator/denominator;
+}
+
bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
VelocityTracker::Estimator* outEstimator) const {
outEstimator->clear();
@@ -597,6 +637,19 @@
degree = m - 1;
}
if (degree >= 1) {
+ if (degree == 2 && mWeighting == WEIGHTING_NONE) { // optimize unweighted, degree=2 fit
+ outEstimator->time = newestMovement.eventTime;
+ outEstimator->degree = 2;
+ outEstimator->confidence = 1;
+ outEstimator->xCoeff[0] = 0; // only slope is calculated, set rest of coefficients = 0
+ outEstimator->yCoeff[0] = 0;
+ outEstimator->xCoeff[1] = solveUnweightedLeastSquaresDeg2(time, x, m);
+ outEstimator->yCoeff[1] = solveUnweightedLeastSquaresDeg2(time, y, m);
+ outEstimator->xCoeff[2] = 0;
+ outEstimator->yCoeff[2] = 0;
+ return true;
+ }
+
float xdet, ydet;
uint32_t n = degree + 1;
if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 029a420..0028655 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -7,6 +7,12 @@
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=sign-compare", // to fix later
+ "-Wno-unused-variable",
+ ],
shared_libs: [
"libinput",
"libcutils",
@@ -24,5 +30,7 @@
srcs: ["StructLayout_test.cpp"],
cflags: [
"-O0",
+ "-Wall",
+ "-Werror",
],
}
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
index 0ed24a2..0184f56 100644
--- a/libs/math/tests/Android.bp
+++ b/libs/math/tests/Android.bp
@@ -18,22 +18,26 @@
name: "vec_test",
srcs: ["vec_test.cpp"],
static_libs: ["libmath"],
+ cflags: ["-Wall", "-Werror"],
}
cc_test {
name: "mat_test",
srcs: ["mat_test.cpp"],
static_libs: ["libmath"],
+ cflags: ["-Wall", "-Werror"],
}
cc_test {
name: "half_test",
srcs: ["half_test.cpp"],
static_libs: ["libmath"],
+ cflags: ["-Wall", "-Werror"],
}
cc_test {
name: "quat_test",
srcs: ["quat_test.cpp"],
static_libs: ["libmath"],
+ cflags: ["-Wall", "-Werror"],
}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index e61fbd6..29555fd 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -30,14 +30,25 @@
cc_library {
name: "libnativewindow",
- export_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ "include-private",
+ ],
clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-function",
+ ],
+
cppflags: [
"-std=c++1z"
],
+ version_script: "libnativewindow.map.txt",
+
srcs: [
"AHardwareBuffer.cpp",
"ANativeWindow.cpp",
diff --git a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
similarity index 100%
rename from libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
rename to libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 58045be..105d01b 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -3,13 +3,11 @@
AHardwareBuffer_acquire;
AHardwareBuffer_allocate;
AHardwareBuffer_describe;
- AHardwareBuffer_fromHardwareBuffer;
AHardwareBuffer_getNativeHandle; # vndk
AHardwareBuffer_lock;
AHardwareBuffer_recvHandleFromUnixSocket;
AHardwareBuffer_release;
AHardwareBuffer_sendHandleToUnixSocket;
- AHardwareBuffer_toHardwareBuffer;
AHardwareBuffer_unlock;
ANativeWindowBuffer_getHardwareBuffer; # vndk
ANativeWindow_OemStorageGet; # vndk
@@ -17,8 +15,6 @@
ANativeWindow_acquire;
ANativeWindow_cancelBuffer; # vndk
ANativeWindow_dequeueBuffer; # vndk
- ANativeWindow_fromSurface;
- ANativeWindow_fromSurfaceTexture;
ANativeWindow_getFormat;
ANativeWindow_getHeight;
ANativeWindow_getWidth;
@@ -42,3 +38,17 @@
local:
*;
};
+
+LIBNATIVEWINDOW_PLATFORM {
+ global:
+ extern "C++" {
+ android::AHardwareBuffer_isValidPixelFormat*;
+ android::AHardwareBuffer_convertFromPixelFormat*;
+ android::AHardwareBuffer_convertToPixelFormat*;
+ android::AHardwareBuffer_convertFromGrallocUsageBits*;
+ android::AHardwareBuffer_convertToGrallocUsageBits*;
+ android::AHardwareBuffer_to_GraphicBuffer*;
+ android::AHardwareBuffer_to_ANativeWindowBuffer*;
+ android::AHardwareBuffer_from_GraphicBuffer*;
+ };
+} LIBNATIVEWINDOW;
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index b89c35a..20071be 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -23,4 +23,5 @@
srcs: [
"AHardwareBufferTest.cpp",
"c_compatibility.c"],
+ cflags: ["-Wall", "-Werror"],
}
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 171a627..940ff5a 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -16,9 +16,12 @@
name: "libsensor",
clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
cppflags: [
"-Weverything",
- "-Werror",
// The static constructors and destructors in this library have not been noted to
// introduce significant overheads
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 9d530fc..9fd84bc 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -17,6 +17,8 @@
clang: true,
+ cflags: ["-Wall", "-Werror"],
+
srcs: [
"Sensor_test.cpp",
],
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 59173cb..07aba32 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -20,9 +20,12 @@
},
clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
cppflags: [
"-Weverything",
- "-Werror",
// The static constructors and destructors in this library have not been noted to
// introduce significant overheads
@@ -114,4 +117,7 @@
vendor_available: true,
}
-subdirs = ["tests"]
+subdirs = [
+ "tests",
+ "tools",
+]
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index d5676cc..2d72944 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -16,10 +16,13 @@
#include <ui/DebugUtils.h>
#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
#include <android-base/stringprintf.h>
#include <string>
+using android::base::StringPrintf;
+
std::string decodeStandard(android_dataspace dataspace) {
const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
switch (dataspaceSelect) {
@@ -187,7 +190,7 @@
std::string dataspaceDetails(android_dataspace dataspace) {
if (dataspace == 0) {
- return "Default (0)";
+ return "Default";
}
return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
decodeTransfer(dataspace).c_str(),
@@ -262,3 +265,7 @@
return android::base::StringPrintf("Unknown %#08x", format);
}
}
+
+std::string to_string(const android::Rect& rect) {
+ return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
+}
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d52c508..d854489 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -99,7 +99,7 @@
{
int32_t fenceFd = -1;
status_t error = unlockAsync(handle, &fenceFd);
- if (error == NO_ERROR) {
+ if (error == NO_ERROR && fenceFd >= 0) {
sync_wait(fenceFd, -1);
close(fenceFd);
}
@@ -129,29 +129,6 @@
return static_cast<status_t>(error);
}
-static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) {
- if (plane.bits_per_component != 8) {
- ALOGV("Invalid number of bits per component: %d",
- plane.bits_per_component);
- return false;
- }
- if (plane.bits_used != 8) {
- ALOGV("Invalid number of bits used: %d", plane.bits_used);
- return false;
- }
-
- bool hasValidIncrement = plane.h_increment == 1 ||
- (plane.component != FLEX_COMPONENT_Y && plane.h_increment == 2);
- hasValidIncrement = hasValidIncrement && plane.v_increment > 0;
- if (!hasValidIncrement) {
- ALOGV("Invalid increment: h %d v %d", plane.h_increment,
- plane.v_increment);
- return false;
- }
-
- return true;
-}
-
status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd)
{
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 30f4a59..dad9446 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -21,9 +21,14 @@
#include <string>
+namespace android {
+class Rect;
+}
+
std::string decodeStandard(android_dataspace dataspace);
std::string decodeTransfer(android_dataspace dataspace);
std::string decodeRange(android_dataspace dataspace);
std::string dataspaceDetails(android_dataspace dataspace);
std::string decodeColorMode(android_color_mode colormode);
std::string decodePixelFormat(android::PixelFormat format);
+std::string to_string(const android::Rect& rect);
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 6733505..08067fc 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -18,10 +18,12 @@
name: "Region_test",
shared_libs: ["libui"],
srcs: ["Region_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
}
cc_test {
name: "colorspace_test",
shared_libs: ["libui"],
srcs: ["colorspace_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
}
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index f327200..a2287e1 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -48,6 +48,8 @@
"-DLOG_TAG=\"libbufferhub\"",
"-DTRACE=0",
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ "-Wall",
+ "-Werror",
],
export_include_dirs: localIncludeFiles,
static_libs: staticLibraries,
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
index c4b9a8c..d0d3a73 100644
--- a/libs/vr/libbufferhub/bufferhub_tests.cpp
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -46,17 +46,17 @@
// Producer state mask is unique, i.e. 1.
EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit);
// Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0);
+ EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0ULL);
// Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c->buffer_state_bit(), 0);
- EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0);
+ EXPECT_NE(c->buffer_state_bit(), 0ULL);
+ EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0ULL);
// Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0);
+ EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0ULL);
// Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c2->buffer_state_bit(), 0);
- EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0);
+ EXPECT_NE(c2->buffer_state_bit(), 0ULL);
+ EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0ULL);
// Each consumer should have unique bit.
- EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0);
+ EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0ULL);
// Initial state: producer not available, consumers not available.
EXPECT_EQ(0, RETRY_EINTR(p->Poll(100)));
@@ -166,7 +166,7 @@
cs[i] = BufferConsumer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
// Expect all buffers have unique state mask.
- EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0);
+ EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0ULL);
buffer_state_bits |= cs[i]->buffer_state_bit();
}
EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
@@ -182,7 +182,7 @@
cs[i] = BufferConsumer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
// The released state mask will be reused.
- EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0);
+ EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0ULL);
buffer_state_bits |= cs[i]->buffer_state_bit();
EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
}
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 93ccd0f..b279875 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -49,6 +49,11 @@
"-DLOG_TAG=\"libbufferhubqueue\"",
"-DTRACE=0",
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ "-Wall",
+ "-Werror",
+ "-Wno-format",
+ "-Wno-unused-parameter",
+ "-Wno-unused-variable",
],
srcs: sourceFiles,
export_include_dirs: includeFiles,
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index 8bd1ef1..d8a9b90 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -32,6 +32,9 @@
"-DTRACE=0",
"-O0",
"-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-error=sign-compare", // to fix later
],
name: "buffer_hub_queue-test",
tags: ["optional"],
@@ -47,6 +50,8 @@
"-DTRACE=0",
"-O0",
"-g",
+ "-Wall",
+ "-Werror",
],
name: "buffer_hub_queue_producer-test",
tags: ["optional"],
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index e3ab7fa..8213ca2 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -57,6 +57,8 @@
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
export_include_dirs: localIncludeFiles,
shared_libs: sharedLibraries,
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 9fe161d..04418d2 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -22,6 +22,8 @@
cflags = [
"-DLOG_TAG=\"libdvr\"",
"-DTRACE=0",
+ "-Wall",
+ "-Werror",
]
srcs = [
diff --git a/libs/vr/libdvr/include/dvr/dvr_display_types.h b/libs/vr/libdvr/include/dvr/dvr_display_types.h
index 25364d8..fd69843 100644
--- a/libs/vr/libdvr/include/dvr/dvr_display_types.h
+++ b/libs/vr/libdvr/include/dvr/dvr_display_types.h
@@ -13,7 +13,7 @@
#ifndef __FLOAT32X4T_86
#define __FLOAT32X4T_86
typedef float float32x4_t __attribute__((__vector_size__(16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; };
+typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
#endif
#endif
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
index c21deb0..5c837e7 100644
--- a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
@@ -5,7 +5,6 @@
#include <dvr/dvr_surface.h>
#include <system/graphics.h>
-#include <base/logging.h>
#include <gtest/gtest.h>
namespace android {
@@ -22,7 +21,7 @@
DvrBuffer* buffer2 = nullptr;
int ret2 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer2);
- ASSERT_EQ(0, ret1);
+ ASSERT_EQ(0, ret2);
ASSERT_NE(nullptr, buffer2);
AHardwareBuffer* hardware_buffer1 = nullptr;
@@ -159,7 +158,7 @@
ASSERT_EQ(0, e3);
ASSERT_NE(nullptr, buffer);
// Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
uint64_t* data = static_cast<uint64_t*>(buffer);
constexpr size_t num_values = size / sizeof(uint64_t);
@@ -192,7 +191,7 @@
ASSERT_EQ(0, e3);
ASSERT_NE(nullptr, buffer);
// Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
uint64_t* data = static_cast<uint64_t*>(buffer);
constexpr size_t num_values = size / sizeof(uint64_t);
@@ -233,7 +232,7 @@
ASSERT_EQ(0, e3);
ASSERT_NE(nullptr, buffer);
// Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
uint64_t* data = static_cast<uint64_t*>(buffer);
constexpr size_t num_values = size / sizeof(uint64_t);
@@ -241,7 +240,7 @@
for (size_t i = 0; i < num_values; ++i) {
zero |= data[i];
}
- ASSERT_EQ(0, zero);
+ ASSERT_EQ(0U, zero);
int32_t fence = -1;
int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
index 62aeb79..c7d808b 100644
--- a/libs/vr/libdvrcommon/Android.bp
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -40,6 +40,8 @@
cflags: [
"-DLOG_TAG=\"libdvrcommon\"",
"-DTRACE=0",
+ "-Wall",
+ "-Werror",
],
export_include_dirs: localIncludeFiles,
@@ -59,6 +61,11 @@
tags: ["optional"],
srcs: testFiles,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
shared_libs: sharedLibraries,
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
index 364873d..6a46876 100644
--- a/libs/vr/libperformance/Android.bp
+++ b/libs/vr/libperformance/Android.bp
@@ -32,7 +32,9 @@
srcs: sourceFiles,
cflags: [
"-DLOG_TAG=\"libperformance\"",
- "-DTRACE=0"
+ "-DTRACE=0",
+ "-Wall",
+ "-Werror",
],
export_include_dirs: includeFiles,
static_libs: staticLibraries,
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 0fb2d84..4d80e91 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -77,6 +77,10 @@
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ "-Wno-error=sign-compare", // to fix later
+ "-Wno-unused-variable",
],
shared_libs: sharedLibraries,
whole_static_libs: staticLibraries,
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index b3da120..fb69d5c 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -230,7 +230,7 @@
// Standalones only create the composer client once and then use SetPowerMode
// to control the screen on pause/resume.
if (!is_standalone_device_ || !composer_) {
- composer_.reset(new Hwc2::Composer(false));
+ composer_.reset(new Hwc2::Composer("default"));
composer_callback_ = new ComposerCallback;
composer_->registerCallback(composer_callback_);
Layer::SetComposer(composer_.get());
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
index d022adf..e92178a 100644
--- a/libs/vr/libvrsensor/Android.bp
+++ b/libs/vr/libvrsensor/Android.bp
@@ -42,6 +42,11 @@
cc_library {
srcs: sourceFiles,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-macro-redefined",
+ ],
export_include_dirs: includeFiles,
static_libs: staticLibraries,
shared_libs: sharedLibraries,
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
index bb25f1d..b663a67 100644
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -7,7 +7,7 @@
#ifndef __FLOAT32X4T_86
#define __FLOAT32X4T_86
typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; };
+typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
#endif
#endif
diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h
index 36acff9..25033f2 100644
--- a/opengl/include/GLES/gl.h
+++ b/opengl/include/GLES/gl.h
@@ -51,6 +51,7 @@
#ifndef GL_VERSION_ES_CM_1_0
#define GL_VERSION_ES_CM_1_0 1
typedef void GLvoid;
+typedef char GLchar;
typedef unsigned int GLenum;
#include <KHR/khrplatform.h>
typedef khronos_float_t GLfloat;
diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h
index b6fe620..1a150e3 100644
--- a/opengl/include/GLES/glext.h
+++ b/opengl/include/GLES/glext.h
@@ -104,7 +104,6 @@
#ifndef GL_OES_byte_coordinates
#define GL_OES_byte_coordinates 1
-typedef khronos_int8_t GLbyte;
#endif /* GL_OES_byte_coordinates */
#ifndef GL_OES_compressed_ETC1_RGB8_sub_texture
@@ -128,7 +127,6 @@
#ifndef GL_OES_draw_texture
#define GL_OES_draw_texture 1
-typedef short GLshort;
#define GL_TEXTURE_CROP_RECT_OES 0x8B9D
typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height);
typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height);
@@ -409,7 +407,6 @@
#ifndef GL_OES_single_precision
#define GL_OES_single_precision 1
-typedef khronos_float_t GLclampf;
typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth);
typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation);
typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f);
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
index c7635e2..15a12e4 100644
--- a/opengl/libagl/Android.mk
+++ b/opengl/libagl/Android.mk
@@ -38,6 +38,8 @@
# The graphics code can generate division by zero
LOCAL_CFLAGS_mips += -mno-check-zero-division
+LOCAL_CFLAGS += -Wall -Werror
+
# we need to access the private Bionic header <bionic_tls.h>
LOCAL_C_INCLUDES += bionic/libc/private
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 54207fa..2d36c61 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -136,9 +136,6 @@
static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) {
memcpy(v, c->current.color.v, sizeof(vec4_t));
}
-static void currentColor_clamp(ogles_context_t* c, GLfixed* v, const GLvoid*) {
- memcpy(v, c->currentColorClamped.v, sizeof(vec4_t));
-}
static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) {
memcpy(v, c->currentNormal.v, sizeof(vec3_t));
}
@@ -349,6 +346,7 @@
{
// make sure the size of vertex_t allows cache-line alignment
CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize;
+ (void)assertAlignedSize; // suppress unused warning.
const int align = 32;
const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index b79051c..be43705 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -81,7 +81,9 @@
const unsigned int NUM_DISPLAYS = 1;
+#ifndef __ANDROID__
static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t gEGLErrorKey = -1;
#ifndef __ANDROID__
@@ -1776,7 +1778,6 @@
// if we're detaching, we need the current context
current_ctx = (EGLContext)getGlThreadSpecific();
} else {
- egl_context_t* c = egl_context_t::context(ctx);
egl_surface_t* d = (egl_surface_t*)draw;
egl_surface_t* r = (egl_surface_t*)read;
if ((d && d->ctx && d->ctx != ctx) ||
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index e7fe9d7..216c725 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -467,7 +467,6 @@
}
light_t& light = c->lighting.lights[i-GL_LIGHT0];
- const GLfixed kDegToRad = GLfixed((M_PI * gglIntToFixed(1)) / 180.0f);
switch (pname) {
case GL_SPOT_EXPONENT:
if (GGLfixed(param) >= gglIntToFixed(128)) {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index 034c857..edd474d 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -51,7 +51,6 @@
static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o);
static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o);
static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o);
static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
@@ -524,16 +523,6 @@
}
}
-static inline
-GLfloat det22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) {
- return a*d - b*c;
-}
-
-static inline
-GLfloat ndet22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) {
- return b*c - a*d;
-}
-
static __attribute__((noinline))
void invert(GLfloat* inverse, const GLfloat* src)
{
diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp
index 57a798d..d3b19e8 100644
--- a/opengl/libagl/primitives.cpp
+++ b/opengl/libagl/primitives.cpp
@@ -241,6 +241,7 @@
m_dx20 = v0->window.x - v2->window.x;
m_dy02 = v2->window.y - v0->window.y;
m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20;
+ (void)m_reserved; // suppress unused warning
}
void compute_iterators_t::initLine(
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 3fe5ed0..aae8e05 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -356,10 +356,6 @@
GLenum format, GLenum type, GLsizei width, GLsizei height,
GLenum compressedFormat = 0)
{
- // find out which texture is bound to the current unit
- const int active = c->textures.active;
- const GLuint name = c->textures.tmu[active].name;
-
// convert the pixelformat to one we can handle
const int32_t formatIdx = convertGLPixelFormat(format, type);
if (formatIdx == 0) { // we don't know what to do with this
@@ -1192,7 +1188,6 @@
const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
const int32_t align = c->textures.unpackAlignment-1;
const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
- const size_t size = bpr * height;
const int32_t stride = bpr / pixelFormat.size;
GGLSurface userSurface;
@@ -1276,7 +1271,6 @@
const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
const int32_t align = c->textures.unpackAlignment-1;
const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
- const size_t size = bpr * height;
const int32_t stride = bpr / pixelFormat.size;
GGLSurface userSurface;
userSurface.version = sizeof(userSurface);
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 802b3b4..32c2d7e 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -3,6 +3,7 @@
name: "libETC1",
srcs: ["ETC1/etc1.cpp"],
host_supported: true,
+ cflags: ["-Wall", "-Werror"],
target: {
android: {
@@ -56,6 +57,9 @@
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
"-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-variable",
],
shared_libs: [
// ***** DO NOT ADD NEW DEPENDENCIES HERE *****
@@ -87,6 +91,11 @@
defaults: ["gl_libs_defaults"],
cflags: [
"-DLOG_TAG=\"libEGL\"",
+ "-Wall",
+ "-Werror",
+ "-Wno-error=deprecated-register",
+ "-Wno-error=unknown-attributes",
+ "-Wno-unused-variable",
],
shared_libs: [
// ***** DO NOT ADD NEW DEPENDENCIES HERE *****
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index f1b30c7..0624b60 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -18,6 +18,7 @@
#include "BlobCache.h"
+#include <errno.h>
#include <inttypes.h>
#include <cutils/properties.h>
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 371239d..9822849 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -340,6 +340,14 @@
result = std::string("/vendor/lib/egl/lib") + kind + "_emulation.so";
#endif
return result;
+ case 2:
+ // Use guest side swiftshader library
+#if defined(__LP64__)
+ result = std::string("/vendor/lib64/egl/lib") + kind + "_swiftshader.so";
+#else
+ result = std::string("/vendor/lib/egl/lib") + kind + "_swiftshader.so";
+#endif
+ return result;
default:
// Not in emulator, or use other guest-side implementation
break;
@@ -389,7 +397,7 @@
static bool find(std::string& result,
const std::string& pattern, const char* const search, bool exact) {
if (exact) {
- std::string absolutePath = std::string(search) + "/" + pattern;
+ std::string absolutePath = std::string(search) + "/" + pattern + ".so";
if (!access(absolutePath.c_str(), R_OK)) {
result = absolutePath;
return true;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 4e5833a..3c1edd1 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -79,6 +79,10 @@
}
egl_display_t* egl_display_t::get(EGLDisplay dpy) {
+ if (uintptr_t(dpy) == 0) {
+ return nullptr;
+ }
+
uintptr_t index = uintptr_t(dpy)-1U;
if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
return nullptr;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 837cfa9..72b4823 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -116,17 +116,23 @@
if (gl_extensions.empty()) {
// call the implementation's glGetString(GL_EXTENSIONS)
const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
- gl_extensions = exts;
- if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
- gl_extensions.insert(0, "GL_EXT_debug_marker ");
- }
- // tokenize the supported extensions for the glGetStringi() wrapper
- std::stringstream ss;
- std::string str;
- ss << gl_extensions;
- while (ss >> str) {
- tokenized_gl_extensions.push_back(str);
+ // If this context is sharing with another context, and the other context was reset
+ // e.g. due to robustness failure, this context might also be reset and glGetString can
+ // return NULL.
+ if (exts) {
+ gl_extensions = exts;
+ if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+ gl_extensions.insert(0, "GL_EXT_debug_marker ");
+ }
+
+ // tokenize the supported extensions for the glGetStringi() wrapper
+ std::stringstream ss;
+ std::string str;
+ ss << gl_extensions;
+ while (ss >> str) {
+ tokenized_gl_extensions.push_back(str);
+ }
}
}
}
diff --git a/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
index 772b21a..285bba4 100644
--- a/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
+++ b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
@@ -16,15 +16,15 @@
Status
- Draft
+ Complete
Version
- Version 1.0, January 27, 2017
+ Version 3, October 11, 2017
Number
- EGL Extension #XXX
+ EGL Extension #123
Dependencies
@@ -43,11 +43,11 @@
New Types
-struct AHardwareBuffer
+ struct AHardwareBuffer
New Procedures and Functions
-EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer)
+ EGLClientBuffer eglGetNativeClientBufferANDROID(const struct AHardwareBuffer *buffer)
New Tokens
@@ -62,7 +62,7 @@
"The command
EGLClientBuffer eglGetNativeClientBufferANDROID(
- AHardwareBuffer *buffer)
+ const struct AHardwareBuffer *buffer)
may be used to create an EGLClientBuffer from an AHardwareBuffer object.
EGL implementations must guarantee that the lifetime of the returned
@@ -92,6 +92,9 @@
Revision History
+#3 (Jesse Hall, October 11, 2017)
+ - Assigned extension number, fixed minor issues for publication
+
#2 (Craig Donner, February 17, 2017)
- Fix typographical errors.
diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp
index d85af81..f246077 100644
--- a/opengl/tests/EGLTest/Android.bp
+++ b/opengl/tests/EGLTest/Android.bp
@@ -8,6 +8,11 @@
"EGL_test.cpp",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
shared_libs: [
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
diff --git a/opengl/tests/angeles/Android.bp b/opengl/tests/angeles/Android.bp
index bbbc447..5c398a6 100644
--- a/opengl/tests/angeles/Android.bp
+++ b/opengl/tests/angeles/Android.bp
@@ -8,6 +8,11 @@
"demo.c",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
gtest: false,
shared_libs: [
diff --git a/opengl/tests/angeles/app-linux.cpp b/opengl/tests/angeles/app-linux.cpp
index ced8786..9d2c98f 100644
--- a/opengl/tests/angeles/app-linux.cpp
+++ b/opengl/tests/angeles/app-linux.cpp
@@ -62,9 +62,6 @@
int gAppAlive = 1;
-static const char sAppName[] =
- "San Angeles Observation OpenGL ES version example (Linux)";
-
static int sWindowWidth = WINDOW_DEFAULT_WIDTH;
static int sWindowHeight = WINDOW_DEFAULT_HEIGHT;
static EGLDisplay sEglDisplay = EGL_NO_DISPLAY;
@@ -132,7 +129,6 @@
EGLContext context;
EGLConfig config;
EGLSurface surface;
- EGLint w, h;
EGLDisplay dpy;
EGLNativeWindowType window = windowSurface.getSurface();
diff --git a/opengl/tests/configdump/Android.bp b/opengl/tests/configdump/Android.bp
index c46477c..ee967970 100644
--- a/opengl/tests/configdump/Android.bp
+++ b/opengl/tests/configdump/Android.bp
@@ -5,6 +5,11 @@
srcs: ["configdump.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
shared_libs: [
"libcutils",
"libEGL",
diff --git a/opengl/tests/fillrate/Android.bp b/opengl/tests/fillrate/Android.bp
index 543f1e3..689cee4 100644
--- a/opengl/tests/fillrate/Android.bp
+++ b/opengl/tests/fillrate/Android.bp
@@ -3,6 +3,11 @@
srcs: ["fillrate.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
gtest: false,
shared_libs: [
diff --git a/opengl/tests/fillrate/fillrate.cpp b/opengl/tests/fillrate/fillrate.cpp
index a42f3f2..f022069 100644
--- a/opengl/tests/fillrate/fillrate.cpp
+++ b/opengl/tests/fillrate/fillrate.cpp
@@ -153,7 +153,7 @@
for (int c=1, j=0 ; c<32 ; c++, j++) {
nsecs_t t = times[j];
- printf("%ld\t%d\t%f\n", t, c, (double(t)/c)/1000000.0);
+ printf("%lld\t%d\t%f\n", (long long)t, c, (double(t)/c)/1000000.0);
}
diff --git a/opengl/tests/filter/Android.bp b/opengl/tests/filter/Android.bp
index 5f925c8..23241e1 100644
--- a/opengl/tests/filter/Android.bp
+++ b/opengl/tests/filter/Android.bp
@@ -16,5 +16,10 @@
static_libs: ["libglTest"],
- cflags: ["-DGL_GLEXT_PROTOTYPES"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ ],
+
}
diff --git a/opengl/tests/filter/filter.cpp b/opengl/tests/filter/filter.cpp
index 287ee93..49778a0 100644
--- a/opengl/tests/filter/filter.cpp
+++ b/opengl/tests/filter/filter.cpp
@@ -140,6 +140,7 @@
//glDrawTexiOES(0, 0, 0, dim, dim);
+#if !USE_DRAW_TEXTURE || !GL_OES_draw_texture
const GLfloat fdim = dim;
const GLfloat vertices[4][2] = {
{ 0, 0 },
@@ -154,6 +155,7 @@
{ 1, 1 },
{ 1, 0 }
};
+#endif
if (!usePbuffer) {
eglSwapBuffers(dpy, surface);
diff --git a/opengl/tests/finish/Android.bp b/opengl/tests/finish/Android.bp
index fb5671d..be20851 100644
--- a/opengl/tests/finish/Android.bp
+++ b/opengl/tests/finish/Android.bp
@@ -16,5 +16,10 @@
static_libs: ["libglTest"],
- cflags: ["-DGL_GLEXT_PROTOTYPES"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ ],
+
}
diff --git a/opengl/tests/finish/finish.cpp b/opengl/tests/finish/finish.cpp
index ab955fe..63ea822 100644
--- a/opengl/tests/finish/finish.cpp
+++ b/opengl/tests/finish/finish.cpp
@@ -64,7 +64,6 @@
eglMakeCurrent(dpy, surface, surface, context);
eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
- GLint dim = w<h ? w : h;
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
diff --git a/opengl/tests/gl2_basic/Android.bp b/opengl/tests/gl2_basic/Android.bp
index 7403271..f4538ad 100644
--- a/opengl/tests/gl2_basic/Android.bp
+++ b/opengl/tests/gl2_basic/Android.bp
@@ -16,5 +16,10 @@
static_libs: ["libglTest"],
- cflags: ["-DGL_GLEXT_PROTOTYPES"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ ],
+
}
diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp
index 67c0969..13f6fba 100644
--- a/opengl/tests/gl2_basic/gl2_basic.cpp
+++ b/opengl/tests/gl2_basic/gl2_basic.cpp
@@ -340,7 +340,6 @@
checkEglError("eglQuerySurface");
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
- GLint dim = w < h ? w : h;
fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
diff --git a/opengl/tests/gl2_copyTexImage/Android.bp b/opengl/tests/gl2_copyTexImage/Android.bp
index 51e269c..87fa7ea 100644
--- a/opengl/tests/gl2_copyTexImage/Android.bp
+++ b/opengl/tests/gl2_copyTexImage/Android.bp
@@ -16,5 +16,9 @@
static_libs: ["libglTest"],
- cflags: ["-DGL_GLEXT_PROTOTYPES"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp b/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
index e0aafa2..ada98aa 100644
--- a/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
+++ b/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
@@ -444,7 +444,6 @@
checkEglError("eglQuerySurface");
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
- GLint dim = w < h ? w : h;
fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
diff --git a/opengl/tests/gl2_yuvtex/Android.bp b/opengl/tests/gl2_yuvtex/Android.bp
index 613d678..b64d94d 100644
--- a/opengl/tests/gl2_yuvtex/Android.bp
+++ b/opengl/tests/gl2_yuvtex/Android.bp
@@ -20,5 +20,7 @@
cflags: [
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
],
}
diff --git a/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp b/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
index 22128ab..850a615 100644
--- a/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
+++ b/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
@@ -190,7 +190,7 @@
static sp<GraphicBuffer> yuvTexBuffer;
static GLuint yuvTex;
-bool setupYuvTexSurface(EGLDisplay dpy, EGLContext context) {
+bool setupYuvTexSurface(EGLDisplay dpy, EGLContext /*context*/) {
int blockWidth = yuvTexWidth > 16 ? yuvTexWidth / 16 : 1;
int blockHeight = yuvTexHeight > 16 ? yuvTexHeight / 16 : 1;
yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat,
@@ -399,7 +399,6 @@
checkEglError("eglQuerySurface");
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
- GLint dim = w < h ? w : h;
fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
diff --git a/opengl/tests/gl_basic/Android.bp b/opengl/tests/gl_basic/Android.bp
index 881d8ce..5eed17e 100644
--- a/opengl/tests/gl_basic/Android.bp
+++ b/opengl/tests/gl_basic/Android.bp
@@ -3,6 +3,11 @@
srcs: ["gl_basic.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
gtest: false,
shared_libs: [
diff --git a/opengl/tests/gl_basic/gl_basic.cpp b/opengl/tests/gl_basic/gl_basic.cpp
index 0b663f7..a675c7c 100644
--- a/opengl/tests/gl_basic/gl_basic.cpp
+++ b/opengl/tests/gl_basic/gl_basic.cpp
@@ -191,8 +191,6 @@
int main(int /*argc*/, char **/*argv*/)
{
- int q;
- int start, end;
printf("Initializing EGL...\n");
WindowSurface windowSurface;
if(!init_gl_surface(windowSurface))
@@ -212,7 +210,6 @@
int init_gl_surface(const WindowSurface& windowSurface)
{
- EGLint numConfigs = 1;
EGLConfig myConfig = {0};
EGLint attrib[] =
{
@@ -265,7 +262,6 @@
checkEglError("eglQuerySurface");
eglQuerySurface(eglDisplay, eglSurface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
- GLint dim = w < h ? w : h;
fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
@@ -336,9 +332,6 @@
void render()
{
- int i, j;
- int quads = 1;
-
const GLfloat vertices[] = {
-1, -1, 0,
1, -1, 0,
diff --git a/opengl/tests/gl_perf/Android.bp b/opengl/tests/gl_perf/Android.bp
index 0ffb121..25a317c 100644
--- a/opengl/tests/gl_perf/Android.bp
+++ b/opengl/tests/gl_perf/Android.bp
@@ -20,5 +20,9 @@
static_libs: ["libglTest"],
- cflags: ["-DGL_GLEXT_PROTOTYPES"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
index 2998864..047db20 100644
--- a/opengl/tests/gl_perf/gl2_perf.cpp
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -44,13 +44,6 @@
}
}
-static void checkGlError(const char* op) {
- for (GLint error = glGetError(); error; error
- = glGetError()) {
- fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
- }
-}
-
bool doTest(uint32_t w, uint32_t h);
static EGLDisplay dpy;
@@ -118,7 +111,6 @@
checkEglError("eglQuerySurface");
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
- GLint dim = w < h ? w : h;
glViewport(0, 0, w, h);
diff --git a/opengl/tests/gl_yuvtex/Android.bp b/opengl/tests/gl_yuvtex/Android.bp
index b6be327..9b4924a 100644
--- a/opengl/tests/gl_yuvtex/Android.bp
+++ b/opengl/tests/gl_yuvtex/Android.bp
@@ -19,5 +19,7 @@
cflags: [
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
+ "-Wall",
+ "-Werror",
],
}
diff --git a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
index 7ef3f4e..2c9640b 100644
--- a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
+++ b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
@@ -289,7 +289,6 @@
checkEglError("eglQuerySurface");
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
checkEglError("eglQuerySurface");
- GLint dim = w < h ? w : h;
fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
diff --git a/opengl/tests/gralloc/Android.bp b/opengl/tests/gralloc/Android.bp
index 414c804..33c3dba 100644
--- a/opengl/tests/gralloc/Android.bp
+++ b/opengl/tests/gralloc/Android.bp
@@ -3,6 +3,11 @@
srcs: ["gralloc.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
gtest: false,
shared_libs: [
diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp
index 69e56ff..5956366 100644
--- a/opengl/tests/hwc/hwcRects.cpp
+++ b/opengl/tests/hwc/hwcRects.cpp
@@ -170,7 +170,7 @@
static EGLint width, height;
// Function prototypes
-static Rectangle parseRect(string rectStr);
+static Rectangle parseRect(const string& rectStr);
void init(void);
void printSyntax(const char *cmd);
@@ -358,7 +358,7 @@
// Parse string description of rectangle and add it to list of rectangles
// to be rendered.
-static Rectangle parseRect(string rectStr)
+static Rectangle parseRect(const string& rectStr)
{
int rv;
string str;
diff --git a/opengl/tests/linetex/Android.mk b/opengl/tests/linetex/Android.mk
index 968756a..3df0a0f 100644
--- a/opengl/tests/linetex/Android.mk
+++ b/opengl/tests/linetex/Android.mk
@@ -4,6 +4,8 @@
LOCAL_SRC_FILES:= \
linetex.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libEGL \
diff --git a/opengl/tests/swapinterval/Android.mk b/opengl/tests/swapinterval/Android.mk
index b0b15eb..2a2c12f 100644
--- a/opengl/tests/swapinterval/Android.mk
+++ b/opengl/tests/swapinterval/Android.mk
@@ -4,6 +4,8 @@
LOCAL_SRC_FILES:= \
swapinterval.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk
index bee61f9..629a2d2 100644
--- a/opengl/tests/textures/Android.mk
+++ b/opengl/tests/textures/Android.mk
@@ -21,5 +21,6 @@
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -Wall -Werror
include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/textures/textures.cpp b/opengl/tests/textures/textures.cpp
index 7be942a..bdb5c82 100644
--- a/opengl/tests/textures/textures.cpp
+++ b/opengl/tests/textures/textures.cpp
@@ -87,12 +87,6 @@
0x0000, 0x5555, 0x0000, 0x5555,
0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF };
- uint16_t t5551[] = {
- 0x0000, 0xFFFF, 0x0000, 0xFFFF,
- 0xFFFF, 0x0000, 0xFFFF, 0x0000,
- 0x0000, 0xFFFF, 0x0000, 0xFFFF,
- 0xFFFF, 0x0000, 0xFFFF, 0x0000 };
-
uint32_t t32[] = {
0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFFFF0000,
0xFF00FF00, 0xFFFF0000, 0xFF000000, 0xFF0000FF,
diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk
index 64382ed..7055afa 100644
--- a/opengl/tests/tritex/Android.mk
+++ b/opengl/tests/tritex/Android.mk
@@ -4,6 +4,8 @@
LOCAL_SRC_FILES:= \
tritex.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libEGL \
diff --git a/opengl/tests/tritex/tritex.cpp b/opengl/tests/tritex/tritex.cpp
index 2db73ef..c008003 100644
--- a/opengl/tests/tritex/tritex.cpp
+++ b/opengl/tests/tritex/tritex.cpp
@@ -93,9 +93,6 @@
int main(int argc, char **argv)
{
- int q;
- int start, end;
-
printf("Initializing EGL...\n");
WindowSurface windowSurface;
@@ -120,7 +117,6 @@
int init_gl_surface(const WindowSurface& windowSurface)
{
- EGLint numConfigs = 1;
EGLConfig myConfig = {0};
EGLint attrib[] =
{
diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
index f6813fd..f90e3ec 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
@@ -17,11 +17,10 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
index 4df61d3..12b96f4 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
@@ -17,11 +17,10 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
index 4004a7d..b2bbdf6 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES2/gl2.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp
index c5bdf32..b039bc9 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl3.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp
index 2260a80..dd00e92 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl31.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp
index 130612d..88e00be 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp
index e9c5fc7..3e7ec8b 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp
@@ -17,8 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
-#include <GLES3/gl32.h>
\ No newline at end of file
+#include <GLES3/gl32.h>
diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp
index 7062c57..2163d76 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -1,5 +1,5 @@
#include <jni.h>
-#include <JNIHelp.h>
+#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
#include <assert.h>
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index 026cb37..29296ff 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -17,11 +17,10 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
diff --git a/services/Android.bp b/services/Android.bp
deleted file mode 100644
index 7a8ee5d..0000000
--- a/services/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = [ "*" ]
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 4fd98e2..238cba3 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -37,6 +37,9 @@
],
cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
"-Wno-unused-parameter",
// TODO: Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index 50589b4..99fe0f5 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -69,12 +69,6 @@
static const char *WAKE_LOCK_ID = "KeyEvents";
static const char *DEVICE_PATH = "/dev/input";
-/* return the larger integer */
-static inline int max(int v1, int v2)
-{
- return (v1 > v2) ? v1 : v2;
-}
-
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 69067d2..98e135d 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -1132,8 +1132,6 @@
INJECTION_PERMISSION_DENIED
};
- nsecs_t startTime = now();
-
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
int32_t displayId = entry->displayId;
@@ -2904,7 +2902,7 @@
for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
TouchState& state = mTouchStatesByDisplay.editValueAt(d);
- for (size_t i = 0; i < state.windows.size(); i++) {
+ for (size_t i = 0; i < state.windows.size(); ) {
TouchedWindow& touchedWindow = state.windows.editItemAt(i);
if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
#if DEBUG_FOCUS
@@ -2919,7 +2917,9 @@
synthesizeCancelationEventsForInputChannelLocked(
touchedInputChannel, options);
}
- state.windows.removeAt(i--);
+ state.windows.removeAt(i);
+ } else {
+ ++i;
}
}
}
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index d4266f6..420d06b 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -1174,7 +1174,7 @@
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
size_t numMappers = mMappers.size();
- for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
+ for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
#if DEBUG_RAW_EVENTS
ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
@@ -1202,6 +1202,7 @@
mapper->process(rawEvent);
}
}
+ --count;
}
}
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index b8e9bce..775dbdc 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -32,6 +32,8 @@
],
cflags: [
+ "-Wall",
+ "-Werror",
"-Wno-unused-parameter",
// TODO: Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
@@ -47,6 +49,8 @@
srcs: ["main.cpp"],
+ cflags: ["-Wall", "-Werror"],
+
shared_libs: [
"libbinder",
"libinputflingerhost",
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 29d93f0..a49c8c8 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -7,7 +7,11 @@
"InputDispatcher_test.cpp",
],
test_per_src: true,
- cflags: ["-Wno-unused-parameter"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
shared_libs = [
"libcutils",
"liblog",
diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp
index ca91b8e..0227164 100644
--- a/services/schedulerservice/Android.bp
+++ b/services/schedulerservice/Android.bp
@@ -3,6 +3,7 @@
srcs: [
"SchedulingPolicyService.cpp",
],
+ cflags: ["-Wall", "-Werror"],
shared_libs: [
"libhidlbase",
"libhidltransport",
diff --git a/services/schedulerservice/SchedulingPolicyService.cpp b/services/schedulerservice/SchedulingPolicyService.cpp
index 1f6ed57..a1106cf 100644
--- a/services/schedulerservice/SchedulingPolicyService.cpp
+++ b/services/schedulerservice/SchedulingPolicyService.cpp
@@ -17,6 +17,8 @@
#include "SchedulingPolicyService.h"
+#include <private/android_filesystem_config.h> // for AID_CAMERASERVER
+
#include <log/log.h>
#include <hwbinder/IPCThreadState.h>
#include <mediautils/SchedulingPolicyService.h>
@@ -28,8 +30,9 @@
namespace implementation {
bool SchedulingPolicyService::isAllowed() {
- // TODO(b/37291237)
- return true;
+ using ::android::hardware::IPCThreadState;
+
+ return IPCThreadState::self()->getCallingUid() == AID_CAMERASERVER;
}
Return<bool> SchedulingPolicyService::requestPriority(int32_t pid, int32_t tid, int32_t priority) {
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 8d381b1..a7f3a52 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -14,6 +14,7 @@
"RecentEventLogger.cpp",
"RotationVectorSensor.cpp",
"SensorDevice.cpp",
+ "SensorDeviceUtils.cpp",
"SensorDirectConnection.cpp",
"SensorEventConnection.cpp",
"SensorFusion.cpp",
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 7d9b0b7..535d0db 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -26,11 +26,10 @@
#include <cinttypes>
#include <thread>
-using android::hardware::hidl_vec;
-
using namespace android::hardware::sensors::V1_0;
using namespace android::hardware::sensors::V1_0::implementation;
-
+using android::hardware::hidl_vec;
+using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
namespace android {
// ---------------------------------------------------------------------------
@@ -52,7 +51,8 @@
}
}
-SensorDevice::SensorDevice() : mHidlTransportErrors(20) {
+SensorDevice::SensorDevice()
+ : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) {
if (!connectHidlService()) {
return;
}
@@ -87,35 +87,29 @@
}
bool SensorDevice::connectHidlService() {
- // SensorDevice may wait upto 100ms * 10 = 1s for hidl service.
- constexpr auto RETRY_DELAY = std::chrono::milliseconds(100);
+ // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
size_t retry = 10;
- while (true) {
- int initStep = 0;
+ while (retry-- > 0) {
mSensors = ISensors::getService();
- if (mSensors != nullptr) {
- ++initStep;
- // Poke ISensor service. If it has lingering connection from previous generation of
- // system server, it will kill itself. There is no intention to handle the poll result,
- // which will be done since the size is 0.
- if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
- // ok to continue
- break;
- }
- // hidl service is restarting, pointer is invalid.
- mSensors = nullptr;
- }
-
- if (--retry <= 0) {
- ALOGE("Cannot connect to ISensors hidl service!");
+ if (mSensors == nullptr) {
+ // no sensor hidl service found
break;
}
- // Delay 100ms before retry, hidl service is expected to come up in short time after
- // crash.
- ALOGI("%s unsuccessful, try again soon (remaining retry %zu).",
- (initStep == 0) ? "getService()" : "poll() check", retry);
- std::this_thread::sleep_for(RETRY_DELAY);
+
+ mRestartWaiter->reset();
+ // Poke ISensor service. If it has lingering connection from previous generation of
+ // system server, it will kill itself. There is no intention to handle the poll result,
+ // which will be done since the size is 0.
+ if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
+ // ok to continue
+ break;
+ }
+
+ // hidl service is restarting, pointer is invalid.
+ mSensors = nullptr;
+ ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
+ mRestartWaiter->wait();
}
return (mSensors != nullptr);
}
@@ -173,7 +167,7 @@
}
status_t SensorDevice::initCheck() const {
- return mSensors != NULL ? NO_ERROR : NO_INIT;
+ return mSensors != nullptr ? NO_ERROR : NO_INIT;
}
ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index fd6cee6..6d75051 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SENSOR_DEVICE_H
#define ANDROID_SENSOR_DEVICE_H
+#include "SensorDeviceUtils.h"
#include "SensorServiceUtils.h"
#include <sensor/Sensor.h>
@@ -39,12 +40,9 @@
namespace android {
// ---------------------------------------------------------------------------
-using SensorServiceUtil::Dumpable;
-using hardware::Return;
-class SensorDevice : public Singleton<SensorDevice>, public Dumpable {
+class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable {
public:
-
class HidlTransportErrorLog {
public:
@@ -105,7 +103,7 @@
private:
friend class Singleton<SensorDevice>;
- sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+ sp<hardware::sensors::V1_0::ISensors> mSensors;
Vector<sensor_t> mSensorList;
std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
@@ -174,6 +172,8 @@
}
return std::move(ret);
}
+ //TODO(b/67425500): remove waiter after bug is resolved.
+ sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
bool isClientDisabled(void* ident);
bool isClientDisabledLocked(void* ident);
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
new file mode 100644
index 0000000..b1344be
--- /dev/null
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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 "SensorDeviceUtils.h"
+
+#include <android/hardware/sensors/1.0/ISensors.h>
+#include <utils/Log.h>
+
+#include <chrono>
+#include <thread>
+
+using ::android::hardware::Void;
+using namespace android::hardware::sensors::V1_0;
+
+namespace android {
+namespace SensorDeviceUtils {
+
+HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter()
+ : mRegistered(ISensors::registerForNotifications("default", this)) {
+}
+
+Return<void> HidlServiceRegistrationWaiter::onRegistration(
+ const hidl_string &fqName, const hidl_string &name, bool preexisting) {
+ ALOGV("onRegistration fqName %s, name %s, preexisting %d",
+ fqName.c_str(), name.c_str(), preexisting);
+
+ {
+ std::lock_guard<std::mutex> lk(mLock);
+ mRestartObserved = true;
+ }
+ mCondition.notify_all();
+ return Void();
+}
+
+void HidlServiceRegistrationWaiter::reset() {
+ std::lock_guard<std::mutex> lk(mLock);
+ mRestartObserved = false;
+}
+
+bool HidlServiceRegistrationWaiter::wait() {
+ constexpr int DEFAULT_WAIT_MS = 100;
+ constexpr int TIMEOUT_MS = 1000;
+
+ if (!mRegistered) {
+ ALOGW("Cannot register service notification, use default wait(%d ms)", DEFAULT_WAIT_MS);
+ std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_WAIT_MS));
+ // not sure if service is actually restarted
+ return false;
+ }
+
+ std::unique_lock<std::mutex> lk(mLock);
+ return mCondition.wait_for(lk, std::chrono::milliseconds(TIMEOUT_MS),
+ [this]{return mRestartObserved;});
+}
+
+} // namespace SensorDeviceUtils
+} // namespace android
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
new file mode 100644
index 0000000..da36928
--- /dev/null
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_DEVICE_UTIL
+#define ANDROID_SENSOR_DEVICE_UTIL
+
+#include <android/hidl/manager/1.0/IServiceNotification.h>
+
+#include <condition_variable>
+#include <thread>
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hidl::manager::V1_0::IServiceNotification;
+
+namespace android {
+namespace SensorDeviceUtils {
+
+class HidlServiceRegistrationWaiter : public IServiceNotification {
+public:
+
+ HidlServiceRegistrationWaiter();
+
+ Return<void> onRegistration(const hidl_string &fqName,
+ const hidl_string &name,
+ bool preexisting) override;
+
+ void reset();
+
+ /**
+ * Wait for service restart
+ *
+ * @return true if service is restart since last reset(); false otherwise.
+ */
+ bool wait();
+private:
+ const bool mRegistered;
+
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ bool mRestartObserved;
+};
+
+} // namespace SensorDeviceUtils
+} // namespace android;
+
+#endif // ANDROID_SENSOR_SERVICE_UTIL
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
index e894655..8a9c36b 100644
--- a/services/sensorservice/tests/Android.mk
+++ b/services/sensorservice/tests/Android.mk
@@ -4,6 +4,8 @@
LOCAL_SRC_FILES:= \
sensorservicetest.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := \
libutils libsensor libandroid
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cc93105..4775e4e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -2,3 +2,5 @@
name: "libsurfaceflingerincludes",
export_include_dirs: ["."],
}
+
+subdirs = ["tests/fakehwc"]
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b28c9ba..95802bd 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -65,7 +65,7 @@
using namespace android::hardware::configstore::V1_0;
static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) == 3;
+ &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) >= 3;
#if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle)
// Dummy implementation in case it is missing.
@@ -401,6 +401,14 @@
return mVisibleLayersSortedByZ;
}
+void DisplayDevice::setLayersNeedingFences(const Vector< sp<Layer> >& layers) {
+ mLayersNeedingFences = layers;
+}
+
+const Vector< sp<Layer> >& DisplayDevice::getLayersNeedingFences() const {
+ return mLayersNeedingFences;
+}
+
Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
Region dirty;
if (repaintEverything) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 8636e2a..e8a5929 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -124,6 +124,8 @@
void setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
+ void setLayersNeedingFences(const Vector< sp<Layer> >& layers);
+ const Vector< sp<Layer> >& getLayersNeedingFences() const;
Region getDirtyRegion(bool repaintEverything) const;
void setLayerStack(uint32_t stack);
@@ -241,6 +243,8 @@
// list of visible layers on that display
Vector< sp<Layer> > mVisibleLayersSortedByZ;
+ // list of layers needing fences
+ Vector< sp<Layer> > mLayersNeedingFences;
/*
* Transaction state
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index cf01ad0..7d6d988 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -157,15 +157,11 @@
write64(metadata.usage);
}
-Composer::Composer(bool useVrComposer)
+Composer::Composer(const std::string& serviceName)
: mWriter(kWriterInitialSize),
- mIsUsingVrComposer(useVrComposer)
+ mIsUsingVrComposer(serviceName == std::string("vr"))
{
- if (mIsUsingVrComposer) {
- mComposer = IComposer::getService("vr");
- } else {
- mComposer = IComposer::getService(); // use default name
- }
+ mComposer = IComposer::getService(serviceName);
if (mComposer == nullptr) {
LOG_ALWAYS_FATAL("failed to get hwcomposer service");
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 533509b..31a3c1d 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -136,7 +136,7 @@
// Composer is a wrapper to IComposer, a proxy to server-side composer.
class Composer {
public:
- Composer(bool useVrComposer);
+ Composer(const std::string& serviceName);
std::vector<IComposer::Capability> getCapabilities();
std::string dumpDebugInfo();
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 1ac21c6..93c6d54 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -34,6 +34,7 @@
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
+#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
@@ -103,6 +104,7 @@
sp<Fence> acquireFence(Fence::NO_FENCE);
android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
+ mDataSpace = dataspace;
if (result != NO_ERROR) {
ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
strerror(-result), result);
@@ -249,7 +251,10 @@
#endif
void FramebufferSurface::dumpAsString(String8& result) const {
- ConsumerBase::dumpState(result);
+ Mutex::Autolock lock(mMutex);
+ result.appendFormat("FramebufferSurface: dataspace: %s(%d)\n",
+ dataspaceDetails(mDataSpace).c_str(), mDataSpace);
+ ConsumerBase::dumpLocked(result, "");
}
void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 69a72d7..a1756ca 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -83,6 +83,13 @@
// or the buffer is not associated with a slot.
int mCurrentBufferSlot;
+ // mDataSpace is the dataspace of the current composition buffer for
+ // this FramebufferSurface. It will be 0 when HWC is doing the
+ // compositing. Otherwise it will display the dataspace of the buffer
+ // use for compositing which can change as wide-color content is
+ // on/off.
+ android_dataspace mDataSpace;
+
// mCurrentBuffer is the current buffer or NULL to indicate that there is
// no current buffer.
sp<GraphicBuffer> mCurrentBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ae44ae0..78c0c85 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -98,8 +98,8 @@
// Device methods
-Device::Device(bool useVrComposer)
- : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
+Device::Device(const std::string& serviceName)
+ : mComposer(std::make_unique<Hwc2::Composer>(serviceName)),
mCapabilities(),
mDisplays(),
mRegisteredCallback(false)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 949f0e3..fbe4c7e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -79,10 +79,9 @@
class Device
{
public:
- // useVrComposer is passed to the composer HAL. When true, the composer HAL
- // will use the vr composer service, otherwise it uses the real hardware
- // composer.
- Device(bool useVrComposer);
+ // Service name is expected to be 'default' or 'vr' for normal use.
+ // 'vr' will slightly modify the behavior of the mComposer.
+ Device(const std::string& serviceName);
void registerCallback(ComposerCallback* callback, int32_t sequenceId);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3f3c67b..a16c040 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -59,7 +59,7 @@
// ---------------------------------------------------------------------------
-HWComposer::HWComposer(bool useVrComposer)
+HWComposer::HWComposer(const std::string& serviceName)
: mHwcDevice(),
mDisplayData(2),
mFreeDisplaySlots(),
@@ -73,7 +73,7 @@
mVSyncCounts[i] = 0;
}
- mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
+ mHwcDevice = std::make_unique<HWC2::Device>(serviceName);
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
@@ -444,10 +444,16 @@
HWC2::Error error = HWC2::Error::None;
- // First try to skip validate altogether if the HWC supports it.
+ // First try to skip validate altogether when there is no client
+ // composition. When there is client composition, since we haven't
+ // rendered to the client target yet, we should not attempt to skip
+ // validate.
+ //
+ // displayData.hasClientComposition hasn't been updated for this frame.
+ // The check below is incorrect. We actually rely on HWC here to fall
+ // back to validate when there is any client layer.
displayData.validateWasSkipped = false;
- if (hasCapability(HWC2::Capability::SkipValidate) &&
- !displayData.hasClientComposition) {
+ if (!displayData.hasClientComposition) {
sp<android::Fence> outPresentFence;
uint32_t state = UINT32_MAX;
error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e25dee1..3640bb5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -65,10 +65,9 @@
class HWComposer
{
public:
- // useVrComposer is passed to the composer HAL. When true, the composer HAL
- // will use the vr composer service, otherwise it uses the real hardware
- // composer.
- HWComposer(bool useVrComposer);
+ // Uses the named composer service. Valid choices for normal use
+ // are 'default' and 'vr'.
+ HWComposer(const std::string& serviceName);
~HWComposer();
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index c953c68..01c9c0f 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -133,8 +133,6 @@
0.7, 0.7, 1.0, 0,
0, 0, 0, 1);
- const mat4 identity;
-
// And the magic happens here...
// We construct the matrix that will perform the whole correction.
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index a9bb2ba..f647742 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -248,7 +248,7 @@
// find out connections waiting for events
size_t count = mDisplayEventConnections.size();
- for (size_t i=0 ; i<count ; i++) {
+ for (size_t i=0 ; i<count ; ) {
sp<Connection> connection(mDisplayEventConnections[i].promote());
if (connection != NULL) {
bool added = false;
@@ -279,11 +279,12 @@
// messages.
signalConnections.add(connection);
}
+ ++i;
} else {
// we couldn't promote this reference, the connection has
// died, so clean-up!
mDisplayEventConnections.removeAt(i);
- --i; --count;
+ --count;
}
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 038ece2..70c702b 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -40,6 +40,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
+#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include "clz.h"
@@ -69,7 +70,7 @@
: contentDirty(false),
sequence(uint32_t(android_atomic_inc(&sSequence))),
mFlinger(flinger),
- mTextureName(-1U),
+ mTextureName(UINT32_MAX),
mPremultipliedAlpha(true),
mName("unnamed"),
mFormat(PIXEL_FORMAT_NONE),
@@ -216,9 +217,6 @@
#ifdef USE_HWC2
void Layer::onLayerDisplayed(const sp<Fence>& releaseFence) {
- if (mHwcLayers.empty()) {
- return;
- }
mSurfaceFlingerConsumer->setReleaseFence(releaseFence);
}
#else
@@ -394,9 +392,9 @@
return true;
}
-void Layer::destroyHwcLayer(int32_t hwcId) {
+bool Layer::destroyHwcLayer(int32_t hwcId) {
if (mHwcLayers.count(hwcId) == 0) {
- return;
+ return false;
}
auto& hwcInfo = mHwcLayers[hwcId];
LOG_ALWAYS_FATAL_IF(hwcInfo.layer == nullptr,
@@ -407,6 +405,8 @@
// mHwcLayers. Verify that.
LOG_ALWAYS_FATAL_IF(mHwcLayers.count(hwcId) != 0,
"Stale layer entry in mHwcLayers");
+
+ return true;
}
void Layer::destroyAllHwcLayers() {
@@ -2420,69 +2420,51 @@
// debugging
// ----------------------------------------------------------------------------
-void Layer::dump(String8& result, Colorizer& colorizer) const
-{
- const Layer::State& s(getDrawingState());
-
- colorizer.colorize(result, Colorizer::GREEN);
- result.appendFormat(
- "+ %s %p (%s)\n",
- getTypeId(), this, getName().string());
- colorizer.reset(result);
-
- s.activeTransparentRegion.dump(result, "transparentRegion");
- visibleRegion.dump(result, "visibleRegion");
- surfaceDamageRegion.dump(result, "surfaceDamageRegion");
- sp<Client> client(mClientRef.promote());
- PixelFormat pf = PIXEL_FORMAT_UNKNOWN;
- const sp<GraphicBuffer>& buffer(getActiveBuffer());
- if (buffer != NULL) {
- pf = buffer->getPixelFormat();
+LayerDebugInfo Layer::getLayerDebugInfo() const {
+ LayerDebugInfo info;
+ const Layer::State& ds = getDrawingState();
+ info.mName = getName();
+ sp<Layer> parent = getParent();
+ info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
+ info.mType = String8(getTypeId());
+ info.mTransparentRegion = ds.activeTransparentRegion;
+ info.mVisibleRegion = visibleRegion;
+ info.mSurfaceDamageRegion = surfaceDamageRegion;
+ info.mLayerStack = getLayerStack();
+ info.mX = ds.active.transform.tx();
+ info.mY = ds.active.transform.ty();
+ info.mZ = ds.z;
+ info.mWidth = ds.active.w;
+ info.mHeight = ds.active.h;
+ info.mCrop = ds.crop;
+ info.mFinalCrop = ds.finalCrop;
+ info.mAlpha = ds.alpha;
+ info.mFlags = ds.flags;
+ info.mPixelFormat = getPixelFormat();
+ info.mDataSpace = getDataSpace();
+ info.mMatrix[0][0] = ds.active.transform[0][0];
+ info.mMatrix[0][1] = ds.active.transform[0][1];
+ info.mMatrix[1][0] = ds.active.transform[1][0];
+ info.mMatrix[1][1] = ds.active.transform[1][1];
+ {
+ sp<const GraphicBuffer> activeBuffer = getActiveBuffer();
+ if (activeBuffer != 0) {
+ info.mActiveBufferWidth = activeBuffer->getWidth();
+ info.mActiveBufferHeight = activeBuffer->getHeight();
+ info.mActiveBufferStride = activeBuffer->getStride();
+ info.mActiveBufferFormat = activeBuffer->format;
+ } else {
+ info.mActiveBufferWidth = 0;
+ info.mActiveBufferHeight = 0;
+ info.mActiveBufferStride = 0;
+ info.mActiveBufferFormat = 0;
+ }
}
-
- result.appendFormat( " "
- "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), "
- "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), "
- "isOpaque=%1d, invalidate=%1d, "
- "dataspace=%s, pixelformat=%s "
-#ifdef USE_HWC2
- "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#else
- "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#endif
- " client=%p\n",
- getLayerStack(), s.z,
- s.active.transform.tx(), s.active.transform.ty(),
- s.active.w, s.active.h,
- s.crop.left, s.crop.top,
- s.crop.right, s.crop.bottom,
- s.finalCrop.left, s.finalCrop.top,
- s.finalCrop.right, s.finalCrop.bottom,
- isOpaque(s), contentDirty,
- dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(),
- s.alpha, s.flags,
- 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);
- uint32_t w0=0, h0=0, s0=0, f0=0;
- if (buf0 != 0) {
- w0 = buf0->getWidth();
- h0 = buf0->getHeight();
- s0 = buf0->getStride();
- f0 = buf0->format;
- }
- result.appendFormat(
- " "
- "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
- " queued-frames=%d, mRefreshPending=%d\n",
- mFormat, w0, h0, s0,f0,
- mQueuedFrames, mRefreshPending);
-
- if (mSurfaceFlingerConsumer != 0) {
- mSurfaceFlingerConsumer->dumpState(result, " ");
- }
+ info.mNumQueuedFrames = getQueuedFrameCount();
+ info.mRefreshPending = isBufferLatched();
+ info.mIsOpaque = isOpaque(ds);
+ info.mContentDirty = contentDirty;
+ return info;
}
#ifdef USE_HWC2
@@ -2677,6 +2659,7 @@
return mDrawingState.z;
}
+__attribute__((no_sanitize("unsigned-integer-overflow")))
LayerVector Layer::makeTraversalList(LayerVector::StateSet stateSet) {
LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
"makeTraversalList received invalid stateSet");
@@ -2732,7 +2715,7 @@
LayerVector list = makeTraversalList(stateSet);
int32_t i = 0;
- for (i = list.size()-1; i>=0; i--) {
+ for (i = int32_t(list.size()) - 1; i >= 0; i--) {
const auto& relative = list[i];
if (relative->getZ() < 0) {
break;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c34d8a0..f7501a3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -60,6 +60,7 @@
class DisplayDevice;
class GraphicBuffer;
class SurfaceFlinger;
+class LayerDebugInfo;
// ---------------------------------------------------------------------------
@@ -447,11 +448,13 @@
bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
mSidebandStreamChanged || mAutoRefresh; }
+ int32_t getQueuedFrameCount() const { return mQueuedFrames; }
+
#ifdef USE_HWC2
// -----------------------------------------------------------------------
bool createHwcLayer(HWComposer* hwc, int32_t hwcId);
- void destroyHwcLayer(int32_t hwcId);
+ bool destroyHwcLayer(int32_t hwcId);
void destroyAllHwcLayers();
bool hasHwcLayer(int32_t hwcId) {
@@ -479,9 +482,9 @@
inline const State& getCurrentState() const { return mCurrentState; }
inline State& getCurrentState() { return mCurrentState; }
+ LayerDebugInfo getLayerDebugInfo() const;
/* always call base class first */
- void dump(String8& result, Colorizer& colorizer) const;
#ifdef USE_HWC2
static void miniDumpHeader(String8& result);
void miniDump(String8& result, int32_t hwcId) const;
@@ -679,6 +682,9 @@
sp<IGraphicBufferProducer> getProducer() const;
const String8& getName() const;
void notifyAvailableFrames();
+
+ PixelFormat getPixelFormat() const { return mFormat; }
+
private:
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 2233e78..d0f8fbe 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -35,14 +35,17 @@
uint32_t ls = l->getCurrentState().layerStack;
uint32_t rs = r->getCurrentState().layerStack;
if (ls != rs)
- return ls - rs;
+ return (ls > rs) ? 1 : -1;
uint32_t lz = l->getCurrentState().z;
uint32_t rz = r->getCurrentState().z;
if (lz != rz)
- return lz - rz;
+ return (lz > rz) ? 1 : -1;
- return l->sequence - r->sequence;
+ if (l->sequence == r->sequence)
+ return 0;
+
+ return (l->sequence > r->sequence) ? 1 : -1;
}
void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) const {
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 37a530b..9c0af8b 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -166,12 +166,12 @@
size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap,
Transform::orientation_flags rotation) {
- size_t l = sourceCrop.left;
- size_t r = sourceCrop.right;
+ int32_t l = sourceCrop.left;
+ int32_t r = sourceCrop.right;
// In GL, (0, 0) is the bottom-left corner, so flip y coordinates
- size_t t = hwh - sourceCrop.top;
- size_t b = hwh - sourceCrop.bottom;
+ int32_t t = hwh - sourceCrop.top;
+ int32_t b = hwh - sourceCrop.bottom;
mat4 m;
if (yswap) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4055693..ee85e38 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -46,6 +46,7 @@
#include <gui/BufferQueue.h>
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <ui/GraphicBufferAllocator.h>
@@ -138,6 +139,21 @@
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
bool SurfaceFlinger::hasWideColorDisplay;
+
+std::string getHwcServiceName() {
+ char value[PROPERTY_VALUE_MAX] = {};
+ property_get("debug.sf.hwc_service_name", value, "default");
+ ALOGI("Using HWComposer service: '%s'", value);
+ return std::string(value);
+}
+
+bool useTrebleTestingOverride() {
+ char value[PROPERTY_VALUE_MAX] = {};
+ property_get("debug.sf.treble_testing_override", value, "false");
+ ALOGI("Treble testing override: '%s'", value);
+ return std::string(value) == "true";
+}
+
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(),
mTransactionFlags(0),
@@ -146,6 +162,7 @@
mLayersRemoved(false),
mLayersAdded(false),
mRepaintEverything(0),
+ mHwcServiceName(getHwcServiceName()),
mRenderEngine(nullptr),
mBootTime(systemTime()),
mBuiltinDisplays(),
@@ -247,6 +264,15 @@
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
// instead read after the boot animation
+
+ if (useTrebleTestingOverride()) {
+ // Without the override SurfaceFlinger cannot connect to HIDL
+ // services that are not listed in the manifests. Considered
+ // deriving the setting from the set service name, but it
+ // would be brittle if the name that's not 'default' is used
+ // for production purposes later on.
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ }
}
void SurfaceFlinger::onFirstRef()
@@ -601,7 +627,7 @@
LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
"Starting with vr flinger active is not currently supported.");
- mHwc.reset(new HWComposer(false));
+ mHwc.reset(new HWComposer(mHwcServiceName));
mHwc->registerCallback(this, mComposerSequenceId);
if (useVrFlinger) {
@@ -1092,6 +1118,33 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(sDump, pid, uid)) {
+ ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+
+ // Try to acquire a lock for 1s, fail gracefully
+ const status_t err = mStateLock.timedLock(s2ns(1));
+ const bool locked = (err == NO_ERROR);
+ if (!locked) {
+ ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+ return TIMED_OUT;
+ }
+
+ outLayers->clear();
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ outLayers->push_back(layer->getLayerDebugInfo());
+ });
+
+ mStateLock.unlock();
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1382,7 +1435,8 @@
resetDisplayState();
mHwc.reset(); // Delete the current instance before creating the new one
- mHwc.reset(new HWComposer(vrFlingerRequestsDisplay));
+ mHwc.reset(new HWComposer(
+ vrFlingerRequestsDisplay ? "vr" : mHwcServiceName));
mHwc->registerCallback(this, ++mComposerSequenceId);
LOG_ALWAYS_FATAL_IF(!mHwc->getComposer()->isRemote(),
@@ -1728,6 +1782,7 @@
Region opaqueRegion;
Region dirtyRegion;
Vector<sp<Layer>> layersSortedByZ;
+ Vector<sp<Layer>> layersNeedingFences;
const sp<DisplayDevice>& displayDevice(mDisplays[dpy]);
const Transform& tr(displayDevice->getTransform());
const Rect bounds(displayDevice->getBounds());
@@ -1735,6 +1790,7 @@
computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
mDrawingState.traverseInZOrder([&](Layer* layer) {
+ bool hwcLayerDestroyed = false;
if (layer->belongsToDisplay(displayDevice->getLayerStack(),
displayDevice->isPrimary())) {
Region drawRegion(tr.transform(
@@ -1745,18 +1801,32 @@
} else {
// Clear out the HWC layer if this layer was
// previously visible, but no longer is
- layer->destroyHwcLayer(
+ hwcLayerDestroyed = layer->destroyHwcLayer(
displayDevice->getHwcDisplayId());
}
} else {
// WM changes displayDevice->layerStack upon sleep/awake.
// Here we make sure we delete the HWC layers even if
// WM changed their layer stack.
- layer->destroyHwcLayer(displayDevice->getHwcDisplayId());
+ hwcLayerDestroyed = layer->destroyHwcLayer(
+ displayDevice->getHwcDisplayId());
+ }
+
+ // If a layer is not going to get a release fence because
+ // it is invisible, but it is also going to release its
+ // old buffer, add it to the list of layers needing
+ // fences.
+ if (hwcLayerDestroyed) {
+ auto found = std::find(mLayersWithQueuedFrames.cbegin(),
+ mLayersWithQueuedFrames.cend(), layer);
+ if (found != mLayersWithQueuedFrames.cend()) {
+ layersNeedingFences.add(layer);
+ }
}
});
}
displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
+ displayDevice->setLayersNeedingFences(layersNeedingFences);
displayDevice->undefinedRegion.set(bounds);
displayDevice->undefinedRegion.subtractSelf(
tr.transform(opaqueRegion));
@@ -1978,15 +2048,35 @@
displayDevice->onSwapBuffersCompleted();
displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- sp<Fence> releaseFence = Fence::NO_FENCE;
+ // The layer buffer from the previous frame (if any) is released
+ // by HWC only when the release fence from this frame (if any) is
+ // signaled. Always get the release fence from HWC first.
+ auto hwcLayer = layer->getHwcLayer(hwcId);
+ sp<Fence> releaseFence = mHwc->getLayerReleaseFence(hwcId, hwcLayer);
+
+ // If the layer was client composited in the previous frame, we
+ // need to merge with the previous client target acquire fence.
+ // Since we do not track that, always merge with the current
+ // client target acquire fence when it is available, even though
+ // this is suboptimal.
if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) {
- releaseFence = displayDevice->getClientTargetAcquireFence();
- } else {
- auto hwcLayer = layer->getHwcLayer(hwcId);
- releaseFence = mHwc->getLayerReleaseFence(hwcId, hwcLayer);
+ releaseFence = Fence::merge("LayerRelease", releaseFence,
+ displayDevice->getClientTargetAcquireFence());
}
+
layer->onLayerDisplayed(releaseFence);
}
+
+ // We've got a list of layers needing fences, that are disjoint with
+ // displayDevice->getVisibleLayersSortedByZ. The best we can do is to
+ // supply them with the present fence.
+ if (!displayDevice->getLayersNeedingFences().isEmpty()) {
+ sp<Fence> presentFence = mHwc->getPresentFence(hwcId);
+ for (auto& layer : displayDevice->getLayersNeedingFences()) {
+ layer->onLayerDisplayed(presentFence);
+ }
+ }
+
if (hwcId >= 0) {
mHwc->clearReleaseFences(hwcId);
}
@@ -2073,7 +2163,7 @@
// (ie: in drawing state but not in current state)
// also handle displays that changed
// (ie: displays that are in both lists)
- for (size_t i=0 ; i<dc ; i++) {
+ for (size_t i=0 ; i<dc ;) {
const ssize_t j = curr.indexOfKey(draw.keyAt(i));
if (j < 0) {
// in drawing state but not in current state
@@ -2108,7 +2198,7 @@
hw->disconnect(getHwComposer());
mDisplays.removeItem(display);
mDrawingState.displays.removeItemsAt(i);
- dc--; i--;
+ dc--;
// at this point we must loop to the next item
continue;
}
@@ -2130,6 +2220,7 @@
}
}
}
+ ++i;
}
// find displays that were added
@@ -2255,7 +2346,7 @@
sp<const DisplayDevice> hw(mDisplays[dpy]);
if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
if (disp == NULL) {
- disp = hw;
+ disp = std::move(hw);
} else {
disp = NULL;
break;
@@ -3029,7 +3120,10 @@
}
}
if (what & layer_state_t::eRelativeLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -3755,7 +3849,7 @@
result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
colorizer.reset(result);
mCurrentState.traverseInZOrder([&](Layer* layer) {
- layer->dump(result, colorizer);
+ result.append(to_string(layer->getLayerDebugInfo()).c_str());
});
/*
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 99d4a1a..231709d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -313,6 +313,7 @@
HdrCapabilities* outCapabilities) const;
virtual status_t enableVSyncInjections(bool enable);
virtual status_t injectVSync(nsecs_t when);
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
/* ------------------------------------------------------------------------
@@ -672,6 +673,10 @@
// acquiring mStateLock.
std::unique_ptr<HWComposer> mHwc;
+#ifdef USE_HWC2
+ const std::string mHwcServiceName; // "default" for real use, something else for testing.
+#endif
+
// constant members (no synchronization needed for access)
RenderEngine* mRenderEngine;
nsecs_t mBootTime;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b28fe68..1d6fbaf 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -41,6 +41,7 @@
#include <gui/BufferQueue.h>
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <ui/GraphicBufferAllocator.h>
@@ -178,6 +179,8 @@
maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
&ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+ mPrimaryDispSync.init(hasSyncFramework, dispSyncPresentTimeOffset);
+
char value[PROPERTY_VALUE_MAX];
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
@@ -931,6 +934,34 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(sDump, pid, uid)) {
+ ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+
+ // Try to acquire a lock for 1s, fail gracefully
+ status_t err = mStateLock.timedLock(s2ns(1));
+ bool locked = (err == NO_ERROR);
+ if (!locked) {
+ ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+ return TIMED_OUT;
+ }
+
+ outLayers->clear();
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ outLayers->push_back(layer->getLayerDebugInfo());
+ });
+
+ mStateLock.unlock();
+
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -2577,6 +2608,14 @@
}
}
}
+ if (what & layer_state_t::eRelativeLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+ if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
+ flags |= eTransactionNeeded|eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eSizeChanged) {
if (layer->setSize(s.w, s.h)) {
flags |= eTraversalNeeded;
@@ -3254,7 +3293,7 @@
result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
colorizer.reset(result);
mCurrentState.traverseInZOrder([&](Layer* layer) {
- layer->dump(result, colorizer);
+ result.append(to_string(layer->getLayerDebugInfo()).c_str());
});
/*
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
new file mode 100644
index 0000000..94f3f25
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -0,0 +1,35 @@
+cc_test {
+ name: "sffakehwc_test",
+ srcs: [
+ "FakeComposerClient.cpp",
+ "FakeComposerService.cpp",
+ "FakeComposerUtils.cpp",
+ "SFFakeHwc_test.cpp"
+ ],
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libbinder",
+ "libui",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.mapper@2.0",
+ "libhwbinder",
+ "libhardware",
+ "libhidlbase",
+ "libsync",
+ "libfmq",
+ "libbase",
+ "libhidltransport"
+ ],
+ static_libs: [
+ "libhwcomposer-client",
+ "libsurfaceflingerincludes",
+ "libtrace_proto",
+ "libgmock"
+ ],
+ tags: ["tests"],
+ test_suites: ["device-tests"]
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
new file mode 100644
index 0000000..07b8cc0
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeComposer"
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+#include <inttypes.h>
+#include <time.h>
+#include <algorithm>
+#include <condition_variable>
+#include <iostream>
+#include <mutex>
+#include <set>
+#include <thread>
+
+constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
+constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1);
+
+using namespace sftest;
+
+using android::Condition;
+using android::Mutex;
+
+using Clock = std::chrono::steady_clock;
+using TimePoint = std::chrono::time_point<Clock>;
+
+namespace {
+
+// Internal state of a layer in the HWC API.
+class LayerImpl {
+public:
+ LayerImpl() = default;
+
+ bool mValid = true;
+ RenderState mRenderState;
+ uint32_t mZ = 0;
+};
+
+// Struct for storing per frame rectangle state. Contains the render
+// state shared to the test case. Basically a snapshot and a subset of
+// LayerImpl sufficient to re-create the pixels of a layer for the
+// frame.
+struct FrameRect {
+public:
+ FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
+ : layer(layer_), renderState(state), z(z_) {}
+
+ const Layer layer;
+ const RenderState renderState;
+ const uint32_t z;
+};
+
+// Collection of FrameRects forming one rendered frame. Could store
+// related fences and other data in the future.
+class Frame {
+public:
+ Frame() = default;
+ std::vector<std::unique_ptr<FrameRect>> rectangles;
+};
+
+class DelayedEventGenerator {
+public:
+ DelayedEventGenerator(std::function<void()> onTimerExpired)
+ : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {}
+
+ ~DelayedEventGenerator() {
+ ALOGI("DelayedEventGenerator exiting.");
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mRunning = false;
+ mWakeups.clear();
+ mCondition.notify_one();
+ }
+ mThread.join();
+ ALOGI("DelayedEventGenerator exited.");
+ }
+
+ void wakeAfter(std::chrono::nanoseconds waitTime) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mWakeups.insert(Clock::now() + waitTime);
+ mCondition.notify_one();
+ }
+
+private:
+ void loop() {
+ while (true) {
+ // Lock scope
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
+ if (!mRunning && mWakeups.empty()) {
+ // This thread should only exit once the destructor has been called and all
+ // wakeups have been processed
+ return;
+ }
+
+ // At this point, mWakeups will not be empty
+
+ TimePoint target = *(mWakeups.begin());
+ auto status = mCondition.wait_until(lock, target);
+ while (status == std::cv_status::no_timeout) {
+ // This was either a spurious wakeup or another wakeup was added, so grab the
+ // oldest point and wait again
+ target = *(mWakeups.begin());
+ status = mCondition.wait_until(lock, target);
+ }
+
+ // status must have been timeout, so we can finally clear this point
+ mWakeups.erase(target);
+ }
+ // Callback *without* locks!
+ mOnTimerExpired();
+ }
+ }
+
+ std::function<void()> mOnTimerExpired;
+ std::thread mThread;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mRunning = true;
+ std::set<TimePoint> mWakeups;
+};
+
+} // namespace
+
+FakeComposerClient::FakeComposerClient()
+ : mCallbacksOn(false),
+ mClient(nullptr),
+ mCurrentConfig(NULL_DISPLAY_CONFIG),
+ mVsyncEnabled(false),
+ mLayers(),
+ mDelayedEventGenerator(
+ std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
+ mSurfaceComposer(nullptr) {}
+
+FakeComposerClient::~FakeComposerClient() {}
+
+bool FakeComposerClient::hasCapability(hwc2_capability_t /*capability*/) {
+ return false;
+}
+
+void FakeComposerClient::removeClient() {
+ ALOGV("removeClient");
+ // TODO: Ahooga! Only thing current lifetime management choices in
+ // APIs make possible. Sad.
+ delete this;
+}
+
+void FakeComposerClient::enableCallback(bool enable) {
+ ALOGV("enableCallback");
+ mCallbacksOn = enable;
+ if (mCallbacksOn) {
+ mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ }
+}
+
+void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
+ if (mCallbacksOn) {
+ mClient->onHotplug(display, state);
+ }
+}
+
+uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
+ ALOGV("getMaxVirtualDisplayCount");
+ return 1;
+}
+
+Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat* /*format*/, Display* /*outDisplay*/) {
+ ALOGV("createVirtualDisplay");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+ ALOGV("destroyVirtualDisplay");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+ ALOGV("createLayer");
+ *outLayer = mLayers.size();
+ auto newLayer = std::make_unique<LayerImpl>();
+ mLayers.push_back(std::move(newLayer));
+ return Error::NONE;
+}
+
+Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+ ALOGV("destroyLayer");
+ mLayers[layer]->mValid = false;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+ ALOGV("getActiveConfig");
+
+ // TODO Assert outConfig != nullptr
+
+ // TODO This is my reading of the
+ // IComposerClient::getActiveConfig, but returning BAD_CONFIG
+ // seems to not fit SurfaceFlinger plans. See version 2 below.
+ // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
+ // return Error::BAD_CONFIG;
+ // }
+ //*outConfig = mCurrentConfig;
+ *outConfig = 1; // Very special config for you my friend
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+ uint32_t /*height*/, PixelFormat /*format*/,
+ Dataspace /*dataspace*/) {
+ ALOGV("getClientTargetSupport");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+ ALOGV("getColorModes");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+ IComposerClient::Attribute attribute,
+ int32_t* outValue) {
+ ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
+ static_cast<int>(config), static_cast<int>(attribute), outValue);
+
+ // TODO: SOOO much fun to be had with these alone
+ switch (attribute) {
+ case IComposerClient::Attribute::WIDTH:
+ *outValue = 1920;
+ break;
+ case IComposerClient::Attribute::HEIGHT:
+ *outValue = 1080;
+ break;
+ case IComposerClient::Attribute::VSYNC_PERIOD:
+ *outValue = 1666666666;
+ break; // TOOD: Tests break down if lowered to 16ms?
+ case IComposerClient::Attribute::DPI_X:
+ *outValue = 240;
+ break;
+ case IComposerClient::Attribute::DPI_Y:
+ *outValue = 240;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Say what!?! New attribute");
+ }
+
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
+ ALOGV("getDisplayConfigs");
+ // TODO assert display == 1, outConfigs != nullptr
+
+ outConfigs->resize(1);
+ (*outConfigs)[0] = 1;
+
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+ ALOGV("getDisplayName");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayType(Display /*display*/,
+ IComposerClient::DisplayType* outType) {
+ ALOGV("getDisplayType");
+ // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+ // assumed to be physical?
+ *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+ ALOGV("getDozeSupport");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
+ float* /*outMaxLuminance*/,
+ float* /*outMaxAverageLuminance*/,
+ float* /*outMinLuminance*/) {
+ ALOGV("getHdrCapabilities");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
+ ALOGV("setActiveConfig");
+ mCurrentConfig = config;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
+ ALOGV("setColorMode");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
+ ALOGV("setPowerMode");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
+ mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+ ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+ int32_t /*hint*/) {
+ ALOGV("setColorTransform");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+ int32_t /*acquireFence*/, int32_t /*dataspace*/,
+ const std::vector<hwc_rect_t>& /*damage*/) {
+ ALOGV("setClientTarget");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+ int32_t /*releaseFence*/) {
+ ALOGV("setOutputBuffer");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::validateDisplay(
+ Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+ std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+ uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+ std::vector<uint32_t>* /*outRequestMasks*/) {
+ ALOGV("validateDisplay");
+ // TODO: Assume touching nothing means All Korrekt!
+ return Error::NONE;
+}
+
+Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+ ALOGV("acceptDisplayChanges");
+ // Didn't ask for changes because software is omnipotent.
+ return Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+ return a->z <= b->z;
+}
+
+Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+ std::vector<Layer>* /*outLayers*/,
+ std::vector<int32_t>* /*outReleaseFences*/) {
+ ALOGV("presentDisplay");
+ // TODO Leaving layers and their fences out for now. Doing so
+ // means that we've already processed everything. Important to
+ // test that the fences are respected, though. (How?)
+
+ std::unique_ptr<Frame> newFrame(new Frame);
+ for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+ const LayerImpl& layerImpl = *mLayers[layer];
+
+ if (!layerImpl.mValid) continue;
+
+ auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+ newFrame->rectangles.push_back(std::move(rect));
+ }
+ std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+ {
+ Mutex::Autolock _l(mStateMutex);
+ mFrames.push_back(std::move(newFrame));
+ mFramesAvailable.broadcast();
+ }
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+ int32_t /*x*/, int32_t /*y*/) {
+ ALOGV("setLayerCursorPosition");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
+ int32_t acquireFence) {
+ ALOGV("setLayerBuffer");
+ LayerImpl& l = getLayerImpl(layer);
+ if (buffer != l.mRenderState.mBuffer) {
+ l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+ }
+ l.mRenderState.mBuffer = buffer;
+ l.mRenderState.mAcquireFence = acquireFence;
+
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+ const std::vector<hwc_rect_t>& /*damage*/) {
+ ALOGV("setLayerSurfaceDamage");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+ ALOGV("setLayerBlendMode");
+ getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+ IComposerClient::Color color) {
+ ALOGV("setLayerColor");
+ getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+ getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+ getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+ getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+ int32_t /*type*/) {
+ ALOGV("setLayerCompositionType");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+ int32_t /*dataspace*/) {
+ ALOGV("setLayerDataspace");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+ const hwc_rect_t& frame) {
+ ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+ frame.bottom);
+ getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+ ALOGV("setLayerPlaneAlpha");
+ getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+ buffer_handle_t /*stream*/) {
+ ALOGV("setLayerSidebandStream");
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+ const hwc_frect_t& crop) {
+ ALOGV("setLayerSourceCrop");
+ getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
+ ALOGV("setLayerTransform");
+ getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+ const std::vector<hwc_rect_t>& visible) {
+ ALOGV("setLayerVisibleRegion");
+ getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+ return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+ ALOGV("setLayerZOrder");
+ getLayerImpl(layer).mZ = z;
+ return Error::NONE;
+}
+
+//////////////////////////////////////////////////////////////////
+
+void FakeComposerClient::setClient(ComposerClient* client) {
+ mClient = client;
+}
+
+void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
+ if (mCallbacksOn) {
+ uint64_t timestamp = vsyncTime;
+ ALOGV("Vsync");
+ if (timestamp == 0) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ }
+ if (mSurfaceComposer != nullptr) {
+ mSurfaceComposer->injectVSync(timestamp);
+ } else {
+ mClient->onVsync(DEFAULT_DISPLAY, timestamp);
+ }
+ }
+}
+
+void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
+ mDelayedEventGenerator->wakeAfter(wait);
+}
+
+LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
+ // TODO Change these to an internal state check that can be
+ // invoked from the gtest? GTest macros do not seem all that safe
+ // when used outside the test class
+ EXPECT_GE(handle, static_cast<Layer>(0));
+ EXPECT_LT(handle, mLayers.size());
+ return *(mLayers[handle]);
+}
+
+int FakeComposerClient::getFrameCount() const {
+ return mFrames.size();
+}
+
+static std::vector<RenderState> extractRenderState(
+ const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
+ std::vector<RenderState> result;
+ result.reserve(internalRects.size());
+ for (const std::unique_ptr<FrameRect>& rect : internalRects) {
+ result.push_back(rect->renderState);
+ }
+ return result;
+}
+
+std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
+ Mutex::Autolock _l(mStateMutex);
+ return extractRenderState(mFrames[frame]->rectangles);
+}
+
+std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
+ Mutex::Autolock _l(mStateMutex);
+ return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
+}
+
+void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
+ int currentFrame = 0;
+ {
+ Mutex::Autolock _l(mStateMutex); // I hope this is ok...
+ currentFrame = static_cast<int>(mFrames.size());
+ requestVSync();
+ }
+ waitUntilFrame(currentFrame + 1, maxWait);
+}
+
+void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
+ Mutex::Autolock _l(mStateMutex);
+ while (mFrames.size() < static_cast<size_t>(targetFrame)) {
+ android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
+ if (result == android::TIMED_OUT) {
+ ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
+ mFrames.size(), maxWait.count());
+ return;
+ }
+ }
+}
+
+void FakeComposerClient::clearFrames() {
+ Mutex::Autolock _l(mStateMutex);
+ mFrames.clear();
+ for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
+ if (layer->mValid) {
+ layer->mRenderState.mSwapCount = 0;
+ }
+ }
+}
+
+void FakeComposerClient::onSurfaceFlingerStart() {
+ mSurfaceComposer = nullptr;
+ do {
+ mSurfaceComposer = new android::SurfaceComposerClient;
+ android::status_t initResult = mSurfaceComposer->initCheck();
+ if (initResult != android::NO_ERROR) {
+ ALOGD("Init result: %d", initResult);
+ mSurfaceComposer = nullptr;
+ std::this_thread::sleep_for(10ms);
+ }
+ } while (mSurfaceComposer == nullptr);
+ ALOGD("SurfaceComposerClient created");
+ mSurfaceComposer->enableVSyncInjections(true);
+}
+
+void FakeComposerClient::onSurfaceFlingerStop() {
+ mSurfaceComposer->dispose();
+ mSurfaceComposer.clear();
+}
+
+// Includes destroyed layers, stored in order of creation.
+int FakeComposerClient::getLayerCount() const {
+ return mLayers.size();
+}
+
+Layer FakeComposerClient::getLayer(size_t index) const {
+ // NOTE: If/when passing calls through to actual implementation,
+ // this might get more involving.
+ return static_cast<Layer>(index);
+}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
new file mode 100644
index 0000000..dd384c0
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#pragma once
+
+#include "ComposerClient.h"
+#include "RenderState.h"
+
+#include <utils/Condition.h>
+
+#include <chrono>
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace {
+class LayerImpl;
+class Frame;
+class DelayedEventGenerator;
+} // namespace
+
+namespace android {
+class SurfaceComposerClient;
+} // namespace android
+
+namespace sftest {
+
+class FakeComposerClient : public ComposerBase {
+public:
+ FakeComposerClient();
+ virtual ~FakeComposerClient();
+
+ bool hasCapability(hwc2_capability_t capability) override;
+
+ void removeClient() override;
+ void enableCallback(bool enable) override;
+ uint32_t getMaxVirtualDisplayCount() override;
+ Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ Display* outDisplay) override;
+ Error destroyVirtualDisplay(Display display) override;
+ Error createLayer(Display display, Layer* outLayer) override;
+ Error destroyLayer(Display display, Layer layer) override;
+
+ Error getActiveConfig(Display display, Config* outConfig) override;
+ Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+ PixelFormat format, Dataspace dataspace) override;
+ Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
+ Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+ int32_t* outValue) override;
+ Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+ Error getDisplayName(Display display, hidl_string* outName) override;
+ Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+ Error getDozeSupport(Display display, bool* outSupport) override;
+ Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+ Error setActiveConfig(Display display, Config config) override;
+ Error setColorMode(Display display, ColorMode mode) override;
+ Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+ Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+ Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+ Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+ int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+ Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
+ Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+ std::vector<IComposerClient::Composition>* outCompositionTypes,
+ uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+ std::vector<uint32_t>* outRequestMasks) override;
+ Error acceptDisplayChanges(Display display) override;
+ Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
+ std::vector<int32_t>* outReleaseFences) override;
+
+ Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+ Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+ int32_t acquireFence) override;
+ Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<hwc_rect_t>& damage) override;
+ Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+ Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+ Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+ Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+ Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
+ Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+ Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
+ Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+ Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+ Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<hwc_rect_t>& visible) override;
+ Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+ void setClient(ComposerClient* client);
+
+ void requestVSync(uint64_t vsyncTime = 0);
+ // We don't want tests hanging, so always use a timeout. Remember
+ // to always check the number of frames with test ASSERT_!
+ // Wait until next frame is rendered after requesting vsync.
+ void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
+ void runVSyncAfter(std::chrono::nanoseconds wait);
+
+ int getFrameCount() const;
+ // We don't want tests hanging, so always use a timeout. Remember
+ // to always check the number of frames with test ASSERT_!
+ void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
+ std::vector<RenderState> getFrameRects(int frame) const;
+ std::vector<RenderState> getLatestFrame() const;
+ void clearFrames();
+
+ void onSurfaceFlingerStart();
+ void onSurfaceFlingerStop();
+
+ int getLayerCount() const;
+ Layer getLayer(size_t index) const;
+
+ void hotplugDisplay(Display display, IComposerCallback::Connection state);
+
+private:
+ LayerImpl& getLayerImpl(Layer handle);
+
+ bool mCallbacksOn;
+ ComposerClient* mClient;
+ Config mCurrentConfig;
+ bool mVsyncEnabled;
+ std::vector<std::unique_ptr<LayerImpl>> mLayers;
+ std::vector<std::unique_ptr<Frame>> mFrames;
+ // Using a pointer to hide the implementation into the CPP file.
+ std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
+ android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
+ mutable android::Mutex mStateMutex;
+ mutable android::Condition mFramesAvailable;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
new file mode 100644
index 0000000..c411604
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcService"
+#include <log/log.h>
+
+#include "FakeComposerService.h"
+
+using namespace android::hardware;
+
+namespace sftest {
+
+FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+
+FakeComposerService::~FakeComposerService() {
+ ALOGI("Maybe killing client %p", mClient.get());
+ // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+ ALOGI("FakeComposerService::getCapabilities");
+ hidl_cb(hidl_vec<Capability>());
+ return Void();
+}
+
+Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+ ALOGI("FakeComposerService::dumpDebugInfo");
+ hidl_cb(hidl_string());
+ return Void();
+}
+
+Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+ ALOGI("FakeComposerService::createClient %p", mClient.get());
+ mClient->initialize();
+ hidl_cb(Error::NONE, mClient);
+ return Void();
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
new file mode 100644
index 0000000..5204084
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include "ComposerClient.h"
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using android::hardware::Return;
+
+namespace sftest {
+
+class FakeComposerService : public IComposer {
+public:
+ FakeComposerService(android::sp<ComposerClient>& client);
+ virtual ~FakeComposerService();
+
+ Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+ Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+ Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+ android::sp<ComposerClient> mClient;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
new file mode 100644
index 0000000..51956ec
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcUtil"
+#include <log/log.h>
+
+#include "FakeComposerUtils.h"
+#include "RenderState.h"
+
+#include "SurfaceFlinger.h" // Get the name of the service...
+
+#include <binder/IServiceManager.h>
+
+#include <cutils/properties.h>
+
+#include <iomanip>
+#include <thread>
+
+using android::String16;
+using android::sp;
+using namespace std::chrono_literals;
+using namespace sftest;
+using std::setw;
+
+namespace sftest {
+
+// clang-format off
+inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
+ os << std::fixed << std::setprecision(1) << "("
+ << setw(align) << sourceRect.left << setw(0) << ","
+ << setw(align) << sourceRect.top << setw(0) << ","
+ << setw(align) << sourceRect.right << setw(0) << ","
+ << setw(align) << sourceRect.bottom << setw(0) << ")";
+}
+
+inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
+ os << "("
+ << setw(align) << displayRect.left << setw(0) << ","
+ << setw(align) << displayRect.top << setw(0) << ","
+ << setw(align) << displayRect.right << setw(0) << ","
+ << setw(align) << displayRect.bottom << setw(0) << ")";
+}
+// clang-format on
+
+inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
+ printSourceRectAligned(os, state.mSourceCrop, 7);
+ os << "->";
+ printDisplayRectAligned(os, state.mDisplayFrame, 5);
+ return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
+ << state.mPlaneAlpha << " Xform:" << state.mTransform;
+}
+
+// Helper for verifying the parts of the RenderState
+template <typename T>
+bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
+ const char* name) {
+ if (ref != val) {
+ message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
+ return false;
+ }
+ return true;
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
+ // TODO: Message could start as success and be assigned as failure.
+ // Only problem is that utility assumes it to be failure and just adds stuff. Would
+ // need still special case the initial failure in the utility?
+ // TODO: ... or would it be possible to break this back to gtest primitives?
+ ::testing::AssertionResult message = ::testing::AssertionFailure();
+ bool passes = true;
+
+ // The work here is mostly about providing good log strings for differences
+ passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
+ passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
+ passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
+ passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
+ // ... add more
+ if (passes) {
+ return ::testing::AssertionSuccess();
+ }
+ return message;
+}
+
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+ const std::vector<RenderState>& val) {
+ ::testing::AssertionResult message = ::testing::AssertionFailure();
+ bool passed = true;
+ if (ref.size() != val.size()) {
+ message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
+ passed = false;
+ }
+ for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
+ ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
+ if (rectResult == false) {
+ message << "First different rect at " << rectIndex << ": " << rectResult.message();
+ passed = false;
+ break;
+ }
+ }
+
+ if (passed) {
+ return ::testing::AssertionSuccess();
+ } else {
+ message << "\nReference:";
+ for (auto state = ref.begin(); state != ref.end(); ++state) {
+ message << "\n" << *state;
+ }
+ message << "\nActual:";
+ for (auto state = val.begin(); state != val.end(); ++state) {
+ message << "\n" << *state;
+ }
+ }
+ return message;
+}
+
+void startSurfaceFlinger() {
+ ALOGI("Start SurfaceFlinger");
+ system("start surfaceflinger");
+
+ sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<android::IBinder> sf;
+ while (sf == nullptr) {
+ std::this_thread::sleep_for(10ms);
+ sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+ }
+ ALOGV("SurfaceFlinger running");
+}
+
+void stopSurfaceFlinger() {
+ ALOGI("Stop SurfaceFlinger");
+ system("stop surfaceflinger");
+ sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<android::IBinder> sf;
+ while (sf != nullptr) {
+ std::this_thread::sleep_for(10ms);
+ sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+ }
+ ALOGV("SurfaceFlinger stopped");
+}
+
+////////////////////////////////////////////////
+
+void FakeHwcEnvironment::SetUp() {
+ ALOGI("Test env setup");
+ system("setenforce 0");
+ system("stop");
+ property_set("debug.sf.nobootanimation", "1");
+ {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.nobootanimation", value, "0");
+ LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
+ }
+ // TODO: Try registering the mock as the default service instead.
+ property_set("debug.sf.hwc_service_name", "mock");
+ // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+ property_set("debug.sf.treble_testing_override", "true");
+}
+
+void FakeHwcEnvironment::TearDown() {
+ ALOGI("Test env tear down");
+ system("stop");
+ // Wait for mock call signaling teardown?
+ property_set("debug.sf.nobootanimation", "0");
+ property_set("debug.sf.hwc_service_name", "default");
+ ALOGI("Test env tear down - done");
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
new file mode 100644
index 0000000..74dc0e5
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#pragma once
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <hardware/hwcomposer_defs.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+// clang-format off
+// Note: This needs to reside in the global namespace for the GTest to use it
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
+ return os << "(" << rect.left << ","
+ << rect.top << ","
+ << rect.right << ","
+ << rect.bottom << ")";
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
+ return os << "(" << rect.left << ","
+ << rect.top << ","
+ << rect.right << ","
+ << rect.bottom << ")";
+}
+// clang-format on
+
+namespace sftest {
+
+class RenderState;
+
+// clang-format off
+inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
+ return a.top == b.top &&
+ a.left == b.left &&
+ a.bottom == b.bottom &&
+ a.right == b.right;
+}
+
+inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
+ return a.top == b.top &&
+ a.left == b.left &&
+ a.bottom == b.bottom &&
+ a.right == b.right;
+}
+// clang-format on
+
+inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
+ return !(a == b);
+}
+
+inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
+ return !(a == b);
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+ const std::vector<RenderState>& val);
+
+void startSurfaceFlinger();
+void stopSurfaceFlinger();
+
+class FakeHwcEnvironment : public ::testing::Environment {
+public:
+ virtual ~FakeHwcEnvironment() {}
+ void SetUp() override;
+ void TearDown() override;
+};
+
+/*
+ * All surface state changes are supposed to happen inside a global
+ * transaction. GlobalTransactionScope object at the beginning of
+ * scope automates the process. The resulting scope gives a visual cue
+ * on the span of the transaction as well.
+ *
+ * Closing the transaction is synchronous, i.e., it waits for
+ * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
+ * is built to explicitly request vsyncs one at the time. A delayed
+ * request must be made before closing the transaction or the test
+ * thread stalls until SurfaceFlinger does an emergency vsync by
+ * itself. GlobalTransactionScope encapsulates this vsync magic.
+ */
+class GlobalTransactionScope {
+public:
+ GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) {
+ android::SurfaceComposerClient::openGlobalTransaction();
+ }
+ ~GlobalTransactionScope() {
+ int frameCount = mComposer.getFrameCount();
+ mComposer.runVSyncAfter(1ms);
+ android::SurfaceComposerClient::closeGlobalTransaction(true);
+ // Make sure that exactly one frame has been rendered.
+ mComposer.waitUntilFrame(frameCount + 1);
+ LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+ "Unexpected frame advance. Delta: %d",
+ mComposer.getFrameCount() - frameCount);
+ }
+ FakeComposerClient& mComposer;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
new file mode 100644
index 0000000..0059289
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/RenderState.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#pragma once
+
+#include <hardware/hwcomposer2.h>
+
+#include <vector>
+
+namespace sftest {
+// Description of a rendered rectangle. Should only contain
+// instructions necessary to rasterize the rectangle. The full scene
+// is given as a sorted list of rectangles, bottom layer at index 0.
+class RenderState {
+public:
+ RenderState() = default;
+ // Default copy-ctor
+
+ hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
+ hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
+ std::vector<hwc_rect_t> mVisibleRegion;
+ hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
+ buffer_handle_t mBuffer = 0;
+ uint32_t mSwapCount = 0; // How many set buffer calls to the layer.
+ int32_t mAcquireFence = 0; // Probably should not be here.
+ float mPlaneAlpha = 0.f;
+ hwc_color_t mLayerColor = {0, 0, 0, 0};
+ hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
new file mode 100644
index 0000000..68fefea
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -0,0 +1,1306 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcTest"
+
+#include "FakeComposerClient.h"
+#include "FakeComposerService.h"
+#include "FakeComposerUtils.h"
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+
+#include <android/native_window.h>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include <hwbinder/ProcessState.h>
+
+#include <binder/ProcessState.h>
+
+#include <log/log.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <limits>
+
+using namespace std::chrono_literals;
+
+using namespace android;
+using namespace android::hardware;
+
+using namespace sftest;
+
+namespace {
+
+// Mock test helpers
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::_;
+
+///////////////////////////////////////////////
+
+struct TestColor {
+public:
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+};
+
+constexpr static TestColor RED = {195, 63, 63, 255};
+constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
+constexpr static TestColor GREEN = {63, 195, 63, 255};
+constexpr static TestColor BLUE = {63, 63, 195, 255};
+constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
+constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
+ bool unlock = true) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = sc->getSurface();
+ ASSERT_TRUE(s != nullptr);
+ ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+ uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+ pixel[0] = color.r;
+ pixel[1] = color.g;
+ pixel[2] = color.b;
+ pixel[3] = color.a;
+ }
+ }
+ if (unlock) {
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+ }
+}
+
+inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
+ RenderState res;
+ res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
+ res.mPlaneAlpha = 1.0f;
+ res.mSwapCount = 0;
+ res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
+ static_cast<float>(bottom - top)};
+ return res;
+}
+
+inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
+ unsigned int bottom) {
+ EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
+ EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
+ EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
+ EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
+ return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
+ static_cast<int>(bottom));
+}
+
+////////////////////////////////////////////////
+
+class DisplayTest : public ::testing::Test {
+public:
+ class MockComposerClient : public FakeComposerClient {
+ public:
+ MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
+ MOCK_METHOD4(getDisplayAttribute,
+ Error(Display display, Config config, IComposerClient::Attribute attribute,
+ int32_t* outValue));
+
+ // Re-routing to basic fake implementation
+ Error getDisplayAttributeFake(Display display, Config config,
+ IComposerClient::Attribute attribute, int32_t* outValue) {
+ return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
+ }
+ };
+
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ sp<IComposer> mFakeService;
+ sp<SurfaceComposerClient> mComposerClient;
+
+ MockComposerClient* mMockComposer;
+};
+
+void DisplayTest::SetUp() {
+ // TODO: The mMockComposer should be a unique_ptr, but it needs to
+ // outlive the test class. Currently ComposerClient only dies
+ // when the service is replaced. The Mock deletes itself when
+ // removeClient is called on it, which is ugly. This can be
+ // changed if HIDL ServiceManager allows removing services or
+ // ComposerClient starts taking the ownership of the contained
+ // implementation class. Moving the fake class to the HWC2
+ // interface instead of the current Composer interface might also
+ // change the situation.
+ mMockComposer = new MockComposerClient;
+ sp<ComposerClient> client = new ComposerClient(*mMockComposer);
+ mMockComposer->setClient(client.get());
+ mFakeService = new FakeComposerService(client);
+ (void)mFakeService->registerAsService("mock");
+
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::ProcessState::self()->startThreadPool();
+
+ EXPECT_CALL(*mMockComposer, getDisplayType(1, _))
+ .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+ Return(Error::NONE)));
+ // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy
+ // but encoding that here exactly.
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _))
+ .Times(5)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+ // TODO: Find out what code is generating the ID 0.
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _))
+ .Times(5)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+
+ startSurfaceFlinger();
+
+ // Fake composer wants to enable VSync injection
+ mMockComposer->onSurfaceFlingerStart();
+
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+}
+
+void DisplayTest::TearDown() {
+ mComposerClient->dispose();
+ mComposerClient = nullptr;
+
+ // Fake composer needs to release SurfaceComposerClient before the stop.
+ mMockComposer->onSurfaceFlingerStop();
+ stopSurfaceFlinger();
+
+ mFakeService = nullptr;
+ // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+ // management.
+ mMockComposer = nullptr;
+}
+
+TEST_F(DisplayTest, Hotplug) {
+ ALOGD("DisplayTest::Hotplug");
+
+ EXPECT_CALL(*mMockComposer, getDisplayType(2, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+ Return(Error::NONE)));
+ // The attribute queries will get done twice. This is for defaults
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _))
+ .Times(2 * 3)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+ // ... and then special handling for dimensions. Specifying this
+ // rules later means that gmock will try them first, i.e.,
+ // ordering of width/height vs. the default implementation for
+ // other queries is significant.
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+ // TODO: Width and height queries are not actually called. Display
+ // info returns dimensions 0x0 in display info. Why?
+
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::CONNECTED);
+
+ {
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(400u, info.w);
+ ASSERT_EQ(200u, info.h);
+
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(surfaceControl != nullptr);
+ ASSERT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ GlobalTransactionScope gts(*mMockComposer);
+ mComposerClient->setDisplayLayerStack(display, 0);
+
+ ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+ ASSERT_EQ(NO_ERROR, surfaceControl->show());
+ }
+ }
+
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::DISCONNECTED);
+
+ mMockComposer->clearFrames();
+
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::CONNECTED);
+
+ {
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(400u, info.w);
+ ASSERT_EQ(200u, info.h);
+
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(surfaceControl != nullptr);
+ ASSERT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ GlobalTransactionScope gts(*mMockComposer);
+ mComposerClient->setDisplayLayerStack(display, 0);
+
+ ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+ ASSERT_EQ(NO_ERROR, surfaceControl->show());
+ }
+ }
+ mMockComposer->hotplugDisplay(static_cast<Display>(2),
+ IComposerCallback::Connection::DISCONNECTED);
+}
+
+////////////////////////////////////////////////
+
+class TransactionTest : public ::testing::Test {
+protected:
+ // Layer array indexing constants.
+ constexpr static int BG_LAYER = 0;
+ constexpr static int FG_LAYER = 1;
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ void SetUp() override;
+ void TearDown() override;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mBGSurfaceControl;
+ sp<SurfaceControl> mFGSurfaceControl;
+ std::vector<RenderState> mBaseFrame;
+ uint32_t mDisplayWidth;
+ uint32_t mDisplayHeight;
+
+ static FakeComposerClient* sFakeComposer;
+};
+
+FakeComposerClient* TransactionTest::sFakeComposer;
+
+void TransactionTest::SetUpTestCase() {
+ // TODO: See TODO comment at DisplayTest::SetUp for background on
+ // the lifetime of the FakeComposerClient.
+ sFakeComposer = new FakeComposerClient;
+ sp<ComposerClient> client = new ComposerClient(*sFakeComposer);
+ sFakeComposer->setClient(client.get());
+ sp<IComposer> fakeService = new FakeComposerService(client);
+ (void)fakeService->registerAsService("mock");
+
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::ProcessState::self()->startThreadPool();
+
+ startSurfaceFlinger();
+
+ // Fake composer wants to enable VSync injection
+ sFakeComposer->onSurfaceFlingerStart();
+}
+
+void TransactionTest::TearDownTestCase() {
+ // Fake composer needs to release SurfaceComposerClient before the stop.
+ sFakeComposer->onSurfaceFlingerStop();
+ stopSurfaceFlinger();
+ // TODO: This is deleted when the ComposerClient calls
+ // removeClient. Devise better lifetime control.
+ sFakeComposer = nullptr;
+}
+
+void TransactionTest::SetUp() {
+ ALOGI("TransactionTest::SetUp");
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ ALOGI("TransactionTest::SetUp - display");
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+
+ mDisplayWidth = info.w;
+ mDisplayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+ mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+ // Foreground surface
+ mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mFGSurfaceControl != nullptr);
+ ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+ SurfaceComposerClient::openGlobalTransaction();
+
+ mComposerClient->setDisplayLayerStack(display, 0);
+
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2));
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1));
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+
+ // Synchronous transaction will stop this thread, so we set up a
+ // delayed, off-thread vsync request before closing the
+ // transaction. In the test code this is usually done with
+ // GlobalTransactionScope. Leaving here in the 'vanilla' form for
+ // reference.
+ ASSERT_EQ(0, sFakeComposer->getFrameCount());
+ sFakeComposer->runVSyncAfter(1ms);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ sFakeComposer->waitUntilFrame(1);
+
+ // Reference data. This is what the HWC should see.
+ static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+ mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+ mBaseFrame[BG_LAYER].mSwapCount = 1;
+ mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+ mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+ auto frame = sFakeComposer->getFrameRects(0);
+ ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+}
+
+void TransactionTest::TearDown() {
+ ALOGD("TransactionTest::TearDown");
+
+ mComposerClient->dispose();
+ mBGSurfaceControl = 0;
+ mFGSurfaceControl = 0;
+ mComposerClient = 0;
+
+ sFakeComposer->runVSyncAndWait();
+ mBaseFrame.clear();
+ sFakeComposer->clearFrames();
+ ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ std::vector<LayerDebugInfo> layers;
+ status_t result = sf->getLayerDebugInfo(&layers);
+ if (result != NO_ERROR) {
+ ALOGE("Failed to get layers %s %d", strerror(-result), result);
+ } else {
+ // If this fails, the test being torn down leaked layers.
+ EXPECT_EQ(0u, layers.size());
+ if (layers.size() > 0) {
+ for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+ std::cout << to_string(*layer).c_str();
+ }
+ // To ensure the next test has clean slate, will run the class
+ // tear down and setup here.
+ TearDownTestCase();
+ SetUpTestCase();
+ }
+ }
+ ALOGD("TransactionTest::TearDown - complete");
+}
+
+TEST_F(TransactionTest, LayerMove) {
+ ALOGD("TransactionTest::LayerMove");
+
+ // The scope opens and closes a global transaction and, at the
+ // same time, makes sure the SurfaceFlinger progresses one frame
+ // after the transaction closes. The results of the transaction
+ // should be available in the latest frame stored by the fake
+ // composer.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+ // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+ // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+ //
+ // sFakeComposer->runVSyncAndWait();
+ }
+
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+
+ ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+ // no extra frames.
+
+ // NOTE: Frame 0 is produced in the SetUp.
+ auto frame1Ref = mBaseFrame;
+ frame1Ref[FG_LAYER].mDisplayFrame =
+ hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+ EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+ auto frame2Ref = frame1Ref;
+ frame2Ref[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerResize) {
+ ALOGD("TransactionTest::LayerResize");
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128));
+ }
+
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+
+ ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+ // no extra frames.
+
+ auto frame1Ref = mBaseFrame;
+ // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
+ EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+ auto frame2Ref = frame1Ref;
+ frame2Ref[FG_LAYER].mSwapCount++;
+ frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+ frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+ EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerCrop) {
+ // TODO: Add scaling to confirm that crop happens in buffer space?
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ Rect cropRect(16, 16, 32, 32);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCrop) {
+ // TODO: Add scaling to confirm that crop happens in display space?
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ Rect cropRect(32, 32, 32 + 64, 32 + 64);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // In display space we are cropping with [32, 32, 96, 96] against display rect
+ // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCropEmpty) {
+ // TODO: Add scaling to confirm that crop happens in display space?
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ Rect cropRect(16, 16, 32, 32);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // In display space we are cropping with [16, 16, 32, 32] against display rect
+ // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
+ std::vector<RenderState> referenceFrame(1);
+ referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayer) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // The layers will switch order, but both are rendered because the background layer is
+ // transparent (RGBA8888).
+ std::vector<RenderState> referenceFrame(2);
+ referenceFrame[0] = mBaseFrame[FG_LAYER];
+ referenceFrame[1] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayerOpaque) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+ ASSERT_EQ(NO_ERROR,
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque,
+ layer_state_t::eLayerOpaque));
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // The former foreground layer is now covered with opaque layer - it should have disappeared
+ std::vector<RenderState> referenceFrame(1);
+ referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetLayerStack) {
+ ALOGD("TransactionTest::SetLayerStack");
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1));
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerShowHide) {
+ ALOGD("TransactionTest::LayerShowHide");
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide());
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+ }
+
+ // Foreground layer should be back
+ ASSERT_EQ(3, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetAlpha) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f));
+ }
+
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetFlags) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR,
+ mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden,
+ layer_state_t::eLayerHidden));
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetMatrix) {
+ struct matrixTestData {
+ float matrix[4];
+ hwc_transform_t expectedTransform;
+ hwc_rect_t expectedDisplayFrame;
+ };
+
+ // The matrix operates on the display frame and is applied before
+ // the position is added. So, the foreground layer rect is (0, 0,
+ // 64, 64) is first transformed, potentially yielding negative
+ // coordinates and then the position (64, 64) is added yielding
+ // the final on-screen rectangles given.
+
+ const matrixTestData MATRIX_TESTS[7] = // clang-format off
+ {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}},
+ {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}},
+ {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}},
+ {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}},
+ {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}},
+ {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
+ {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
+ // clang-format on
+ constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData);
+
+ for (int i = 0; i < TEST_COUNT; i++) {
+ // TODO: How to leverage the HWC2 stringifiers?
+ const matrixTestData& xform = MATRIX_TESTS[i];
+ SCOPED_TRACE(i);
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR,
+ mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1],
+ xform.matrix[2], xform.matrix[3]));
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+ referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+}
+
+#if 0
+TEST_F(TransactionTest, LayerSetMatrix2) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ // TODO: PLEASE SPEC THE FUNCTION!
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f,
+ -2.33f, 0.22f));
+ }
+ auto referenceFrame = mBaseFrame;
+ // TODO: Is this correct for sure?
+ //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+#endif
+
+TEST_F(TransactionTest, DeferredTransaction) {
+ // Synchronization surface
+ constexpr static int SYNC_LAYER = 2;
+ auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(syncSurfaceControl != nullptr);
+ ASSERT_TRUE(syncSurfaceControl->isValid());
+
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1));
+ ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2));
+ ASSERT_EQ(NO_ERROR, syncSurfaceControl->show());
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+ mDisplayWidth - 1, mDisplayHeight - 1));
+ referenceFrame[SYNC_LAYER].mSwapCount = 1;
+ EXPECT_EQ(2, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // set up two deferred transactions on different frames - these should not yield composited
+ // frames
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75));
+ mFGSurfaceControl
+ ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber());
+ }
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+ mFGSurfaceControl
+ ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+ }
+ EXPECT_EQ(4, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // should trigger the first deferred transaction, but not the second one
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+ sFakeComposer->runVSyncAndWait();
+ EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+ referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+ referenceFrame[SYNC_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // should show up immediately since it's not deferred
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0));
+ }
+ referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+ EXPECT_EQ(6, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // trigger the second deferred transaction
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+ sFakeComposer->runVSyncAndWait();
+ // TODO: Compute from layer size?
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+ referenceFrame[SYNC_LAYER].mSwapCount++;
+ EXPECT_EQ(7, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetRelativeLayer) {
+ constexpr int RELATIVE_LAYER = 2;
+ auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+ // Now we stack the surface above the foreground surface and make sure it is visible.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ relativeSurfaceControl->setPosition(64, 64);
+ relativeSurfaceControl->show();
+ relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1);
+ }
+ auto referenceFrame = mBaseFrame;
+ // NOTE: All three layers will be visible as the surfaces are
+ // transparent because of the RGBA format.
+ referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+ referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // A call to setLayer will override a call to setRelativeLayer
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ relativeSurfaceControl->setLayer(0);
+ }
+
+ // Previous top layer will now appear at the bottom.
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+ EXPECT_EQ(3, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+class ChildLayerTest : public TransactionTest {
+protected:
+ constexpr static int CHILD_LAYER = 2;
+
+ void SetUp() override {
+ TransactionTest::SetUp();
+ mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(mChild, LIGHT_GRAY);
+
+ sFakeComposer->runVSyncAndWait();
+ mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+ mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+ }
+ void TearDown() override {
+ mChild = 0;
+ TransactionTest::TearDown();
+ }
+
+ sp<SurfaceControl> mChild;
+};
+
+TEST_F(ChildLayerTest, Positioning) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(10, 10);
+ // Move to the same position as in the original setup.
+ mFGSurfaceControl->setPosition(64, 64);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0));
+ }
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Cropping) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5));
+ }
+ // NOTE: The foreground surface would be occluded by the child
+ // now, but is included in the stack because the child is
+ // transparent.
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, FinalCropping) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5));
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Constraints) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mFGSurfaceControl->setPosition(0, 0);
+ mChild->setPosition(63, 63);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Scaling) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setPosition(0, 0);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0);
+ }
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, LayerAlpha) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5));
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5));
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
+ referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle());
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, DetachChildren) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->detachChildren();
+ }
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->hide();
+ }
+
+ // Nothing should have changed. The child control becomes a no-op
+ // zombie on detach. See comments for detachChildren in the
+ // SurfaceControl.h file.
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ }
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // We cause scaling by 2.
+ mFGSurfaceControl->setSize(128, 128);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->show();
+ mChild->setPosition(0, 0);
+ mFGSurfaceControl->setPosition(0, 0);
+ }
+
+ // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+ // the WM specified state size.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 64);
+ }
+
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 64, 128);
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+ sFakeComposer->runVSyncAndWait();
+
+ // The child should still be in the same place and not have any strange scaling as in
+ // b/37673612.
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+ referenceFrame[FG_LAYER].mSwapCount++;
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+ // Destroy the child layer
+ mChild.clear();
+
+ // Now recreate it as hidden
+ mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
+ mFGSurfaceControl.get());
+
+ // Show the child layer in a deferred transaction
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber());
+ mChild->show();
+ }
+
+ // Render the foreground surface a few times
+ //
+ // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+ // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+ // never acquire/release the first buffer
+ ALOGI("Filling 1");
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 2");
+ fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
+ sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 3");
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+ sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 4");
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+}
+
+class LatchingTest : public TransactionTest {
+protected:
+ void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+
+ void unlockFGBuffer() {
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+ sFakeComposer->runVSyncAndWait();
+ }
+
+ void completeFGResize() {
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+ sFakeComposer->runVSyncAndWait();
+ }
+ void restoreInitialState() {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(64, 64);
+ mFGSurfaceControl->setPosition(64, 64);
+ mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64));
+ mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+ }
+};
+
+TEST_F(LatchingTest, SurfacePositionLatching) {
+ // By default position can be updated even while
+ // a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(32, 32);
+ mFGSurfaceControl->setPosition(100, 100);
+ }
+
+ // The size should not have updated as we have not provided a new buffer.
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ // Now we repeat with setGeometryAppliesWithResize
+ // and verify the position DOESN'T latch.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setSize(32, 32);
+ mFGSurfaceControl->setPosition(100, 100);
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+ referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, CropLatching) {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+ }
+
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+ referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+ referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatching) {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame1[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame2[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+// In this test we ensure that setGeometryAppliesWithResize actually demands
+// a buffer of the new size, and not just any size.
+TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+
+ auto referenceFrame1 = mBaseFrame;
+ referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame1[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
+ // initiating the resize.
+ lockAndFillFGBuffer();
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ // We now submit our old buffer, at the old size, and ensure it doesn't
+ // trigger geometry latching.
+ unlockFGBuffer();
+
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+ auto referenceFrame3 = referenceFrame2;
+ referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+ referenceFrame3[FG_LAYER].mSourceCrop =
+ hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+ referenceFrame3[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
+ // In this scenario, we attempt to set the final crop a second time while the resize
+ // is still pending, and ensure we are successful. Success meaning the second crop
+ // is the one which eventually latches and not the first.
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setSize(128, 128);
+ mFGSurfaceControl->setGeometryAppliesWithResize();
+ mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+ }
+
+ {
+ GlobalTransactionScope gts(*sFakeComposer);
+ mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+ }
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+ completeFGResize();
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+ ::testing::AddGlobalTestEnvironment(fakeEnvironment);
+ ::testing::InitGoogleMock(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
index 062485e..4055527 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -382,7 +382,9 @@
if (outErr) {
*outErr = err;
} else {
- ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position";
+ ASSERT_TRUE((err == HWC2_ERROR_NONE) ||
+ (err == HWC2_ERROR_BAD_LAYER)) <<
+ "failed to set cursor position";
}
}
@@ -652,7 +654,7 @@
hwc2_layer_request_t request = requests.at(i);
EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer),
- 0) << "get display requests returned an unknown layer";
+ 1) << "get display requests returned an unknown layer";
EXPECT_NE(request, 0) << "returned empty request for layer "
<< requestedLayer;
@@ -1603,9 +1605,10 @@
EXPECT_EQ(layers.size(), fences.size());
for (int32_t fence : fences) {
- EXPECT_GE(sync_wait(fence, msWait), 0);
- if (fence >= 0)
+ if (fence >= 0) {
+ EXPECT_GE(sync_wait(fence, msWait), 0);
close(fence);
+ }
}
}
@@ -1643,8 +1646,9 @@
testLayers->getBlendMode(layer)));
EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer,
testLayers->getColor(layer)));
- EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left,
- cursor.top));
+ if (composition == HWC2_COMPOSITION_CURSOR)
+ EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer,
+ cursor.left, cursor.top));
EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer,
testLayers->getDataspace(layer)));
EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer,
@@ -2895,7 +2899,6 @@
ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
[] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
cursorPosition.left, cursorPosition.top, outErr));
@@ -4406,11 +4409,11 @@
/* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */
TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter)
{
- hwc2_display_t display = HWC_DISPLAY_PRIMARY;
hwc2_error_t err = HWC2_ERROR_NONE;
-
- ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
- EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+ for (auto display : mDisplays) {
+ ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+ EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+ }
}
/* TESTCASE: Tests that the HWC2 can get the max virtual display count. */
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 9201520..e92b8d8 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -44,6 +44,13 @@
cflags: [
"-DLOG_TAG=\"vr_hwc\"",
+ "-Wall",
+ "-Werror",
+ // mVrClient unused in vr_composer_client.cpp
+ "-Wno-error=unused-private-field",
+ // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
+ "-Wno-sign-compare",
+ "-Wno-unused-parameter",
],
}
@@ -63,6 +70,11 @@
},
export_include_dirs: ["aidl"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
shared_libs: [
"libbinder",
"libui",
@@ -92,6 +104,8 @@
],
cflags: [
"-DLOG_TAG=\"vr_hwc\"",
+ "-Wall",
+ "-Werror",
],
}
@@ -120,6 +134,8 @@
],
cflags: [
"-DLOG_TAG=\"vr_hwc\"",
+ "-Wall",
+ "-Werror",
],
init_rc: [
"vr_hwc.rc",
@@ -137,6 +153,13 @@
// symbols in the *-binder library get optimized out.
"libvr_hwc-binder",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ // warnings in vr_composer_test.cpp to be fixed after merge of goog/master
+ "-Wno-sign-compare",
+ "-Wno-unused-parameter",
+ ],
shared_libs: [
"libbase",
"libbinder",
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 5e32af0..d5664d5 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -232,7 +232,7 @@
VrHwc::~VrHwc() {}
-bool VrHwc::hasCapability(Capability capability) const { return false; }
+bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
void VrHwc::removeClient() {
std::lock_guard<std::mutex> guard(mutex_);
@@ -306,13 +306,15 @@
return Error::NONE;
}
-Error VrHwc::getClientTargetSupport(Display display, uint32_t width,
- uint32_t height, PixelFormat format,
- Dataspace dataspace) {
+Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */,
+ uint32_t /* height */,
+ PixelFormat /* format */,
+ Dataspace /* dataspace */) {
return Error::NONE;
}
-Error VrHwc::getColorModes(Display display, hidl_vec<ColorMode>* outModes) {
+Error VrHwc::getColorModes(Display /* display */,
+ hidl_vec<ColorMode>* outModes) {
std::vector<ColorMode> color_modes(1, ColorMode::NATIVE);
*outModes = hidl_vec<ColorMode>(color_modes);
return Error::NONE;
@@ -379,7 +381,7 @@
return Error::NONE;
}
-Error VrHwc::getDisplayName(Display display, hidl_string* outName) {
+Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
*outName = hidl_string();
return Error::NONE;
}
@@ -409,7 +411,8 @@
return Error::NONE;
}
-Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
+Error VrHwc::getHdrCapabilities(Display /* display */,
+ hidl_vec<Hdr>* /* outTypes */,
float* outMaxLuminance,
float* outMaxAverageLuminance,
float* outMinLuminance) {
@@ -473,8 +476,8 @@
}
Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
- int32_t acquireFence, int32_t dataspace,
- const std::vector<hwc_rect_t>& damage) {
+ int32_t acquireFence, int32_t /* dataspace */,
+ const std::vector<hwc_rect_t>& /* damage */) {
base::unique_fd fence(acquireFence);
std::lock_guard<std::mutex> guard(mutex_);
auto display_ptr = FindDisplay(display);
@@ -490,7 +493,7 @@
return Error::NONE;
}
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer,
+Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
int32_t releaseFence) {
base::unique_fd fence(releaseFence);
std::lock_guard<std::mutex> guard(mutex_);
@@ -505,8 +508,9 @@
Error VrHwc::validateDisplay(
Display display, std::vector<Layer>* outChangedLayers,
std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks) {
+ uint32_t* /* outDisplayRequestMask */,
+ std::vector<Layer>* /* outRequestedLayers */,
+ std::vector<uint32_t>* /* outRequestMasks */) {
std::lock_guard<std::mutex> guard(mutex_);
auto display_ptr = FindDisplay(display);
if (!display_ptr)
@@ -517,7 +521,7 @@
return Error::NONE;
}
-Error VrHwc::acceptDisplayChanges(Display display) { return Error::NONE; }
+Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
std::vector<Layer>* outLayers,
@@ -709,8 +713,8 @@
return Error::NONE;
}
-Error VrHwc::setLayerSidebandStream(Display display, Layer layer,
- buffer_handle_t stream) {
+Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
+ buffer_handle_t /* stream */) {
std::lock_guard<std::mutex> guard(mutex_);
if (!FindDisplay(display))
return Error::BAD_DISPLAY;
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index fce9a06..eff721b 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -196,8 +196,6 @@
VrHwc();
~VrHwc() override;
- bool hasCapability(Capability capability) const;
-
Error setLayerInfo(Display display, Layer layer, uint32_t type,
uint32_t appId);
Error setClientTargetMetadata(
@@ -207,6 +205,8 @@
const IVrComposerClient::BufferMetadata& metadata);
// ComposerBase
+ bool hasCapability(hwc2_capability_t capability) override;
+
void removeClient() override;
void enableCallback(bool enable) override;
diff --git a/services/vr/performanced/Android.mk b/services/vr/performanced/Android.mk
index dbc66f1..1470234 100644
--- a/services/vr/performanced/Android.mk
+++ b/services/vr/performanced/Android.mk
@@ -36,6 +36,7 @@
LOCAL_SRC_FILES := $(sourceFiles)
LOCAL_CFLAGS := -DLOG_TAG=\"performanced\"
LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -Wall -Werror
LOCAL_STATIC_LIBRARIES := $(staticLibraries)
LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
LOCAL_MODULE := performanced
diff --git a/services/vr/performanced/directory_reader.h b/services/vr/performanced/directory_reader.h
index 7d7ecc5..b9d27c3 100644
--- a/services/vr/performanced/directory_reader.h
+++ b/services/vr/performanced/directory_reader.h
@@ -19,7 +19,7 @@
directory_ = fdopendir(directory_fd.get());
error_ = errno;
if (directory_ != nullptr)
- directory_fd.release();
+ (void) directory_fd.release(); // ignore return result?
}
~DirectoryReader() {
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
index 6b11ce3..7196b2b 100644
--- a/services/vr/virtual_touchpad/Android.bp
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -23,7 +23,11 @@
shared_libs: shared_libs,
header_libs: header_libraries,
cppflags: ["-std=c++11"],
- cflags: ["-DLOG_TAG=\"VrVirtualTouchpad\""],
+ cflags: [
+ "-DLOG_TAG=\"VrVirtualTouchpad\"",
+ "-Wall",
+ "-Werror",
+ ],
name: "libvirtualtouchpad",
tags: ["optional"],
}
@@ -44,6 +48,10 @@
srcs: test_src_files,
static_libs: test_static_libs,
header_libs: header_libraries,
+ cflags = [
+ "-Wall",
+ "-Werror",
+ ],
cppflags = [
"-std=c++11",
],
@@ -83,6 +91,8 @@
cppflags: ["-std=c++11"],
cflags: [
"-DLOG_TAG=\"VrVirtualTouchpad\"",
+ "-Wall",
+ "-Werror",
],
host_ldlibs: ["-llog"],
name: "virtual_touchpad",
@@ -112,7 +122,11 @@
shared_libs: client_shared_libs,
header_libs: header_libraries,
cppflags: ["-std=c++11"],
- cflags: ["-DLOG_TAG=\"VirtualTouchpadClient\""],
+ cflags: [
+ "-DLOG_TAG=\"VirtualTouchpadClient\"",
+ "-Wall",
+ "-Werror",
+ ],
host_ldlibs: ["-llog"],
name: "libvirtualtouchpadclient",
tags: ["optional"],
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index b19b018..198ed06 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -110,17 +110,6 @@
EvdevInjectorForTesting injector[kTouchpads];
};
-void DumpDifference(const char* expect, const char* actual) {
- printf(" common: ");
- while (*expect && *expect == *actual) {
- putchar(*expect);
- ++expect;
- ++actual;
- }
- printf("\n expect: %s\n", expect);
- printf(" actual: %s\n", actual);
-}
-
} // anonymous namespace
class VirtualTouchpadTest : public testing::Test {};
@@ -153,7 +142,6 @@
}
const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
- const int32_t slots = uidev->absmax[ABS_MT_SLOT];
for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
SCOPED_TRACE(t);
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 3a2b1ef..2981a95 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
// API version (major.minor.patch)
define VERSION_MAJOR 1
define VERSION_MINOR 0
-define VERSION_PATCH 54
+define VERSION_PATCH 61
// API limits
define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -102,6 +102,10 @@
@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION 1
@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_NAME "VK_NV_glsl_shader"
+// 14
+@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1
+@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_NAME "VK_EXT_depth_range_unrestricted"
+
// 15
@extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1
@extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_NAME "VK_KHR_sampler_mirror_clamp_to_edge"
@@ -183,7 +187,7 @@
@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
// 61
-@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1
+@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 2
@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
// 62
@@ -267,7 +271,7 @@
@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
// 87
-@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3
@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
// 88
@@ -319,7 +323,7 @@
@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
// 105
-@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 2
+@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 3
@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
// 106
@@ -346,6 +350,10 @@
@extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1
@extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd"
+// 118
+@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_SPEC_VERSION 1
+@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2"
+
// 120
@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
@@ -363,7 +371,7 @@
@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
// 128
-@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 1
+@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3
@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation"
// 131
@@ -378,10 +386,34 @@
@extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 1
@extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16"
+// 137
+@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1
+@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples"
+
+// 138
+@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1
+@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask"
+
+// 141
+@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1
+@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export"
+
+// 144
+@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1
+@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations"
+
+// 145
+@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1
+@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout"
+
// 147
@extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_SPEC_VERSION 1
@extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_EXTENSION_NAME "VK_KHR_get_memory_requirements2"
+// 148
+@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1
+@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list"
+
// 149
@extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2
@extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced"
@@ -398,6 +430,26 @@
@extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_SPEC_VERSION 1
@extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle"
+// 156
+@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1
+@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage"
+
+// 157
+@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1
+@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion"
+
+// 158
+@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_SPEC_VERSION 1
+@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_EXTENSION_NAME "VK_KHR_bind_memory2"
+
+// 161
+@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1
+@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache"
+
+// 165
+@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1
+@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer"
+
/////////////
// Types //
/////////////
@@ -456,6 +508,11 @@
@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
+// 157
+@extension("VK_KHR_sampler_ycbcr_conversion") @nonDispatchHandle type u64 VkSamplerYcbcrConversionKHR
+
+// 161
+@extension("VK_EXT_validation_cache") @nonDispatchHandle type u64 VkValidationCacheEXT
/////////////
// Enums //
@@ -477,6 +534,10 @@
//@extension("VK_KHR_shared_presentable_image") // 112
VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000,
+
+ //@extension("VK_KHR_maintenance2") // 118
+ VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = 1000117000,
+ VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = 1000117001,
}
enum VkAttachmentLoadOp {
@@ -960,6 +1021,42 @@
VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_FORMAT_G8B8G8R8_422_UNORM_KHR = 1000156000,
+ VK_FORMAT_B8G8R8G8_422_UNORM_KHR = 1000156001,
+ VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = 1000156002,
+ VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = 1000156003,
+ VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = 1000156004,
+ VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = 1000156005,
+ VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = 1000156006,
+ VK_FORMAT_R10X6_UNORM_PACK16_KHR = 1000156007,
+ VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = 1000156008,
+ VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = 1000156009,
+ VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = 1000156010,
+ VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = 1000156011,
+ VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = 1000156012,
+ VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = 1000156013,
+ VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = 1000156014,
+ VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = 1000156015,
+ VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = 1000156016,
+ VK_FORMAT_R12X4_UNORM_PACK16_KHR = 1000156017,
+ VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = 1000156018,
+ VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = 1000156019,
+ VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = 1000156020,
+ VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = 1000156021,
+ VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = 1000156022,
+ VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = 1000156023,
+ VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = 1000156024,
+ VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = 1000156025,
+ VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = 1000156026,
+ VK_FORMAT_G16B16G16R16_422_UNORM_KHR = 1000156027,
+ VK_FORMAT_B16G16R16G16_422_UNORM_KHR = 1000156028,
+ VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = 1000156029,
+ VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = 1000156030,
+ VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = 1000156031,
+ VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = 1000156032,
+ VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = 1000156033,
}
/// Structure type enumerant
@@ -1096,8 +1193,6 @@
//@extension("VK_KHX_device_group") // 61
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000,
- VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001,
- VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002,
VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003,
VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005,
@@ -1108,6 +1203,8 @@
VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011,
VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012,
+ VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060013,
+ VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060014,
//@extension("VK_EXT_validation_flags") // 62
VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
@@ -1183,7 +1280,7 @@
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000,
//@extension("VK_EXT_display_surface_counter") // 91
- VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000,
+ VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000,
//@extension("VK_EXT_display_control") // 92
VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000,
@@ -1226,6 +1323,12 @@
VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000,
VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001,
+ //@extension("VK_KHR_maintenance2") // 118
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = 1000117000,
+ VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = 1000117001,
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = 1000117002,
+ VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003,
+
//@extension("VK_KHR_get_surface_capabilities2") // 120
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000,
VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001,
@@ -1248,6 +1351,13 @@
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000,
VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001,
+ //@extension("VK_EXT_sample_locations") // 144
+ VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000,
+ VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001,
+ VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003,
+ VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004,
+
//@extension("VK_KHR_get_memory_requirements2") // 147
VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146000,
VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146001,
@@ -1255,6 +1365,9 @@
VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = 1000146003,
VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = 1000146004,
+ //@extension("VK_KHR_image_format_list") // 148
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000,
+
//@extension("VK_EXT_blend_operation_advanced") // 149
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001,
@@ -1263,8 +1376,24 @@
//@extension("VK_NV_fragment_coverage_to_color") // 150
VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000,
- //@structure("VK_NV_framebuffer_mixed_samples") // 153
+ //@extension("VK_NV_framebuffer_mixed_samples") // 153
VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = 1000156000,
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = 1000156001,
+ VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = 1000156002,
+ VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = 1000156003,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004,
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = 1000156005,
+
+ //@extension("VK_KHR_bind_memory2") // 158
+ VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = 1000157000,
+ VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = 1000157001,
+
+ //@extension("VK_EXT_validation_cache") // 161
+ VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000,
+ VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001,
}
enum VkSubpassContents {
@@ -1343,6 +1472,9 @@
//@extension("VK_EXT_discard_rectangles") // 100
VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000,
+
+ //@extension("VK_EXT_sample_locations") // 144
+ VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000,
}
enum VkObjectType {
@@ -1392,6 +1524,12 @@
//@extension("VK_NVX_device_generated_commands") // 87
VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000,
VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = 1000156000,
+
+ //@extension("VK_EXT_validation_cache") // 161
+ VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
}
@extension("VK_KHR_surface") // 1
@@ -1463,8 +1601,14 @@
VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
+ //extension("VK_EXT_validation_cache") // 161
+ VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = 33,
+
//extension("VK_KHR_descriptor_update_template") // 86
VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000,
}
@extension("VK_AMD_rasterization_order") // 19
@@ -1541,6 +1685,18 @@
VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
}
+@extension("VK_KHR_maintenance2") // 118
+enum VkPointClippingBehaviorKHR {
+ VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = 0,
+ VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = 1,
+}
+
+@extension("VK_KHR_maintenance2") // 118
+enum VkTessellationDomainOriginKHR {
+ VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = 0,
+ VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = 1,
+}
+
@extension("VK_EXT_sampler_filter_minmax") // 131
enum VkSamplerReductionModeEXT {
VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT = 0,
@@ -1563,6 +1719,32 @@
VK_COVERAGE_MODULATION_MODE_RGBA_NV = 3,
}
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+enum VkSamplerYcbcrModelConversionKHR {
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = 0,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = 1,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = 2,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = 3,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = 4,
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+enum VkSamplerYcbcrRangeKHR {
+ VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = 0,
+ VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = 1,
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+enum VkChromaLocationKHR {
+ VK_CHROMA_LOCATION_COSITED_EVEN_KHR = 0,
+ VK_CHROMA_LOCATION_MIDPOINT_KHR = 1,
+}
+
+@extension("VK_EXT_validation_cache") // 161
+enum VkValidationCacheHeaderVersionEXT {
+ VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1,
+}
+
/////////////////
// Bitfields //
/////////////////
@@ -1698,6 +1880,19 @@
//@extension("VK_KHX_device_group") // 61
VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040,
+
+ //@extension("VK_EXT_sample_locations") // 144
+ VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000,
+
+ //@extension("VK_KHR_maintenance2") // 118
+ VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080,
+ VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = 0x00000100,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_IMAGE_CREATE_DISJOINT_BIT_KHR = 0x00000200,
+
+ //@extension("VK_KHR_bind_memory2") // 158
+ VK_IMAGE_CREATE_ALIAS_BIT_KHR = 0x00000400,
}
/// Image view creation flags
@@ -1763,6 +1958,15 @@
//@extension("VK_EXT_sampler_filter_minmax") // 131
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000,
+ VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = 0x00400000,
+ VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000,
}
/// Query control flags
@@ -1826,6 +2030,11 @@
VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002,
VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004,
VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008,
+
+ //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+ VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = 0x00000010,
+ VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = 0x00000020,
+ VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = 0x00000040,
}
/// Sparse memory bind flags
@@ -2353,6 +2562,12 @@
//bitfield VkPipelineCoverageModulationStateCreateFlagBitsNV {
//}
+@extension("VK_EXT_validation_cache") // 161
+type VkFlags VkValidationCacheCreateFlagsEXT
+@extension("VK_EXT_validation_cache") // 161
+//bitfield VkValidationCacheCreateFlagBitsEXT {
+//}
+
//////////////////
// Structures //
//////////////////
@@ -3901,23 +4116,17 @@
}
@extension("VK_KHX_device_group") // 61
-class VkBindBufferMemoryInfoKHX {
+class VkBindBufferMemoryDeviceGroupInfoKHX {
VkStructureType sType
const void* pNext
- VkBuffer buffer
- VkDeviceMemory memory
- VkDeviceSize memoryOffset
u32 deviceIndexCount
const u32* pDeviceIndices
}
@extension("VK_KHX_device_group") // 61
-class VkBindImageMemoryInfoKHX {
+class VkBindImageMemoryDeviceGroupInfoKHX {
VkStructureType sType
const void* pNext
- VkImage image
- VkDeviceMemory memory
- VkDeviceSize memoryOffset
u32 deviceIndexCount
const u32* pDeviceIndices
u32 SFRRectCount
@@ -4674,6 +4883,42 @@
VkExternalFenceHandleTypeFlagBitsKHR handleType
}
+@extension("VK_KHR_maintenance2") // 118
+class VkPhysicalDevicePointClippingPropertiesKHR {
+ VkStructureType sType
+ void* pNext
+ VkPointClippingBehaviorKHR pointClippingBehavior
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkInputAttachmentAspectReferenceKHR {
+ u32 subpass
+ u32 inputAttachmentIndex
+ VkImageAspectFlags aspectMask
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkRenderPassInputAttachmentAspectCreateInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ u32 aspectReferenceCount
+ const VkInputAttachmentAspectReferenceKHR* pAspectReferences
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkImageViewUsageCreateInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkImageUsageFlags usage
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkPipelineTessellationDomainOriginStateCreateInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkTessellationDomainOriginKHR domainOrigin
+}
+
@extension("VK_KHR_get_surface_capabilities2") // 120
class VkPhysicalDeviceSurfaceInfo2KHR {
VkStructureType sType
@@ -4750,6 +4995,70 @@
VkBool32 filterMinmaxImageComponentMapping
}
+@extension("VK_EXT_sample_locations") // 144
+class VkSampleLocationEXT {
+ f32 x
+ f32 y
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkSampleLocationsInfoEXT {
+ VkStructureType sType
+ const void* pNext
+ VkSampleCountFlagBits sampleLocationsPerPixel
+ VkExtent2D sampleLocationGridSize
+ u32 sampleLocationsCount
+ const VkSampleLocationEXT* pSampleLocations
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkAttachmentSampleLocationsEXT {
+ u32 attachmentIndex
+ VkSampleLocationsInfoEXT sampleLocationsInfo
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkSubpassSampleLocationsEXT {
+ u32 subpassIndex
+ VkSampleLocationsInfoEXT sampleLocationsInfo
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkRenderPassSampleLocationsBeginInfoEXT {
+ VkStructureType sType
+ const void* pNext
+ u32 attachmentInitialSampleLocationsCount
+ const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations
+ u32 postSubpassSampleLocationsCount
+ const VkSubpassSampleLocationsEXT* pSubpassSampleLocations
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkPipelineSampleLocationsStateCreateInfoEXT {
+ VkStructureType sType
+ const void* pNext
+ VkBool32 sampleLocationsEnable
+ VkSampleLocationsInfoEXT sampleLocationsInfo
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkPhysicalDeviceSampleLocationsPropertiesEXT {
+ VkStructureType sType
+ void* pNext
+ VkSampleCountFlags sampleLocationSampleCounts
+ VkExtent2D maxSampleLocationGridSize
+ f32[2] sampleLocationCoordinateRange
+ u32 sampleLocationSubPixelBits
+ VkBool32 variableSampleLocations
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkMultisamplePropertiesEXT {
+ VkStructureType sType
+ void* pNext
+ VkExtent2D maxSampleLocationGridSize
+}
+
@extension("VK_KHR_get_memory_requirements2") // 147
class VkBufferMemoryRequirementsInfo2KHR {
VkStructureType sType
@@ -4785,6 +5094,14 @@
VkSparseImageMemoryRequirements memoryRequirements
}
+@extension("VK_KHR_image_format_list") // 148
+class VkImageFormatListCreateInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ u32 viewFormatCount
+ const VkFormat* pViewFormats
+}
+
@extension("VK_EXT_blend_operation_advanced") // 149
class VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT {
VkStructureType sType
@@ -4833,6 +5150,89 @@
const f32* pCoverageModulationTable
}
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkSamplerYcbcrConversionCreateInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkFormat format
+ VkSamplerYcbcrModelConversionKHR ycbcrModel
+ VkSamplerYcbcrRangeKHR ycbcrRange
+ VkComponentMapping components
+ VkChromaLocationKHR xChromaOffset
+ VkChromaLocationKHR yChromaOffset
+ VkFilter chromaFilter
+ VkBool32 forceExplicitReconstruction
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkSamplerYcbcrConversionInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkSamplerYcbcrConversionKHR conversion
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkBindImagePlaneMemoryInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkImageAspectFlagBits planeAspect
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkImagePlaneMemoryRequirementsInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkImageAspectFlagBits planeAspect
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR {
+ VkStructureType sType
+ void* pNext
+ VkBool32 samplerYcbcrConversion
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkSamplerYcbcrConversionImageFormatPropertiesKHR {
+ VkStructureType sType
+ void* pNext
+ u32 combinedImageSamplerDescriptorCount
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+class VkBindBufferMemoryInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkBuffer buffer
+ VkDeviceMemory memory
+ VkDeviceSize memoryOffset
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+class VkBindImageMemoryInfoKHR {
+ VkStructureType sType
+ const void* pNext
+ VkImage image
+ VkDeviceMemory memory
+ VkDeviceSize memoryOffset
+}
+
+@extension("VK_EXT_validation_cache") // 161
+class VkValidationCacheCreateInfoEXT {
+ VkStructureType sType
+ const void* pNext
+ VkValidationCacheCreateFlagsEXT flags
+ platform.size_t initialDataSize
+ const void* pInitialData
+}
+
+@extension("VK_EXT_validation_cache") // 161
+class VkShaderModuleValidationCacheCreateInfoEXT {
+ VkStructureType sType
+ const void* pNext
+ VkValidationCacheEXT validationCache
+}
+
////////////////
// Commands //
////////////////
@@ -7454,21 +7854,21 @@
@extension("VK_EXT_debug_marker") // 23
cmd VkResult vkDebugMarkerSetObjectTagEXT(
VkDevice device,
- VkDebugMarkerObjectTagInfoEXT* pTagInfo) {
+ const VkDebugMarkerObjectTagInfoEXT* pTagInfo) {
return ?
}
@extension("VK_EXT_debug_marker") // 23
cmd VkResult vkDebugMarkerSetObjectNameEXT(
VkDevice device,
- VkDebugMarkerObjectNameInfoEXT* pNameInfo) {
+ const VkDebugMarkerObjectNameInfoEXT* pNameInfo) {
return ?
}
@extension("VK_EXT_debug_marker") // 23
cmd void vkCmdDebugMarkerBeginEXT(
VkCommandBuffer commandBuffer,
- VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
+ const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
}
@extension("VK_EXT_debug_marker") // 23
@@ -7479,7 +7879,7 @@
@extension("VK_EXT_debug_marker") // 23
cmd void vkCmdDebugMarkerInsertEXT(
VkCommandBuffer commandBuffer,
- VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
+ const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
}
@extension("VK_AMD_draw_indirect_count") // 34
@@ -7584,22 +7984,6 @@
}
@extension("VK_KHX_device_group") // 61
-cmd VkResult vkBindBufferMemory2KHX(
- VkDevice device,
- u32 bindInfoCount,
- const VkBindBufferMemoryInfoKHX* pBindInfos) {
- return ?
-}
-
-@extension("VK_KHX_device_group") // 61
-cmd VkResult vkBindImageMemory2KHX(
- VkDevice device,
- u32 bindInfoCount,
- const VkBindImageMemoryInfoKHX* pBindInfos) {
- return ?
-}
-
-@extension("VK_KHX_device_group") // 61
cmd void vkCmdSetDeviceMaskKHX(
VkCommandBuffer commandBuffer,
u32 deviceMask) {
@@ -8058,6 +8442,19 @@
return ?
}
+@extension("VK_EXT_sample_locations") // 144
+cmd void vkCmdSetSampleLocationsEXT(
+ VkCommandBuffer commandBuffer,
+ const VkSampleLocationsInfoEXT* pSampleLocationsInfo) {
+}
+
+@extension("VK_EXT_sample_locations") // 144
+cmd void vkGetPhysicalDeviceMultisamplePropertiesEXT(
+ VkPhysicalDevice physicalDevice,
+ VkSampleCountFlagBits samples,
+ VkMultisamplePropertiesEXT* pMultisampleProperties) {
+}
+
@extension("VK_KHR_get_memory_requirements2") // 147
cmd void vkGetImageMemoryRequirements2KHR(
VkDevice device,
@@ -8080,6 +8477,72 @@
VkSparseImageMemoryRequirements2KHR* pSparseMemoryRequirements) {
}
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+cmd VkResult vkCreateSamplerYcbcrConversionKHR(
+ VkDevice device,
+ const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkSamplerYcbcrConversionKHR* pYcbcrConversion) {
+ return ?
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+cmd void vkDestroySamplerYcbcrConversionKHR(
+ VkDevice device,
+ VkSamplerYcbcrConversionKHR ycbcrConversion,
+ const VkAllocationCallbacks* pAllocator) {
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+cmd VkResult vkBindBufferMemory2KHR(
+ VkDevice device,
+ u32 bindInfoCount,
+ const VkBindBufferMemoryInfoKHR* pBindInfos) {
+ return ?
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+cmd VkResult vkBindImageMemory2KHR(
+ VkDevice device,
+ u32 bindInfoCount,
+ const VkBindImageMemoryInfoKHR* pBindInfos) {
+ return ?
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd VkResult vkCreateValidationCacheEXT(
+ VkDevice device,
+ const VkValidationCacheCreateInfoEXT* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkValidationCacheEXT* pValidationCache) {
+ return ?
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd void vkDestroyValidationCacheEXT(
+ VkDevice device,
+ VkValidationCacheEXT validationCache,
+ const VkAllocationCallbacks* pAllocator) {
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd VkResult vkMergeValidationCachesEXT(
+ VkDevice device,
+ VkValidationCacheEXT dstCache,
+ u32 srcCacheCount,
+ const VkValidationCacheEXT* pSrcCaches) {
+ return ?
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd VkResult vkGetValidationCacheDataEXT(
+ VkDevice device,
+ VkValidationCacheEXT validationCache,
+ platform.size_t* pDataSize,
+ void* pData) {
+ return ?
+}
+
////////////////
// Validation //
////////////////
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 0271d38..7813e4b 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -34,16 +34,16 @@
(((major) << 22) | ((minor) << 12) | (patch))
// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead.
-//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0)
+//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0
// Vulkan 1.0 version number
-#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)
+#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)// Patch version should always be set to 0
#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
// Version of this file
-#define VK_HEADER_VERSION 54
+#define VK_HEADER_VERSION 61
#define VK_NULL_HANDLE 0
@@ -241,16 +241,16 @@
VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000,
- VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001,
- VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002,
VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003,
VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005,
VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006,
+ VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
+ VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060013,
+ VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060014,
VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007,
VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008,
VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009,
- VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011,
VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012,
VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
@@ -293,7 +293,7 @@
VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000,
- VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000,
+ VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000,
VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000,
VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001,
VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002,
@@ -313,6 +313,10 @@
VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002,
VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000,
VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = 1000117000,
+ VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = 1000117001,
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = 1000117002,
+ VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000,
VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001,
VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002,
@@ -323,16 +327,32 @@
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = 1000127001,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000,
VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001,
+ VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000,
+ VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001,
+ VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003,
+ VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004,
VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146000,
VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146001,
VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146002,
VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = 1000146003,
VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = 1000146004,
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001,
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002,
VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000,
VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000,
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = 1000156000,
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = 1000156001,
+ VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = 1000156002,
+ VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = 1000156003,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004,
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = 1000156005,
+ VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = 1000157000,
+ VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = 1000157001,
+ VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000,
+ VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001,
VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -553,6 +573,40 @@
VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
+ VK_FORMAT_G8B8G8R8_422_UNORM_KHR = 1000156000,
+ VK_FORMAT_B8G8R8G8_422_UNORM_KHR = 1000156001,
+ VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = 1000156002,
+ VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = 1000156003,
+ VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = 1000156004,
+ VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = 1000156005,
+ VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = 1000156006,
+ VK_FORMAT_R10X6_UNORM_PACK16_KHR = 1000156007,
+ VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = 1000156008,
+ VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = 1000156009,
+ VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = 1000156010,
+ VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = 1000156011,
+ VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = 1000156012,
+ VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = 1000156013,
+ VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = 1000156014,
+ VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = 1000156015,
+ VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = 1000156016,
+ VK_FORMAT_R12X4_UNORM_PACK16_KHR = 1000156017,
+ VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = 1000156018,
+ VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = 1000156019,
+ VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = 1000156020,
+ VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = 1000156021,
+ VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = 1000156022,
+ VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = 1000156023,
+ VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = 1000156024,
+ VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = 1000156025,
+ VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = 1000156026,
+ VK_FORMAT_G16B16G16R16_422_UNORM_KHR = 1000156027,
+ VK_FORMAT_B16G16R16G16_422_UNORM_KHR = 1000156028,
+ VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = 1000156029,
+ VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = 1000156030,
+ VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = 1000156031,
+ VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = 1000156032,
+ VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = 1000156033,
VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED,
VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1),
@@ -621,6 +675,8 @@
VK_IMAGE_LAYOUT_PREINITIALIZED = 8,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002,
VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000,
+ VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = 1000117000,
+ VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = 1000117001,
VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1),
@@ -851,6 +907,7 @@
VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8,
VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000,
VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000,
+ VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000,
VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE,
VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1),
@@ -1009,6 +1066,8 @@
VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = 1000085000,
VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000,
VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001,
+ VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = 1000156000,
+ VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN,
VK_OBJECT_TYPE_END_RANGE = VK_OBJECT_TYPE_COMMAND_POOL,
VK_OBJECT_TYPE_RANGE_SIZE = (VK_OBJECT_TYPE_COMMAND_POOL - VK_OBJECT_TYPE_UNKNOWN + 1),
@@ -1035,6 +1094,13 @@
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000,
VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000,
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000,
+ VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000,
+ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000,
+ VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = 0x00400000,
+ VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000,
VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkFormatFeatureFlagBits;
typedef VkFlags VkFormatFeatureFlags;
@@ -1060,6 +1126,11 @@
VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010,
VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040,
VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020,
+ VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080,
+ VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = 0x00000100,
+ VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000,
+ VK_IMAGE_CREATE_DISJOINT_BIT_KHR = 0x00000200,
+ VK_IMAGE_CREATE_ALIAS_BIT_KHR = 0x00000400,
VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkImageCreateFlagBits;
typedef VkFlags VkImageCreateFlags;
@@ -1133,6 +1204,9 @@
VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002,
VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004,
VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008,
+ VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = 0x00000010,
+ VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = 0x00000020,
+ VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = 0x00000040,
VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkImageAspectFlagBits;
typedef VkFlags VkImageAspectFlags;
@@ -1366,6 +1440,27 @@
} VkStencilFaceFlagBits;
typedef VkFlags VkStencilFaceFlags;
+typedef struct VkApplicationInfo {
+ VkStructureType sType;
+ const void* pNext;
+ const char* pApplicationName;
+ uint32_t applicationVersion;
+ const char* pEngineName;
+ uint32_t engineVersion;
+ uint32_t apiVersion;
+} VkApplicationInfo;
+
+typedef struct VkInstanceCreateInfo {
+ VkStructureType sType;
+ const void* pNext;
+ VkInstanceCreateFlags flags;
+ const VkApplicationInfo* pApplicationInfo;
+ uint32_t enabledLayerCount;
+ const char* const* ppEnabledLayerNames;
+ uint32_t enabledExtensionCount;
+ const char* const* ppEnabledExtensionNames;
+} VkInstanceCreateInfo;
+
typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)(
void* pUserData,
size_t size,
@@ -1395,29 +1490,6 @@
VkInternalAllocationType allocationType,
VkSystemAllocationScope allocationScope);
-typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void);
-
-typedef struct VkApplicationInfo {
- VkStructureType sType;
- const void* pNext;
- const char* pApplicationName;
- uint32_t applicationVersion;
- const char* pEngineName;
- uint32_t engineVersion;
- uint32_t apiVersion;
-} VkApplicationInfo;
-
-typedef struct VkInstanceCreateInfo {
- VkStructureType sType;
- const void* pNext;
- VkInstanceCreateFlags flags;
- const VkApplicationInfo* pApplicationInfo;
- uint32_t enabledLayerCount;
- const char* const* ppEnabledLayerNames;
- uint32_t enabledExtensionCount;
- const char* const* ppEnabledExtensionNames;
-} VkInstanceCreateInfo;
-
typedef struct VkAllocationCallbacks {
void* pUserData;
PFN_vkAllocationFunction pfnAllocation;
@@ -1658,6 +1730,7 @@
VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;
+typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void);
typedef struct VkDeviceQueueCreateInfo {
VkStructureType sType;
const void* pNext;
@@ -4774,6 +4847,62 @@
int* pFd);
#endif
+#define VK_KHR_maintenance2 1
+#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1
+#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2"
+
+
+typedef enum VkPointClippingBehaviorKHR {
+ VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = 0,
+ VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = 1,
+ VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE_KHR = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR,
+ VK_POINT_CLIPPING_BEHAVIOR_END_RANGE_KHR = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR,
+ VK_POINT_CLIPPING_BEHAVIOR_RANGE_SIZE_KHR = (VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR + 1),
+ VK_POINT_CLIPPING_BEHAVIOR_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkPointClippingBehaviorKHR;
+
+typedef enum VkTessellationDomainOriginKHR {
+ VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = 0,
+ VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = 1,
+ VK_TESSELLATION_DOMAIN_ORIGIN_BEGIN_RANGE_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR,
+ VK_TESSELLATION_DOMAIN_ORIGIN_END_RANGE_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR,
+ VK_TESSELLATION_DOMAIN_ORIGIN_RANGE_SIZE_KHR = (VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR + 1),
+ VK_TESSELLATION_DOMAIN_ORIGIN_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkTessellationDomainOriginKHR;
+
+typedef struct VkPhysicalDevicePointClippingPropertiesKHR {
+ VkStructureType sType;
+ void* pNext;
+ VkPointClippingBehaviorKHR pointClippingBehavior;
+} VkPhysicalDevicePointClippingPropertiesKHR;
+
+typedef struct VkInputAttachmentAspectReferenceKHR {
+ uint32_t subpass;
+ uint32_t inputAttachmentIndex;
+ VkImageAspectFlags aspectMask;
+} VkInputAttachmentAspectReferenceKHR;
+
+typedef struct VkRenderPassInputAttachmentAspectCreateInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t aspectReferenceCount;
+ const VkInputAttachmentAspectReferenceKHR* pAspectReferences;
+} VkRenderPassInputAttachmentAspectCreateInfoKHR;
+
+typedef struct VkImageViewUsageCreateInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkImageUsageFlags usage;
+} VkImageViewUsageCreateInfoKHR;
+
+typedef struct VkPipelineTessellationDomainOriginStateCreateInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkTessellationDomainOriginKHR domainOrigin;
+} VkPipelineTessellationDomainOriginStateCreateInfoKHR;
+
+
+
#define VK_KHR_get_surface_capabilities2 1
#define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
#define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
@@ -4827,7 +4956,7 @@
#define VK_KHR_dedicated_allocation 1
-#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 1
+#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3
#define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation"
typedef struct VkMemoryDedicatedRequirementsKHR {
@@ -4851,6 +4980,11 @@
#define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class"
+#define VK_KHR_relaxed_block_layout 1
+#define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1
+#define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout"
+
+
#define VK_KHR_get_memory_requirements2 1
#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1
#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2"
@@ -4908,6 +5042,152 @@
VkSparseImageMemoryRequirements2KHR* pSparseMemoryRequirements);
#endif
+#define VK_KHR_image_format_list 1
+#define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1
+#define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list"
+
+typedef struct VkImageFormatListCreateInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t viewFormatCount;
+ const VkFormat* pViewFormats;
+} VkImageFormatListCreateInfoKHR;
+
+
+
+#define VK_KHR_sampler_ycbcr_conversion 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversionKHR)
+
+#define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1
+#define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion"
+
+
+typedef enum VkSamplerYcbcrModelConversionKHR {
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = 0,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = 1,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = 2,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = 3,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = 4,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_BEGIN_RANGE_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_END_RANGE_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR,
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_RANGE_SIZE_KHR = (VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR + 1),
+ VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSamplerYcbcrModelConversionKHR;
+
+typedef enum VkSamplerYcbcrRangeKHR {
+ VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = 0,
+ VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = 1,
+ VK_SAMPLER_YCBCR_RANGE_BEGIN_RANGE_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR,
+ VK_SAMPLER_YCBCR_RANGE_END_RANGE_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR,
+ VK_SAMPLER_YCBCR_RANGE_RANGE_SIZE_KHR = (VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR - VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR + 1),
+ VK_SAMPLER_YCBCR_RANGE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSamplerYcbcrRangeKHR;
+
+typedef enum VkChromaLocationKHR {
+ VK_CHROMA_LOCATION_COSITED_EVEN_KHR = 0,
+ VK_CHROMA_LOCATION_MIDPOINT_KHR = 1,
+ VK_CHROMA_LOCATION_BEGIN_RANGE_KHR = VK_CHROMA_LOCATION_COSITED_EVEN_KHR,
+ VK_CHROMA_LOCATION_END_RANGE_KHR = VK_CHROMA_LOCATION_MIDPOINT_KHR,
+ VK_CHROMA_LOCATION_RANGE_SIZE_KHR = (VK_CHROMA_LOCATION_MIDPOINT_KHR - VK_CHROMA_LOCATION_COSITED_EVEN_KHR + 1),
+ VK_CHROMA_LOCATION_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkChromaLocationKHR;
+
+typedef struct VkSamplerYcbcrConversionCreateInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkSamplerYcbcrModelConversionKHR ycbcrModel;
+ VkSamplerYcbcrRangeKHR ycbcrRange;
+ VkComponentMapping components;
+ VkChromaLocationKHR xChromaOffset;
+ VkChromaLocationKHR yChromaOffset;
+ VkFilter chromaFilter;
+ VkBool32 forceExplicitReconstruction;
+} VkSamplerYcbcrConversionCreateInfoKHR;
+
+typedef struct VkSamplerYcbcrConversionInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkSamplerYcbcrConversionKHR conversion;
+} VkSamplerYcbcrConversionInfoKHR;
+
+typedef struct VkBindImagePlaneMemoryInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkImageAspectFlagBits planeAspect;
+} VkBindImagePlaneMemoryInfoKHR;
+
+typedef struct VkImagePlaneMemoryRequirementsInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkImageAspectFlagBits planeAspect;
+} VkImagePlaneMemoryRequirementsInfoKHR;
+
+typedef struct VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR {
+ VkStructureType sType;
+ void* pNext;
+ VkBool32 samplerYcbcrConversion;
+} VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR;
+
+typedef struct VkSamplerYcbcrConversionImageFormatPropertiesKHR {
+ VkStructureType sType;
+ void* pNext;
+ uint32_t combinedImageSamplerDescriptorCount;
+} VkSamplerYcbcrConversionImageFormatPropertiesKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateSamplerYcbcrConversionKHR)(VkDevice device, const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversionKHR* pYcbcrConversion);
+typedef void (VKAPI_PTR *PFN_vkDestroySamplerYcbcrConversionKHR)(VkDevice device, VkSamplerYcbcrConversionKHR ycbcrConversion, const VkAllocationCallbacks* pAllocator);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateSamplerYcbcrConversionKHR(
+ VkDevice device,
+ const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkSamplerYcbcrConversionKHR* pYcbcrConversion);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversionKHR(
+ VkDevice device,
+ VkSamplerYcbcrConversionKHR ycbcrConversion,
+ const VkAllocationCallbacks* pAllocator);
+#endif
+
+#define VK_KHR_bind_memory2 1
+#define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1
+#define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2"
+
+typedef struct VkBindBufferMemoryInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkBuffer buffer;
+ VkDeviceMemory memory;
+ VkDeviceSize memoryOffset;
+} VkBindBufferMemoryInfoKHR;
+
+typedef struct VkBindImageMemoryInfoKHR {
+ VkStructureType sType;
+ const void* pNext;
+ VkImage image;
+ VkDeviceMemory memory;
+ VkDeviceSize memoryOffset;
+} VkBindImageMemoryInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHR* pBindInfos);
+typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHR(
+ VkDevice device,
+ uint32_t bindInfoCount,
+ const VkBindBufferMemoryInfoKHR* pBindInfos);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHR(
+ VkDevice device,
+ uint32_t bindInfoCount,
+ const VkBindImageMemoryInfoKHR* pBindInfos);
+#endif
+
#define VK_EXT_debug_report 1
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
@@ -4951,10 +5231,12 @@
VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
+ VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = 33,
VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
+ VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000,
VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
- VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT,
- VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
+ VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT,
+ VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
} VkDebugReportObjectTypeEXT;
@@ -5021,6 +5303,11 @@
#define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader"
+#define VK_EXT_depth_range_unrestricted 1
+#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1
+#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME "VK_EXT_depth_range_unrestricted"
+
+
#define VK_IMG_filter_cubic 1
#define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1
#define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic"
@@ -5088,31 +5375,31 @@
} VkDebugMarkerMarkerInfoEXT;
-typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo);
-typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo);
-typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer);
-typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
#ifndef VK_NO_PROTOTYPES
VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(
VkDevice device,
- VkDebugMarkerObjectTagInfoEXT* pTagInfo);
+ const VkDebugMarkerObjectTagInfoEXT* pTagInfo);
VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(
VkDevice device,
- VkDebugMarkerObjectNameInfoEXT* pNameInfo);
+ const VkDebugMarkerObjectNameInfoEXT* pNameInfo);
VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(
VkCommandBuffer commandBuffer,
- VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+ const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(
VkCommandBuffer commandBuffer);
VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(
VkCommandBuffer commandBuffer,
- VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+ const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
#endif
#define VK_AMD_gcn_shader 1
@@ -5350,9 +5637,9 @@
#endif /* VK_USE_PLATFORM_WIN32_KHR */
#define VK_KHX_device_group 1
-#define VK_MAX_DEVICE_GROUP_SIZE_KHX 32
-#define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1
+#define VK_KHX_DEVICE_GROUP_SPEC_VERSION 2
#define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+#define VK_MAX_DEVICE_GROUP_SIZE_KHX 32
typedef enum VkPeerMemoryFeatureFlagBitsKHX {
@@ -5386,28 +5673,6 @@
uint32_t deviceMask;
} VkMemoryAllocateFlagsInfoKHX;
-typedef struct VkBindBufferMemoryInfoKHX {
- VkStructureType sType;
- const void* pNext;
- VkBuffer buffer;
- VkDeviceMemory memory;
- VkDeviceSize memoryOffset;
- uint32_t deviceIndexCount;
- const uint32_t* pDeviceIndices;
-} VkBindBufferMemoryInfoKHX;
-
-typedef struct VkBindImageMemoryInfoKHX {
- VkStructureType sType;
- const void* pNext;
- VkImage image;
- VkDeviceMemory memory;
- VkDeviceSize memoryOffset;
- uint32_t deviceIndexCount;
- const uint32_t* pDeviceIndices;
- uint32_t SFRRectCount;
- const VkRect2D* pSFRRects;
-} VkBindImageMemoryInfoKHX;
-
typedef struct VkDeviceGroupRenderPassBeginInfoKHX {
VkStructureType sType;
const void* pNext;
@@ -5440,6 +5705,22 @@
uint32_t memoryDeviceIndex;
} VkDeviceGroupBindSparseInfoKHX;
+typedef struct VkBindBufferMemoryDeviceGroupInfoKHX {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t deviceIndexCount;
+ const uint32_t* pDeviceIndices;
+} VkBindBufferMemoryDeviceGroupInfoKHX;
+
+typedef struct VkBindImageMemoryDeviceGroupInfoKHX {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t deviceIndexCount;
+ const uint32_t* pDeviceIndices;
+ uint32_t SFRRectCount;
+ const VkRect2D* pSFRRects;
+} VkBindImageMemoryDeviceGroupInfoKHX;
+
typedef struct VkDeviceGroupPresentCapabilitiesKHX {
VkStructureType sType;
const void* pNext;
@@ -5486,14 +5767,12 @@
typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures);
-typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos);
-typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos);
typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes);
-typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
-typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
#ifndef VK_NO_PROTOTYPES
VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX(
@@ -5503,34 +5782,10 @@
uint32_t remoteDeviceIndex,
VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures);
-VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX(
- VkDevice device,
- uint32_t bindInfoCount,
- const VkBindBufferMemoryInfoKHX* pBindInfos);
-
-VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX(
- VkDevice device,
- uint32_t bindInfoCount,
- const VkBindImageMemoryInfoKHX* pBindInfos);
-
VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX(
VkCommandBuffer commandBuffer,
uint32_t deviceMask);
-VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
- VkDevice device,
- VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
-
-VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
- VkDevice device,
- VkSurfaceKHR surface,
- VkDeviceGroupPresentModeFlagsKHX* pModes);
-
-VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
- VkDevice device,
- const VkAcquireNextImageInfoKHX* pAcquireInfo,
- uint32_t* pImageIndex);
-
VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX(
VkCommandBuffer commandBuffer,
uint32_t baseGroupX,
@@ -5540,11 +5795,25 @@
uint32_t groupCountY,
uint32_t groupCountZ);
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
+ VkDevice device,
+ VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
+ VkDevice device,
+ VkSurfaceKHR surface,
+ VkDeviceGroupPresentModeFlagsKHX* pModes);
+
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX(
VkPhysicalDevice physicalDevice,
VkSurfaceKHR surface,
uint32_t* pRectCount,
VkRect2D* pRects);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
+ VkDevice device,
+ const VkAcquireNextImageInfoKHX* pAcquireInfo,
+ uint32_t* pImageIndex);
#endif
#define VK_EXT_validation_flags 1
@@ -5639,7 +5908,7 @@
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
-#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3
#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
@@ -5929,6 +6198,7 @@
#define VK_EXT_display_surface_counter 1
#define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
#define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+#define VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT
typedef enum VkSurfaceCounterFlagBitsEXT {
@@ -6328,6 +6598,96 @@
#define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16"
+#define VK_AMD_mixed_attachment_samples 1
+#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1
+#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples"
+
+
+#define VK_AMD_shader_fragment_mask 1
+#define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1
+#define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask"
+
+
+#define VK_EXT_shader_stencil_export 1
+#define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1
+#define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export"
+
+
+#define VK_EXT_sample_locations 1
+#define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1
+#define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations"
+
+typedef struct VkSampleLocationEXT {
+ float x;
+ float y;
+} VkSampleLocationEXT;
+
+typedef struct VkSampleLocationsInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ VkSampleCountFlagBits sampleLocationsPerPixel;
+ VkExtent2D sampleLocationGridSize;
+ uint32_t sampleLocationsCount;
+ const VkSampleLocationEXT* pSampleLocations;
+} VkSampleLocationsInfoEXT;
+
+typedef struct VkAttachmentSampleLocationsEXT {
+ uint32_t attachmentIndex;
+ VkSampleLocationsInfoEXT sampleLocationsInfo;
+} VkAttachmentSampleLocationsEXT;
+
+typedef struct VkSubpassSampleLocationsEXT {
+ uint32_t subpassIndex;
+ VkSampleLocationsInfoEXT sampleLocationsInfo;
+} VkSubpassSampleLocationsEXT;
+
+typedef struct VkRenderPassSampleLocationsBeginInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t attachmentInitialSampleLocationsCount;
+ const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations;
+ uint32_t postSubpassSampleLocationsCount;
+ const VkSubpassSampleLocationsEXT* pSubpassSampleLocations;
+} VkRenderPassSampleLocationsBeginInfoEXT;
+
+typedef struct VkPipelineSampleLocationsStateCreateInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ VkBool32 sampleLocationsEnable;
+ VkSampleLocationsInfoEXT sampleLocationsInfo;
+} VkPipelineSampleLocationsStateCreateInfoEXT;
+
+typedef struct VkPhysicalDeviceSampleLocationsPropertiesEXT {
+ VkStructureType sType;
+ void* pNext;
+ VkSampleCountFlags sampleLocationSampleCounts;
+ VkExtent2D maxSampleLocationGridSize;
+ float sampleLocationCoordinateRange[2];
+ uint32_t sampleLocationSubPixelBits;
+ VkBool32 variableSampleLocations;
+} VkPhysicalDeviceSampleLocationsPropertiesEXT;
+
+typedef struct VkMultisamplePropertiesEXT {
+ VkStructureType sType;
+ void* pNext;
+ VkExtent2D maxSampleLocationGridSize;
+} VkMultisamplePropertiesEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetSampleLocationsEXT)(VkCommandBuffer commandBuffer, const VkSampleLocationsInfoEXT* pSampleLocationsInfo);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT)(VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples, VkMultisamplePropertiesEXT* pMultisampleProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetSampleLocationsEXT(
+ VkCommandBuffer commandBuffer,
+ const VkSampleLocationsInfoEXT* pSampleLocationsInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMultisamplePropertiesEXT(
+ VkPhysicalDevice physicalDevice,
+ VkSampleCountFlagBits samples,
+ VkMultisamplePropertiesEXT* pMultisampleProperties);
+#endif
+
#define VK_EXT_blend_operation_advanced 1
#define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2
#define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced"
@@ -6421,6 +6781,78 @@
#define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle"
+#define VK_EXT_post_depth_coverage 1
+#define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1
+#define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage"
+
+
+#define VK_EXT_validation_cache 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkValidationCacheEXT)
+
+#define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1
+#define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache"
+
+
+typedef enum VkValidationCacheHeaderVersionEXT {
+ VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1,
+ VK_VALIDATION_CACHE_HEADER_VERSION_BEGIN_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT,
+ VK_VALIDATION_CACHE_HEADER_VERSION_END_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT,
+ VK_VALIDATION_CACHE_HEADER_VERSION_RANGE_SIZE_EXT = (VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT - VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT + 1),
+ VK_VALIDATION_CACHE_HEADER_VERSION_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkValidationCacheHeaderVersionEXT;
+
+typedef VkFlags VkValidationCacheCreateFlagsEXT;
+
+typedef struct VkValidationCacheCreateInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ VkValidationCacheCreateFlagsEXT flags;
+ size_t initialDataSize;
+ const void* pInitialData;
+} VkValidationCacheCreateInfoEXT;
+
+typedef struct VkShaderModuleValidationCacheCreateInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ VkValidationCacheEXT validationCache;
+} VkShaderModuleValidationCacheCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateValidationCacheEXT)(VkDevice device, const VkValidationCacheCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkValidationCacheEXT* pValidationCache);
+typedef void (VKAPI_PTR *PFN_vkDestroyValidationCacheEXT)(VkDevice device, VkValidationCacheEXT validationCache, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkMergeValidationCachesEXT)(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, const VkValidationCacheEXT* pSrcCaches);
+typedef VkResult (VKAPI_PTR *PFN_vkGetValidationCacheDataEXT)(VkDevice device, VkValidationCacheEXT validationCache, size_t* pDataSize, void* pData);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateValidationCacheEXT(
+ VkDevice device,
+ const VkValidationCacheCreateInfoEXT* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkValidationCacheEXT* pValidationCache);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyValidationCacheEXT(
+ VkDevice device,
+ VkValidationCacheEXT validationCache,
+ const VkAllocationCallbacks* pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkMergeValidationCachesEXT(
+ VkDevice device,
+ VkValidationCacheEXT dstCache,
+ uint32_t srcCacheCount,
+ const VkValidationCacheEXT* pSrcCaches);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetValidationCacheDataEXT(
+ VkDevice device,
+ VkValidationCacheEXT validationCache,
+ size_t* pDataSize,
+ void* pData);
+#endif
+
+#define VK_EXT_shader_viewport_index_layer 1
+#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1
+#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer"
+
+
#ifdef __cplusplus
}
#endif
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 001eaf1..8dd55f4 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -467,6 +467,7 @@
"vkGetPhysicalDeviceImageFormatProperties2KHR",
"vkGetPhysicalDeviceMemoryProperties",
"vkGetPhysicalDeviceMemoryProperties2KHR",
+ "vkGetPhysicalDeviceMultisamplePropertiesEXT",
"vkGetPhysicalDevicePresentRectanglesKHX",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2KHR",
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index a3da651..e2d5c83 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -16,6 +16,7 @@
#include <hardware/hwvulkan.h>
+#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@@ -343,9 +344,14 @@
VkResult EnumeratePhysicalDevices(VkInstance instance,
uint32_t* physical_device_count,
VkPhysicalDevice* physical_devices) {
- if (physical_devices && *physical_device_count >= 1)
+ if (!physical_devices)
+ *physical_device_count = 1;
+ else if (*physical_device_count == 0)
+ return VK_INCOMPLETE;
+ else {
physical_devices[0] = &instance->physical_device;
- *physical_device_count = 1;
+ *physical_device_count = 1;
+ }
return VK_SUCCESS;
}