Merge "Different previous velocity calculation"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index a48dab9..0bdca2d 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -130,6 +130,14 @@
{ REQ, "events/irq/enable" },
{ OPT, "events/ipi/enable" },
} },
+ { "irqoff", "IRQ-disabled code section tracing", 0, {
+ { REQ, "events/preemptirq/irq_enable/enable" },
+ { REQ, "events/preemptirq/irq_disable/enable" },
+ } },
+ { "preemptoff", "Preempt-disabled code section tracing", 0, {
+ { REQ, "events/preemptirq/preempt_enable/enable" },
+ { REQ, "events/preemptirq/preempt_disable/enable" },
+ } },
{ "i2c", "I2C Events", 0, {
{ REQ, "events/i2c/enable" },
{ REQ, "events/i2c/i2c_read/enable" },
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index a960333..ea5fbf1 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -17,31 +17,6 @@
LOCAL_SRC_FILES := \
tests/dumpstate_test_fixture.cpp
-LOCAL_MODULE_CLASS := NATIVE_TESTS
-
-dumpstate_tests_intermediates := $(local-intermediates-dir)/DATA
-dumpstate_tests_subpath_from_data := nativetest/dumpstate_test_fixture
-dumpstate_tests_root_in_device := /data/$(dumpstate_tests_subpath_from_data)
-dumpstate_tests_root_for_test_zip := $(dumpstate_tests_intermediates)/$(dumpstate_tests_subpath_from_data)
-testdata_files := $(call find-subdir-files, testdata/*)
-
-# Copy test data files to intermediates/DATA for use with LOCAL_PICKUP_FILES
-GEN := $(addprefix $(dumpstate_tests_root_for_test_zip)/, $(testdata_files))
-$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN): $(dumpstate_tests_root_for_test_zip)/testdata/% : $(LOCAL_PATH)/testdata/%
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-# Copy test data files again to $OUT/data so the tests can be run with adb sync
-# TODO: the build system should do this automatically
-GEN := $(addprefix $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/, $(testdata_files))
-$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN): $(TARGET_OUT_DATA)/$(dumpstate_tests_subpath_from_data)/testdata/% : $(LOCAL_PATH)/testdata/%
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-LOCAL_PICKUP_FILES := $(dumpstate_tests_intermediates)
+LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata)
include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index e866b8b..ea7109b 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -103,6 +103,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;
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 b3d628c..b6b34fb 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1069,14 +1069,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"});
@@ -1135,7 +1136,7 @@
RunCommand("VOLD DUMP", {"vdc", "dump"});
RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
- RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build());
+ RunCommand("STORAGED UID IO INFO", {"storaged", "-u"});
RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
@@ -1850,6 +1851,9 @@
RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"},
CommandOptions::WithTimeout(10).Build());
+ // Run iotop as root to show top 100 IO threads
+ RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"});
+
if (!DropRootUser()) {
return -1;
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 1c19268..92b0c0d 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -94,7 +94,7 @@
protected:
const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str());
const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/";
- const std::string kTestDataPath = kFixturesPath + "/testdata/";
+ const std::string kTestDataPath = kFixturesPath + "tests/testdata/";
const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
const std::string kEchoCommand = "/system/bin/echo";
@@ -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/testdata/empty-file.txt b/cmds/dumpstate/tests/testdata/empty-file.txt
similarity index 100%
rename from cmds/dumpstate/testdata/empty-file.txt
rename to cmds/dumpstate/tests/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt
similarity index 100%
rename from cmds/dumpstate/testdata/multiple-lines-with-newline.txt
rename to cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/tests/testdata/multiple-lines.txt
similarity index 100%
rename from cmds/dumpstate/testdata/multiple-lines.txt
rename to cmds/dumpstate/tests/testdata/multiple-lines.txt
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/tests/testdata/single-line-with-newline.txt
similarity index 100%
rename from cmds/dumpstate/testdata/single-line-with-newline.txt
rename to cmds/dumpstate/tests/testdata/single-line-with-newline.txt
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/tests/testdata/single-line.txt
similarity index 100%
rename from cmds/dumpstate/testdata/single-line.txt
rename to cmds/dumpstate/tests/testdata/single-line.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
rename to cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-one-run-no-newline.txt
rename to cmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/tests/testdata/stats-two-runs.txt
similarity index 100%
rename from cmds/dumpstate/testdata/stats-two-runs.txt
rename to cmds/dumpstate/tests/testdata/stats-two-runs.txt
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 3227749..8fe246b 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -53,13 +53,16 @@
static void usage() {
fprintf(stderr,
- "usage: dumpsys\n"
+ "usage: dumpsys\n"
" To dump all services.\n"
"or:\n"
- " dumpsys [-t TIMEOUT] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n"
+ " dumpsys [-t TIMEOUT] [--priority LEVEL] [--help | -l | --skip SERVICES | "
+ "SERVICE [ARGS]]\n"
" --help: shows this help\n"
" -l: only list services, do not dump them\n"
" -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n"
+ " --priority LEVEL: filter services based on specified priority\n"
+ " LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
" --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
" SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
}
@@ -80,11 +83,11 @@
bool showListOnly = false;
bool skipServices = false;
int timeoutArg = 10;
- static struct option longOptions[] = {
- {"skip", no_argument, 0, 0 },
- {"help", no_argument, 0, 0 },
- { 0, 0, 0, 0 }
- };
+ int dumpPriority = IServiceManager::DUMP_PRIORITY_ALL;
+ static struct option longOptions[] = {{"priority", required_argument, 0, 0},
+ {"skip", no_argument, 0, 0},
+ {"help", no_argument, 0, 0},
+ {0, 0, 0, 0}};
// Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
// happens on test cases).
@@ -106,6 +109,18 @@
} else if (!strcmp(longOptions[optionIndex].name, "help")) {
usage();
return 0;
+ } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
+ if (!strcmp(optarg, "CRITICAL")) {
+ dumpPriority = IServiceManager::DUMP_PRIORITY_CRITICAL;
+ } else if (!strcmp(optarg, "HIGH")) {
+ dumpPriority = IServiceManager::DUMP_PRIORITY_HIGH;
+ } else if (!strcmp(optarg, "NORMAL")) {
+ dumpPriority = IServiceManager::DUMP_PRIORITY_NORMAL;
+ } else {
+ fprintf(stderr, "\n");
+ usage();
+ return -1;
+ }
}
break;
@@ -151,7 +166,7 @@
if (services.empty() || showListOnly) {
// gets all services
- services = sm_->listServices();
+ services = sm_->listServices(dumpPriority);
services.sort(sort_func);
args.add(String16("-a"));
}
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 5ca2b57..9fe4572 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -35,6 +35,7 @@
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::MakeAction;
+using ::testing::Mock;
using ::testing::Not;
using ::testing::Return;
using ::testing::StrEq;
@@ -49,8 +50,8 @@
public:
MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&));
MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&));
- MOCK_METHOD3(addService, status_t(const String16&, const sp<IBinder>&, bool));
- MOCK_METHOD0(listServices, Vector<String16>());
+ MOCK_METHOD4(addService, status_t(const String16&, const sp<IBinder>&, bool, int));
+ MOCK_METHOD1(listServices, Vector<String16>(int));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
@@ -130,7 +131,16 @@
for (auto& service : services) {
services16.add(String16(service.c_str()));
}
- EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16));
+ EXPECT_CALL(sm_, listServices(IServiceManager::DUMP_PRIORITY_ALL))
+ .WillRepeatedly(Return(services16));
+ }
+
+ void ExpectListServicesWithPriority(std::vector<std::string> services, int dumpPriority) {
+ Vector<String16> services16;
+ for (auto& service : services) {
+ services16.add(String16(service.c_str()));
+ }
+ EXPECT_CALL(sm_, listServices(dumpPriority)).WillRepeatedly(Return(services16));
}
sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
@@ -155,10 +165,11 @@
.WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
}
- void ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
+ sp<BinderMock> ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
sp<BinderMock> binder_mock = ExpectCheckService(name);
EXPECT_CALL(*binder_mock, dump(_, _))
.WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0)));
+ return binder_mock;
}
void CallMain(const std::vector<std::string>& args) {
@@ -177,7 +188,10 @@
}
void AssertRunningServices(const std::vector<std::string>& services) {
- std::string expected("Currently running services:\n");
+ std::string expected;
+ if (services.size() > 1) {
+ expected.append("Currently running services:\n");
+ }
for (const std::string& service : services) {
expected.append(" ").append(service).append("\n");
}
@@ -234,6 +248,26 @@
AssertNotDumped({"Valet"});
}
+// Tests 'dumpsys -l --priority HIGH'
+TEST_F(DumpsysTest, ListAllServicesWithPriority) {
+ ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_PRIORITY_HIGH);
+ ExpectCheckService("Locksmith");
+ ExpectCheckService("Valet");
+
+ CallMain({"-l", "--priority", "HIGH"});
+
+ AssertRunningServices({"Locksmith", "Valet"});
+}
+
+// Tests 'dumpsys -l --priority HIGH' with and empty list
+TEST_F(DumpsysTest, ListEmptyServicesWithPriority) {
+ ExpectListServicesWithPriority({}, IServiceManager::DUMP_PRIORITY_HIGH);
+
+ CallMain({"-l", "--priority", "HIGH"});
+
+ AssertRunningServices({});
+}
+
// Tests 'dumpsys service_name' on a service is running
TEST_F(DumpsysTest, DumpRunningService) {
ExpectDump("Valet", "Here's your car");
@@ -245,15 +279,15 @@
// Tests 'dumpsys -t 1 service_name' on a service that times out after 2s
TEST_F(DumpsysTest, DumpRunningServiceTimeout) {
- ExpectDumpAndHang("Valet", 2, "Here's your car");
+ sp<BinderMock> binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car");
CallMain({"-t", "1", "Valet"});
AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED");
AssertNotDumped("Here's your car");
- // Must wait so binder mock is deleted, otherwise test will fail with a leaked object
- sleep(1);
+ // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp
+ Mock::AllowLeak(binder_mock.get());
}
// Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running
@@ -298,3 +332,65 @@
AssertNotDumped("dump3");
AssertNotDumped("dump5");
}
+
+// Tests 'dumpsys --skip skipped3 skipped5 --priority CRITICAL', which should skip these services
+TEST_F(DumpsysTest, DumpWithSkipAndPriority) {
+ ExpectListServicesWithPriority({"running1", "stopped2", "skipped3", "running4", "skipped5"},
+ IServiceManager::DUMP_PRIORITY_CRITICAL);
+ ExpectDump("running1", "dump1");
+ ExpectCheckService("stopped2", false);
+ ExpectDump("skipped3", "dump3");
+ ExpectDump("running4", "dump4");
+ ExpectDump("skipped5", "dump5");
+
+ CallMain({"--priority", "CRITICAL", "--skip", "skipped3", "skipped5"});
+
+ AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"});
+ AssertDumped("running1", "dump1");
+ AssertDumped("running4", "dump4");
+ AssertStopped("stopped2");
+ AssertNotDumped("dump3");
+ AssertNotDumped("dump5");
+}
+
+// Tests 'dumpsys --priority CRITICAL'
+TEST_F(DumpsysTest, DumpWithPriorityCritical) {
+ ExpectListServicesWithPriority({"runningcritical1", "runningcritical2"},
+ IServiceManager::DUMP_PRIORITY_CRITICAL);
+ ExpectDump("runningcritical1", "dump1");
+ ExpectDump("runningcritical2", "dump2");
+
+ CallMain({"--priority", "CRITICAL"});
+
+ AssertRunningServices({"runningcritical1", "runningcritical2"});
+ AssertDumped("runningcritical1", "dump1");
+ AssertDumped("runningcritical2", "dump2");
+}
+
+// Tests 'dumpsys --priority HIGH'
+TEST_F(DumpsysTest, DumpWithPriorityHigh) {
+ ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"},
+ IServiceManager::DUMP_PRIORITY_HIGH);
+ ExpectDump("runninghigh1", "dump1");
+ ExpectDump("runninghigh2", "dump2");
+
+ CallMain({"--priority", "HIGH"});
+
+ AssertRunningServices({"runninghigh1", "runninghigh2"});
+ AssertDumped("runninghigh1", "dump1");
+ AssertDumped("runninghigh2", "dump2");
+}
+
+// Tests 'dumpsys --priority NORMAL'
+TEST_F(DumpsysTest, DumpWithPriorityNormal) {
+ ExpectListServicesWithPriority({"runningnormal1", "runningnormal2"},
+ IServiceManager::DUMP_PRIORITY_NORMAL);
+ ExpectDump("runningnormal1", "dump1");
+ ExpectDump("runningnormal2", "dump2");
+
+ CallMain({"--priority", "NORMAL"});
+
+ AssertRunningServices({"runningnormal1", "runningnormal2"});
+ AssertDumped("runningnormal1", "dump1");
+ AssertDumped("runningnormal2", "dump2");
+}
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 33db6db..56470d6 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -4,6 +4,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wextra",
],
srcs: [
"CacheItem.cpp",
@@ -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-*"
+ ],
}
//
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index af7455a..4246536 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -525,6 +525,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 +714,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);
+ }
}
}
}
@@ -1863,7 +1869,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/dexopt.cpp b/cmds/installd/dexopt.cpp
index 8c75ee5..6a7d845 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -41,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"
@@ -156,7 +157,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) {
@@ -333,7 +334,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) {
@@ -955,14 +957,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) {
@@ -1198,21 +1192,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;
}
}
@@ -1805,8 +1794,14 @@
}
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)) {
+ uid, storage_flag, /*validate_package_path*/ false)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
return false;
}
@@ -1820,42 +1815,56 @@
return false;
}
- // The secondary dex does not exist anymore. 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;
- 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;
- }
+ // As a security measure we want to unlink art artifacts with the reduced capabilities
+ // 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.
+ 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;
+ }
- // Delete oat/vdex/art files.
- result = unlink_if_exists(oat_path) && result;
- result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
- result = unlink_if_exists(create_image_filename(oat_path)) && result;
+ // Delete oat/vdex/art files.
+ result = unlink_if_exists(oat_path) && result;
+ result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
+ result = unlink_if_exists(create_image_filename(oat_path)) && result;
- // Delete profiles.
- std::string current_profile = create_current_profile_path(
+ // Delete profiles.
+ std::string current_profile = create_current_profile_path(
multiuser_get_user_id(uid), dex_path, /*is_secondary*/true);
- std::string reference_profile = create_reference_profile_path(
+ std::string reference_profile = create_reference_profile_path(
dex_path, /*is_secondary*/true);
- result = unlink_if_exists(current_profile) && result;
- result = unlink_if_exists(reference_profile) && result;
+ result = unlink_if_exists(current_profile) && result;
+ result = unlink_if_exists(reference_profile) && result;
- // We upgraded once the location of current profile for secondary dex files.
- // Check for any previous left-overs and remove them as well.
- std::string old_current_profile = dex_path + ".prof";
- result = unlink_if_exists(old_current_profile);
+ // We upgraded once the location of current profile for secondary dex files.
+ // Check for any previous left-overs and remove them as well.
+ std::string old_current_profile = dex_path + ".prof";
+ result = unlink_if_exists(old_current_profile);
- // Try removing the directories as well, they might be empty.
- result = rmdir_if_empty(oat_isa_dir) && result;
- result = rmdir_if_empty(oat_dir) && result;
+ // Try removing the directories as well, they might be empty.
+ result = rmdir_if_empty(oat_isa_dir) && result;
+ result = rmdir_if_empty(oat_dir) && result;
+ }
+ result ? _exit(0) : _exit(1);
}
- return result;
+ int return_code = wait_child(pid);
+ return return_code == 0;
}
// Helper for move_ab, so that we can have common failure-case cleanup.
@@ -2020,5 +2029,98 @@
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;
+ }
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 23446da..1f41e67 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>
@@ -66,6 +68,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/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 09e1a00..e0d23da 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 {
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..09dd25a 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) {
@@ -276,184 +252,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",
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index d277bd3..c21fae5 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -129,24 +129,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 +195,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 +206,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() {
@@ -378,20 +360,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 +724,44 @@
}
}
+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) {
+ pos = path.find('/', pos + 1);
+ count++;
+ }
+
+ if (count > maxSubdirs) {
+ LOG(ERROR) << "Invalid path depth " << path << " when tested against " << dir;
return -1;
}
@@ -788,20 +773,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) {
+ const char* volume_uuid, int uid, int storage_flag, bool validate_package_path) {
CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
// Empty paths are not allowed.
@@ -815,15 +797,18 @@
// The path should be at most PKG_PATH_MAX long.
if (dex_path.size() > PKG_PATH_MAX) { return false; }
- // 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 (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
+ ? 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) {
- return false;
+ if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+ return false;
+ }
}
// If we got here we have a valid path.
@@ -831,116 +816,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) {
@@ -951,48 +846,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);
@@ -1065,7 +918,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 da3a293..3e04af9 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);
@@ -96,11 +89,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 +110,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);
-
-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, bool validate_package_path = true);
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/lshal/Android.bp b/cmds/lshal/Android.bp
index 2eed1ce..6cbe7e2 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -20,14 +20,17 @@
"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",
],
@@ -60,6 +63,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 31c42e7..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>
@@ -35,20 +36,34 @@
#include "Lshal.h"
#include "PipeRelay.h"
-#include "TextTable.h"
#include "Timeout.h"
#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()) {
@@ -63,7 +78,7 @@
if (pair != mCmdlines.end()) {
return pair->second;
}
- mCmdlines[pid] = ::android::lshal::getCmdline(pid);
+ mCmdlines[pid] = parseCmdline(pid);
return mCmdlines[pid];
}
@@ -74,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));
@@ -114,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 ";
@@ -123,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);
@@ -160,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);
@@ -205,48 +230,18 @@
}
}
}
-}
-void ListCommand::addLine(TextTable *textTable, 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 {
- std::vector<std::string> columns;
- for (TableColumnType type : mSelectedColumns) {
- switch (type) {
- case TableColumnType::INTERFACE_NAME: {
- columns.push_back(interfaceName);
- } break;
- case TableColumnType::TRANSPORT: {
- columns.push_back(transport);
- } break;
- case TableColumnType::ARCH: {
- columns.push_back(arch);
- } break;
- case TableColumnType::THREADS: {
- columns.push_back(threadUsage);
- } break;
- case TableColumnType::SERVER_ADDR: {
- columns.push_back(address);
- } break;
- case TableColumnType::SERVER_PID: {
- if (mEnableCmdlines) {
- columns.push_back(serverCmdline);
- } else {
- columns.push_back(server);
- }
- } break;
- case TableColumnType::CLIENT_PIDS: {
- if (mEnableCmdlines) {
- columns.push_back(clientCmdlines);
- } else {
- columns.push_back(clients);
- }
- } break;
- }
- }
- textTable->add(std::move(columns));
+ 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) {
@@ -259,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
@@ -288,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;
}
@@ -321,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;
}
@@ -334,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;
}
@@ -365,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) {
@@ -402,78 +379,54 @@
}
}
-void ListCommand::addLine(TextTable *table, const TableEntry &entry) {
- addLine(table, 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, ";"));
-}
-
-void ListCommand::dumpTable() {
+void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
if (mNeat) {
- TextTable textTable;
- forEachTable([this, &textTable](const Table &table) {
- for (const auto &entry : table) addLine(&textTable, entry);
- });
- textTable.dump(mOut.buf());
+ MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
+ .createTextTable().dump(out.buf());
return;
}
- 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, &out](const Table &table) {
- forEachTable([this](const Table &table) {
- TextTable textTable;
-
- textTable.add(table.description);
- addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD",
- "PTR", "Clients", "Clients CMD");
-
- for (const auto &entry : table) {
- addLine(&textTable, entry);
- // 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) {
- std::stringstream out;
- auto pair = splitFirst(entry.interfaceName, '/');
- mLshal.emitDebugInfo(pair.first, pair.second, {}, out,
+ // 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));
- textTable.add(out.str());
- }
+ return ss.str();
+ };
}
-
- // Add empty line after each table
- textTable.add();
-
- textTable.dump(mOut.buf());
+ 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) {
@@ -486,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));
}
}
@@ -506,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) {
@@ -517,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;
}
@@ -540,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;
}
@@ -555,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;
@@ -567,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;
}
@@ -654,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);
@@ -664,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);
@@ -672,148 +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.push_back(TableColumnType::INTERFACE_NAME);
- break;
- }
- case 't': {
- mSelectedColumns.push_back(TableColumnType::TRANSPORT);
- break;
- }
- case 'r': {
- mSelectedColumns.push_back(TableColumnType::ARCH);
- break;
- }
- case 'p': {
- mSelectedColumns.push_back(TableColumnType::SERVER_PID);
- break;
- }
- case 'a': {
- mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
- break;
- }
- case 'c': {
- mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
- break;
- }
- case 'e': {
- mSelectedColumns.push_back(TableColumnType::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;
- mLshal.usage(command);
+ err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl;
return USAGE;
}
if (mNeat && mEmitDebugInfo) {
- mErr << "Error: --neat should not be used with --debug." << std::endl;
- mLshal.usage(command);
+ err() << "Error: --neat should not be used with --debug." << std::endl;
return USAGE;
}
if (mSelectedColumns.empty()) {
- mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
+ 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 176d5b9..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,6 +27,7 @@
#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"
@@ -36,34 +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;
+ // 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
@@ -72,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;
- std::vector<TableColumnType> mSelectedColumns;
- // 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.
@@ -100,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 ac74775..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,78 +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 -iepc`)\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>]] [--neat]\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. Cannot be used with --neat.\n"
- " --sort=i, --sort=interface: sort by interface name\n"
- " --sort=p, --sort=pid: sort by server pid\n"
- " --neat: output is machine parsable (no explanatory text)\n"
- " Cannot be used with --debug.\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.
@@ -188,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;
}
@@ -218,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..9f8eeaa 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,8 +53,14 @@
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;
@@ -59,6 +69,8 @@
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/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 f18f38a..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,34 +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 class TableColumnType : unsigned int {
- INTERFACE_NAME,
- TRANSPORT,
- SERVER_PID,
- SERVER_ADDR,
- CLIENT_PIDS,
- ARCH,
- THREADS,
-};
-
-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
index a35917c..eca9061 100644
--- a/cmds/lshal/TextTable.cpp
+++ b/cmds/lshal/TextTable.cpp
@@ -53,5 +53,15 @@
}
}
+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
index 4636f15..91d522a 100644
--- a/cmds/lshal/TextTable.h
+++ b/cmds/lshal/TextTable.h
@@ -21,8 +21,6 @@
#include <string>
#include <vector>
-#include "TableEntry.h"
-
namespace android {
namespace lshal {
@@ -68,6 +66,8 @@
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;
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/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 31cd0cb..6b340a8 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -138,6 +138,7 @@
uint32_t handle;
struct binder_death death;
int allow_isolated;
+ uint32_t dumpsys_priority;
size_t len;
uint16_t name[0];
};
@@ -198,11 +199,8 @@
return si->handle;
}
-int do_add_service(struct binder_state *bs,
- const uint16_t *s, size_t len,
- uint32_t handle, uid_t uid, int allow_isolated,
- pid_t spid)
-{
+int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
+ uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
struct svcinfo *si;
//ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
@@ -239,6 +237,7 @@
si->death.func = (void*) svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
+ si->dumpsys_priority = dumpsys_priority;
si->next = svclist;
svclist = si;
}
@@ -259,6 +258,7 @@
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
+ uint32_t dumpsys_priority;
//ALOGI("target=%p code=%d pid=%d uid=%d\n",
// (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);
@@ -317,13 +317,15 @@
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
- if (do_add_service(bs, s, len, handle, txn->sender_euid,
- allow_isolated, txn->sender_pid))
+ dumpsys_priority = bio_get_uint32(msg);
+ if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
+ txn->sender_pid))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
uint32_t n = bio_get_uint32(msg);
+ uint32_t req_dumpsys_priority = bio_get_uint32(msg);
if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
@@ -331,8 +333,15 @@
return -1;
}
si = svclist;
- while ((n-- > 0) && si)
+ // walk through the list of services n times skipping services that
+ // do not support the requested priority
+ while (si) {
+ if (si->dumpsys_priority & req_dumpsys_priority) {
+ if (n == 0) break;
+ n--;
+ }
si = si->next;
+ }
if (si) {
bio_put_string16(reply, si->name);
return 0;
diff --git a/data/etc/android.hardware.wifi.rtt.xml b/data/etc/android.hardware.wifi.rtt.xml
new file mode 100644
index 0000000..60529ea
--- /dev/null
+++ b/data/etc/android.hardware.wifi.rtt.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device supports WiFi RTT (IEEE 802.11mc). -->
+<permissions>
+ <feature name="android.hardware.wifi.rtt" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 835504f..561f5ba 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -41,7 +41,6 @@
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
- <feature name="android.software.input_methods" />
<feature name="android.software.print" />
<!-- Feature to specify if the device supports adding device admins. -->
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 0d5d206..ec7be53 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -45,7 +45,7 @@
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
- <feature name="android.software.picture_in_picture" />
+ <feature name="android.software.picture_in_picture" notLowRam="true" />
<feature name="android.software.activities_on_secondary_displays" />
<feature name="android.software.print" />
<feature name="android.software.companion_device_setup" />
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index e202060..2164d61 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -765,7 +765,9 @@
/** fingerprint navigation key, left. */
AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282,
/** fingerprint navigation key, right. */
- AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283
+ AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283,
+ /** all apps */
+ AKEYCODE_ALL_APPS = 284
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h
deleted file mode 100644
index de5c1c7..0000000
--- a/include/audiomanager/IPlayer.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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_IPLAYER_H
-#define ANDROID_IPLAYER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <media/VolumeShaper.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IPlayer : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(Player);
-
- virtual void start() = 0;
-
- virtual void pause() = 0;
-
- virtual void stop() = 0;
-
- virtual void setVolume(float vol) = 0;
-
- virtual void setPan(float pan) = 0;
-
- virtual void setStartDelayMs(int delayMs) = 0;
-
- virtual void applyVolumeShaper(
- const sp<VolumeShaper::Configuration>& configuration,
- const sp<VolumeShaper::Operation>& operation) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnPlayer : public BnInterface<IPlayer>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPLAYER_H
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 20154eb..c282cf0 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -323,6 +323,7 @@
DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN),
DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT),
DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
+ DEFINE_KEYCODE(ALL_APPS),
{ NULL, 0 }
};
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 0b390c5..1c3fab4 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -686,7 +686,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/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index c7a0f43..70f5108 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -161,19 +161,18 @@
}
virtual status_t addService(const String16& name, const sp<IBinder>& service,
- bool allowIsolated)
- {
+ bool allowIsolated, int dumpsysPriority) {
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
+ data.writeInt32(dumpsysPriority);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
- virtual Vector<String16> listServices()
- {
+ virtual Vector<String16> listServices(int dumpsysPriority) {
Vector<String16> res;
int n = 0;
@@ -181,6 +180,7 @@
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeInt32(n++);
+ data.writeInt32(dumpsysPriority);
status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);
if (err != NO_ERROR)
break;
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 6b7254c..3264666 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -38,4 +38,20 @@
* strings.
*/
@utf8InCpp String[] getNamesForUids(in int[] uids);
+
+ /**
+ * Returns the name of the installer (a package) which installed the named
+ * package. Preloaded packages return the string "preload". Sideloaded packages
+ * return an empty string. Unknown or unknowable are returned as empty strings.
+ */
+
+ @utf8InCpp String getInstallerForPackage(in String packageName);
+
+ /**
+ * Returns the version code of the named package.
+ * Unknown or unknowable versions are returned as 0.
+ */
+
+ int getVersionCodeForPackage(in String packageName);
+
}
diff --git a/libs/binder/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
index ef703bd..4e69067 100644
--- a/libs/binder/include/binder/BinderService.h
+++ b/libs/binder/include/binder/BinderService.h
@@ -34,15 +34,16 @@
class BinderService
{
public:
- static status_t publish(bool allowIsolated = false) {
+ static status_t publish(bool allowIsolated = false,
+ int dumpPriority = IServiceManager::DUMP_PRIORITY_NORMAL) {
sp<IServiceManager> sm(defaultServiceManager());
- return sm->addService(
- String16(SERVICE::getServiceName()),
- new SERVICE(), allowIsolated);
+ return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
+ dumpPriority);
}
- static void publishAndJoinThreadPool(bool allowIsolated = false) {
- publish(allowIsolated);
+ static void publishAndJoinThreadPool(bool allowIsolated = false,
+ int dumpPriority = IServiceManager::DUMP_PRIORITY_NORMAL) {
+ publish(allowIsolated, dumpPriority);
joinThreadPool();
}
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 3b23f81..78b03bd 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -31,6 +31,14 @@
{
public:
DECLARE_META_INTERFACE(ServiceManager)
+ /*
+ * Must match values in IServiceManager.java
+ */
+ static const int DUMP_PRIORITY_CRITICAL = 1 << 0;
+ static const int DUMP_PRIORITY_HIGH = 1 << 1;
+ static const int DUMP_PRIORITY_NORMAL = 1 << 2;
+ static const int DUMP_PRIORITY_ALL =
+ DUMP_PRIORITY_CRITICAL | DUMP_PRIORITY_HIGH | DUMP_PRIORITY_NORMAL;
/**
* Retrieve an existing service, blocking for a few seconds
@@ -46,14 +54,14 @@
/**
* Register a service.
*/
- virtual status_t addService( const String16& name,
- const sp<IBinder>& service,
- bool allowIsolated = false) = 0;
+ virtual status_t addService(const String16& name, const sp<IBinder>& service,
+ bool allowIsolated = false,
+ int dumpsysPriority = DUMP_PRIORITY_NORMAL) = 0;
/**
* Return list of all existing services.
*/
- virtual Vector<String16> listServices() = 0;
+ virtual Vector<String16> listServices(int dumpsysPriority = DUMP_PRIORITY_ALL) = 0;
enum {
GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 455f2c4..bf41e0b 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -215,7 +215,7 @@
int target = cs_pair ? num % server_count : rand() % workers.size();
int sz = payload_size;
- while (sz > sizeof(uint32_t)) {
+ while (sz >= sizeof(uint32_t)) {
data.writeInt32(0);
sz -= sizeof(uint32_t);
}
@@ -381,6 +381,7 @@
// No need to run training round in this case.
if (atoi(argv[i+1]) > 0) {
max_time_bucket = strtoull(argv[i+1], (char **)NULL, 10) * 1000;
+ time_per_bucket = max_time_bucket / num_buckets;
i++;
} else {
cout << "Max latency -m must be positive." << endl;
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index 57ddde0..d3dc16d 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -43,7 +43,10 @@
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->writeFloat(mColor.r));
+ RETURN_ON_ERROR(parcel->writeFloat(mColor.g));
+ RETURN_ON_ERROR(parcel->writeFloat(mColor.b));
+ RETURN_ON_ERROR(parcel->writeFloat(mColor.a));
RETURN_ON_ERROR(parcel->writeUint32(mFlags));
RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat));
RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace)));
@@ -79,7 +82,14 @@
RETURN_ON_ERROR(parcel->readInt32(&mHeight));
RETURN_ON_ERROR(parcel->read(mCrop));
RETURN_ON_ERROR(parcel->read(mFinalCrop));
- RETURN_ON_ERROR(parcel->readFloat(&mAlpha));
+ mColor.r = parcel->readFloat();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mColor.g = parcel->readFloat();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mColor.b = parcel->readFloat();
+ RETURN_ON_ERROR(parcel->errorCheck());
+ mColor.a = parcel->readFloat();
+ RETURN_ON_ERROR(parcel->errorCheck());
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?
@@ -116,8 +126,10 @@
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("color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
+ static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
+ static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
+ 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]));
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 573f685..fcee73f 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -46,7 +46,9 @@
output.writeStrongBinder(IInterface::asBinder(barrierGbp));
output.writeStrongBinder(relativeLayerHandle);
output.writeStrongBinder(parentHandleForChild);
- output.writeStrongBinder(childHandle);
+ output.writeFloat(color.r);
+ output.writeFloat(color.g);
+ output.writeFloat(color.b);
output.write(transparentRegion);
return NO_ERROR;
}
@@ -80,7 +82,9 @@
interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
relativeLayerHandle = input.readStrongBinder();
parentHandleForChild = input.readStrongBinder();
- childHandle = input.readStrongBinder();
+ color.r = input.readFloat();
+ color.g = input.readFloat();
+ color.b = input.readFloat();
input.read(transparentRegion);
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b0ae7e0..c5a4389 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -158,6 +158,8 @@
const Region& transparentRegion);
status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float alpha);
+ status_t setColor(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+ const half3& color);
status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float dsdx, float dtdx, float dtdy, float dsdy);
status_t setOrientation(int orientation);
@@ -176,9 +178,8 @@
status_t reparentChildren(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id,
const sp<IBinder>& newParentHandle);
- status_t reparentChild(const sp<SurfaceComposerClient>& client,
- const sp<IBinder>& id, const sp<IBinder>& newParentHandle,
- const sp<IBinder>& childHandle);
+ status_t reparent(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const sp<IBinder>& newParentHandle);
status_t detachChildren(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
@@ -403,6 +404,17 @@
return NO_ERROR;
}
+status_t Composer::setColor(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const half3& color) {
+ Mutex::Autolock _l(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s)
+ return BAD_INDEX;
+ s->what |= layer_state_t::eColorChanged;
+ s->color = color;
+ return NO_ERROR;
+}
+
status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, uint32_t layerStack) {
Mutex::Autolock _l(mLock);
@@ -496,18 +508,16 @@
return NO_ERROR;
}
-status_t Composer::reparentChild(const sp<SurfaceComposerClient>& client,
+status_t Composer::reparent(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id,
- const sp<IBinder>& newParentHandle,
- const sp<IBinder>& childHandle) {
+ const sp<IBinder>& newParentHandle) {
Mutex::Autolock lock(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s) {
return BAD_INDEX;
}
- s->what |= layer_state_t::eReparentChild;
+ s->what |= layer_state_t::eReparent;
s->parentHandleForChild = newParentHandle;
- s->childHandle = childHandle;
return NO_ERROR;
}
@@ -825,6 +835,10 @@
return getComposer().setAlpha(this, id, alpha);
}
+status_t SurfaceComposerClient::setColor(const sp<IBinder>& id, const half3& color) {
+ return getComposer().setColor(this, id, color);
+}
+
status_t SurfaceComposerClient::setLayerStack(const sp<IBinder>& id, uint32_t layerStack) {
return getComposer().setLayerStack(this, id, layerStack);
}
@@ -849,9 +863,9 @@
return getComposer().reparentChildren(this, id, newParentHandle);
}
-status_t SurfaceComposerClient::reparentChild(const sp<IBinder>& id,
- const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle) {
- return getComposer().reparentChild(this, id, newParentHandle, childHandle);
+status_t SurfaceComposerClient::reparent(const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle) {
+ return getComposer().reparent(this, id, newParentHandle);
}
status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) {
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index b9c5ef9..9e1d7b6 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -155,6 +155,11 @@
if (err < 0) return err;
return mClient->setAlpha(mHandle, alpha);
}
+status_t SurfaceControl::setColor(const half3& color) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->setColor(mHandle, color);
+}
status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
status_t err = validate();
if (err < 0) return err;
@@ -191,11 +196,10 @@
return mClient->reparentChildren(mHandle, newParentHandle);
}
-status_t SurfaceControl::reparentChild(const sp<IBinder>& newParentHandle,
- const sp<IBinder>& childHandle) {
+status_t SurfaceControl::reparent(const sp<IBinder>& newParentHandle) {
status_t err = validate();
if (err < 0) return err;
- return mClient->reparentChild(mHandle, newParentHandle, childHandle);
+ return mClient->reparent(mHandle, newParentHandle);
}
status_t SurfaceControl::detachChildren() {
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 2c613ea..d5bbef2 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -41,7 +41,7 @@
eCursorWindow = 0x00002000,
eFXSurfaceNormal = 0x00000000,
- eFXSurfaceDim = 0x00020000,
+ eFXSurfaceColor = 0x00020000,
eFXSurfaceMask = 0x000F0000,
};
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
index 8453e04..92bd8c5 100644
--- a/libs/gui/include/gui/LayerDebugInfo.h
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -22,6 +22,7 @@
#include <ui/Region.h>
#include <string>
+#include <math/vec4.h>
namespace android {
@@ -52,7 +53,7 @@
int32_t mHeight = -1;
Rect mCrop = Rect::INVALID_RECT;
Rect mFinalCrop = Rect::INVALID_RECT;
- float mAlpha = 0.f;
+ half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf);
uint32_t mFlags = 0;
PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6e2cb83..1718143 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -32,6 +32,7 @@
#include <gui/CpuConsumer.h>
#include <gui/SurfaceControl.h>
+#include <math/vec3.h>
namespace android {
@@ -149,6 +150,7 @@
status_t setRelativeLayer(const sp<IBinder>& id,
const sp<IBinder>& relativeTo, int32_t layer);
status_t setAlpha(const sp<IBinder>& id, float alpha=1.0f);
+ status_t setColor(const sp<IBinder>& id, const half3& color);
status_t setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, float dtdy, float dsdy);
status_t setPosition(const sp<IBinder>& id, float x, float y);
status_t setSize(const sp<IBinder>& id, uint32_t w, uint32_t h);
@@ -161,8 +163,7 @@
const sp<Surface>& handle, uint64_t frameNumber);
status_t reparentChildren(const sp<IBinder>& id,
const sp<IBinder>& newParentHandle);
- status_t reparentChild(const sp<IBinder>& id, const sp<IBinder>& newParentHandle,
- const sp<IBinder>& childHandle);
+ status_t reparent(const sp<IBinder>& id, const sp<IBinder>& newParentHandle);
status_t detachChildren(const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<IBinder>& id,
int32_t overrideScalingMode);
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index d8b67ef..e98e26a 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -29,6 +29,7 @@
#include <ui/Region.h>
#include <gui/ISurfaceComposerClient.h>
+#include <math/vec3.h>
namespace android {
@@ -90,6 +91,7 @@
status_t setFlags(uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(const Region& transparent);
status_t setAlpha(float alpha=1.0f);
+ status_t setColor(const half3& color);
// Experimentarily it appears that the matrix transforms the
// on-screen rectangle and it's contents before the position is
@@ -124,11 +126,10 @@
// Reparents all children of this layer to the new parent handle.
status_t reparentChildren(const sp<IBinder>& newParentHandle);
- // Reparents a specified child from this layer to the new parent handle.
- // The child, parent, and new parent must all have the same client.
+ // Reparents the current layer to the new parent handle. The new parent must not be null.
// This can be used instead of reparentChildren if the caller wants to
- // only re-parent specific children.
- status_t reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle);
+ // only re-parent a specific child.
+ status_t reparent(const sp<IBinder>& newParentHandle);
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
diff --git a/libs/gui/include/private/gui/LayerState.h b/libs/gui/include/private/gui/LayerState.h
index 4f73e04..bd42634 100644
--- a/libs/gui/include/private/gui/LayerState.h
+++ b/libs/gui/include/private/gui/LayerState.h
@@ -25,6 +25,7 @@
#include <ui/Region.h>
#include <ui/Rect.h>
#include <gui/IGraphicBufferProducer.h>
+#include <math/vec3.h>
namespace android {
@@ -60,7 +61,8 @@
eReparentChildren = 0x00002000,
eDetachChildren = 0x00004000,
eRelativeLayerChanged = 0x00008000,
- eReparentChild = 0x00010000
+ eReparent = 0x00010000,
+ eColorChanged = 0x00020000
};
layer_state_t()
@@ -109,7 +111,8 @@
sp<IBinder> relativeLayerHandle;
sp<IBinder> parentHandleForChild;
- sp<IBinder> childHandle;
+
+ half3 color;
// non POD must be last. see write/read
Region transparentRegion;
diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
index 8c6ef69..77f06bb 100644
--- a/libs/hwc2on1adapter/HWC2On1Adapter.cpp
+++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
@@ -426,7 +426,13 @@
std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
- mCallbacks[descriptor] = {callbackData, pointer};
+ if (pointer != nullptr) {
+ mCallbacks[descriptor] = {callbackData, pointer};
+ } else {
+ ALOGI("unregisterCallback(%s)", to_string(descriptor).c_str());
+ mCallbacks.erase(descriptor);
+ return Error::None;
+ }
bool hasPendingInvalidate = false;
std::vector<hwc2_display_t> displayIds;
@@ -2005,10 +2011,21 @@
return Error::None;
}
+static bool compareRects(const hwc_rect_t& rect1, const hwc_rect_t& rect2) {
+ return rect1.left == rect2.left &&
+ rect1.right == rect2.right &&
+ rect1.top == rect2.top &&
+ rect1.bottom == rect2.bottom;
+}
+
Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) {
- mVisibleRegion.resize(visible.numRects);
- std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
- mDisplay.markGeometryChanged();
+ if ((getNumVisibleRegions() != visible.numRects) ||
+ !std::equal(mVisibleRegion.begin(), mVisibleRegion.end(), visible.rects,
+ compareRects)) {
+ mVisibleRegion.resize(visible.numRects);
+ std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
+ mDisplay.markGeometryChanged();
+ }
return Error::None;
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 9abd04c..b8901bd 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -254,7 +254,7 @@
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%lld, eventTime=%lld",
+ "downTime=%" PRId64 ", eventTime=%" PRId64,
mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
@@ -305,7 +305,7 @@
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, "
- "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
+ "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32,
mChannel->getName().string(), seq,
deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState,
@@ -401,7 +401,7 @@
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
int32_t* displayId) {
#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld",
+ ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime);
#endif
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index dd18c54..e54f147 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -75,7 +75,9 @@
str += " ]";
return str;
}
+#endif
+#if DEBUG_STRATEGY
static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
std::string str;
str = "[";
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 029a420..e3e1dec 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -6,6 +6,7 @@
"InputChannel_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
+ "VelocityTracker_test.cpp",
],
shared_libs: [
"libinput",
@@ -13,6 +14,7 @@
"libutils",
"libbinder",
"libui",
+ "libbase",
]
}
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
new file mode 100644
index 0000000..43b6012
--- /dev/null
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -0,0 +1,664 @@
+/*
+ * 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 "VelocityTracker_test"
+
+#include <math.h>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <input/VelocityTracker.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests
+
+// velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV
+// here EV = expected value, tol = VELOCITY_TOLERANCE
+constexpr float VELOCITY_TOLERANCE = 0.2;
+
+// --- VelocityTrackerTest ---
+class VelocityTrackerTest : public testing::Test { };
+
+static void checkVelocity(float Vactual, float Vtarget) {
+ // Compare directions
+ if ((Vactual > 0 && Vtarget <= 0) || (Vactual < 0 && Vtarget >= 0)) {
+ FAIL() << StringPrintf("Velocity %f does not have the same direction"
+ " as the target velocity %f", Vactual, Vtarget);
+ }
+
+ // Compare magnitudes
+ const float Vlower = fabsf(Vtarget * (1 - VELOCITY_TOLERANCE));
+ const float Vupper = fabsf(Vtarget * (1 + VELOCITY_TOLERANCE));
+ if (fabsf(Vactual) < Vlower) {
+ FAIL() << StringPrintf("Velocity %f is more than %.0f%% below target velocity %f",
+ Vactual, VELOCITY_TOLERANCE * 100, Vtarget);
+ }
+ if (fabsf(Vactual) > Vupper) {
+ FAIL() << StringPrintf("Velocity %f is more than %.0f%% above target velocity %f",
+ Vactual, VELOCITY_TOLERANCE * 100, Vtarget);
+ }
+ SUCCEED() << StringPrintf("Velocity %f within %.0f%% of target %f)",
+ Vactual, VELOCITY_TOLERANCE * 100, Vtarget);
+}
+
+void failWithMessage(std::string message) {
+ FAIL() << message; // cannot do this directly from a non-void function
+}
+
+struct Position {
+ nsecs_t time;
+ float x;
+ float y;
+};
+
+
+MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSamples) {
+ /**
+ * Only populate the basic fields of a MotionEvent, such as time and a single axis
+ * Designed for use with manually-defined tests.
+ * Create a new MotionEvent on the heap, caller responsible for destroying the object.
+ */
+ if (numSamples < 1) {
+ failWithMessage(StringPrintf("Need at least 1 sample to create a MotionEvent."
+ " Received numSamples=%zu", numSamples));
+ }
+
+ MotionEvent* event = new MotionEvent();
+ PointerCoords coords;
+ PointerProperties properties[1];
+
+ properties[0].id = DEFAULT_POINTER_ID;
+ properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ // First sample added separately with initialize
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
+ event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords);
+
+ for (size_t i = 1; i < numSamples; i++) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
+ event->addSample(positions[i].time, &coords);
+ }
+ return event;
+}
+
+static void computeAndCheckVelocity(const Position* positions, size_t numSamples,
+ int32_t axis, float targetVelocity) {
+ VelocityTracker vt(nullptr);
+ float Vx, Vy;
+
+ MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
+ vt.addMovement(event);
+
+ vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy);
+
+ switch (axis) {
+ case AMOTION_EVENT_AXIS_X:
+ checkVelocity(Vx, targetVelocity);
+ break;
+ case AMOTION_EVENT_AXIS_Y:
+ checkVelocity(Vy, targetVelocity);
+ break;
+ default:
+ FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
+ }
+ delete event;
+}
+
+/*
+ * ================== VelocityTracker tests generated manually =====================================
+ */
+ // @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy
+TEST_F(VelocityTrackerTest, DISABLED_ThreePointsPositiveVelocityTest) {
+ // Same coordinate is reported 2 times in a row
+ // It is difficult to determine the correct answer here, but at least the direction
+ // of the reported velocity should be positive.
+ Position values[] = {
+ { 0, 273, NAN },
+ { 12585000, 293, NAN },
+ { 14730000, 293, NAN },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1600);
+}
+
+TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
+ // Same coordinate is reported 3 times in a row
+ Position values[] = {
+ { 0, 293, NAN },
+ { 6132000, 293, NAN },
+ { 11283000, 293, NAN },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 0);
+}
+
+TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
+ // Fixed velocity at 5 points per 10 milliseconds
+ Position values[] = {
+ { 0, 0, NAN },
+ { 10000000, 5, NAN },
+ { 20000000, 10, NAN },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 500);
+}
+
+
+/**
+ * ================== VelocityTracker tests generated by recording real events =====================
+ *
+ * To add a test, record the input coordinates and event times to all calls
+ * to void VelocityTracker::addMovement(const MotionEvent* event).
+ * Also record all calls to VelocityTracker::clear().
+ * Finally, record the output of VelocityTracker::getVelocity(...)
+ * This will give you the necessary data to create a new test.
+ */
+
+// --------------- Recorded by hand on swordfish ---------------------------------------------------
+// @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy
+TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) {
+ // Recording of a fling on Swordfish that could cause a fling in the wrong direction
+ Position values[] = {
+ { 0, 271, 96 },
+ { 16071042, 269.786346, 106.922775 },
+ { 35648403, 267.983063, 156.660034 },
+ { 52313925, 262.638397, 220.339081 },
+ { 68976522, 266.138824, 331.581116 },
+ { 85639375, 274.79245, 428.113159 },
+ { 96948871, 274.79245, 428.113159 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 623.577637);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 8523.348633);
+}
+
+// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
+// For some of these tests, the X-direction velocity checking has been removed, because the lsq2
+// and the impulse VelocityTrackerStrategies did not agree within 20%.
+// Since the flings were recorded in the Y-direction, the intentional user action should only
+// be relevant for the Y axis.
+// There have been also cases where lsq2 and impulse disagreed more than 20% in the Y-direction.
+// Those recordings have been discarded because we didn't feel one strategy's interpretation was
+// more correct than another's but didn't want to increase the tolerance for the entire test suite.
+//
+// There are 18 tests total below: 9 in the positive Y direction and 9 in the opposite.
+// The recordings were loosely binned into 3 categories - slow, faster, and fast, which roughly
+// characterizes the velocity of the finger motion.
+// These can be treated approximately as:
+// slow - less than 1 page gets scrolled
+// faster - more than 1 page gets scrolled, but less than 3
+// fast - entire list is scrolled (fling is done as hard as possible)
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
+ // Sailfish - fling up - slow - 1
+ Position values[] = {
+ { 235089067457000, 528.00, 983.00 },
+ { 235089084684000, 527.00, 981.00 },
+ { 235089093349000, 527.00, 977.00 },
+ { 235089095677625, 527.00, 975.93 },
+ { 235089101859000, 527.00, 970.00 },
+ { 235089110378000, 528.00, 960.00 },
+ { 235089112497111, 528.25, 957.51 },
+ { 235089118760000, 531.00, 946.00 },
+ { 235089126686000, 535.00, 931.00 },
+ { 235089129316820, 536.33, 926.02 },
+ { 235089135199000, 540.00, 914.00 },
+ { 235089144297000, 546.00, 896.00 },
+ { 235089146136443, 547.21, 892.36 },
+ { 235089152923000, 553.00, 877.00 },
+ { 235089160784000, 559.00, 851.00 },
+ { 235089162955851, 560.66, 843.82 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) {
+ // Sailfish - fling up - slow - 2
+ Position values[] = {
+ { 235110560704000, 522.00, 1107.00 },
+ { 235110575764000, 522.00, 1107.00 },
+ { 235110584385000, 522.00, 1107.00 },
+ { 235110588421179, 521.52, 1106.52 },
+ { 235110592830000, 521.00, 1106.00 },
+ { 235110601385000, 520.00, 1104.00 },
+ { 235110605088160, 519.14, 1102.27 },
+ { 235110609952000, 518.00, 1100.00 },
+ { 235110618353000, 517.00, 1093.00 },
+ { 235110621755146, 516.60, 1090.17 },
+ { 235110627010000, 517.00, 1081.00 },
+ { 235110634785000, 518.00, 1063.00 },
+ { 235110638422450, 518.87, 1052.58 },
+ { 235110643161000, 520.00, 1039.00 },
+ { 235110651767000, 524.00, 1011.00 },
+ { 235110655089581, 525.54, 1000.19 },
+ { 235110660368000, 530.00, 980.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) {
+ // Sailfish - fling up - slow - 3
+ Position values[] = {
+ { 792536237000, 580.00, 1317.00 },
+ { 792541538987, 580.63, 1311.94 },
+ { 792544613000, 581.00, 1309.00 },
+ { 792552301000, 583.00, 1295.00 },
+ { 792558362309, 585.13, 1282.92 },
+ { 792560828000, 586.00, 1278.00 },
+ { 792569446000, 589.00, 1256.00 },
+ { 792575185095, 591.54, 1241.41 },
+ { 792578491000, 593.00, 1233.00 },
+ { 792587044000, 597.00, 1211.00 },
+ { 792592008172, 600.28, 1195.92 },
+ { 792594616000, 602.00, 1188.00 },
+ { 792603129000, 607.00, 1167.00 },
+ { 792608831290, 609.48, 1155.83 },
+ { 792612321000, 611.00, 1149.00 },
+ { 792620768000, 615.00, 1131.00 },
+ { 792625653873, 617.32, 1121.73 },
+ { 792629200000, 619.00, 1115.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) {
+ // Sailfish - fling up - faster - 1
+ Position values[] = {
+ { 235160420675000, 610.00, 1042.00 },
+ { 235160428220000, 609.00, 1026.00 },
+ { 235160436544000, 609.00, 1024.00 },
+ { 235160441852394, 609.64, 1020.82 },
+ { 235160444878000, 610.00, 1019.00 },
+ { 235160452673000, 613.00, 1006.00 },
+ { 235160458519743, 617.18, 992.06 },
+ { 235160461061000, 619.00, 986.00 },
+ { 235160469798000, 627.00, 960.00 },
+ { 235160475186713, 632.22, 943.02 },
+ { 235160478051000, 635.00, 934.00 },
+ { 235160486489000, 644.00, 906.00 },
+ { 235160491853697, 649.56, 890.56 },
+ { 235160495177000, 653.00, 881.00 },
+ { 235160504148000, 662.00, 858.00 },
+ { 235160509231495, 666.81, 845.37 },
+ { 235160512603000, 670.00, 837.00 },
+ { 235160520366000, 679.00, 814.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) {
+ // Sailfish - fling up - faster - 2
+ Position values[] = {
+ { 847153808000, 576.00, 1264.00 },
+ { 847171174000, 576.00, 1262.00 },
+ { 847179640000, 576.00, 1257.00 },
+ { 847185187540, 577.41, 1249.22 },
+ { 847187487000, 578.00, 1246.00 },
+ { 847195710000, 581.00, 1227.00 },
+ { 847202027059, 583.93, 1209.40 },
+ { 847204324000, 585.00, 1203.00 },
+ { 847212672000, 590.00, 1176.00 },
+ { 847218861395, 594.36, 1157.11 },
+ { 847221190000, 596.00, 1150.00 },
+ { 847230484000, 602.00, 1124.00 },
+ { 847235701400, 607.56, 1103.83 },
+ { 847237986000, 610.00, 1095.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) {
+ // Sailfish - fling up - faster - 3
+ Position values[] = {
+ { 235200532789000, 507.00, 1084.00 },
+ { 235200549221000, 507.00, 1083.00 },
+ { 235200557841000, 507.00, 1081.00 },
+ { 235200558051189, 507.00, 1080.95 },
+ { 235200566314000, 507.00, 1078.00 },
+ { 235200574876586, 508.97, 1070.12 },
+ { 235200575006000, 509.00, 1070.00 },
+ { 235200582900000, 514.00, 1054.00 },
+ { 235200591276000, 525.00, 1023.00 },
+ { 235200591701829, 525.56, 1021.42 },
+ { 235200600064000, 542.00, 976.00 },
+ { 235200608519000, 563.00, 911.00 },
+ { 235200608527086, 563.02, 910.94 },
+ { 235200616933000, 590.00, 844.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) {
+ // Sailfish - fling up - fast - 1
+ Position values[] = {
+ { 920922149000, 561.00, 1412.00 },
+ { 920930185000, 559.00, 1377.00 },
+ { 920930262463, 558.98, 1376.66 },
+ { 920938547000, 559.00, 1371.00 },
+ { 920947096857, 562.91, 1342.68 },
+ { 920947302000, 563.00, 1342.00 },
+ { 920955502000, 577.00, 1272.00 },
+ { 920963931021, 596.87, 1190.54 },
+ { 920963987000, 597.00, 1190.00 },
+ { 920972530000, 631.00, 1093.00 },
+ { 920980765511, 671.31, 994.68 },
+ { 920980906000, 672.00, 993.00 },
+ { 920989261000, 715.00, 903.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) {
+ // Sailfish - fling up - fast - 2
+ Position values[] = {
+ { 235247153233000, 518.00, 1168.00 },
+ { 235247170452000, 517.00, 1167.00 },
+ { 235247178908000, 515.00, 1159.00 },
+ { 235247179556213, 514.85, 1158.39 },
+ { 235247186821000, 515.00, 1125.00 },
+ { 235247195265000, 521.00, 1051.00 },
+ { 235247196389476, 521.80, 1041.15 },
+ { 235247203649000, 538.00, 932.00 },
+ { 235247212253000, 571.00, 794.00 },
+ { 235247213222491, 574.72, 778.45 },
+ { 235247220736000, 620.00, 641.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) {
+ // Sailfish - fling up - fast - 3
+ Position values[] = {
+ { 235302568736000, 529.00, 1167.00 },
+ { 235302576644000, 523.00, 1140.00 },
+ { 235302579395063, 520.91, 1130.61 },
+ { 235302585140000, 522.00, 1130.00 },
+ { 235302593615000, 527.00, 1065.00 },
+ { 235302596207444, 528.53, 1045.12 },
+ { 235302602102000, 559.00, 872.00 },
+ { 235302610545000, 652.00, 605.00 },
+ { 235302613019881, 679.26, 526.73 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) {
+ // Sailfish - fling down - slow - 1
+ Position values[] = {
+ { 235655749552755, 582.00, 432.49 },
+ { 235655750638000, 582.00, 433.00 },
+ { 235655758865000, 582.00, 440.00 },
+ { 235655766221523, 581.16, 448.43 },
+ { 235655767594000, 581.00, 450.00 },
+ { 235655776044000, 580.00, 462.00 },
+ { 235655782890696, 579.18, 474.35 },
+ { 235655784360000, 579.00, 477.00 },
+ { 235655792795000, 578.00, 496.00 },
+ { 235655799559531, 576.27, 515.04 },
+ { 235655800612000, 576.00, 518.00 },
+ { 235655809535000, 574.00, 542.00 },
+ { 235655816988015, 572.17, 564.86 },
+ { 235655817685000, 572.00, 567.00 },
+ { 235655825981000, 569.00, 595.00 },
+ { 235655833808653, 566.26, 620.60 },
+ { 235655834541000, 566.00, 623.00 },
+ { 235655842893000, 563.00, 649.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) {
+ // Sailfish - fling down - slow - 2
+ Position values[] = {
+ { 235671152083370, 485.24, 558.28 },
+ { 235671154126000, 485.00, 559.00 },
+ { 235671162497000, 484.00, 566.00 },
+ { 235671168750511, 483.27, 573.29 },
+ { 235671171071000, 483.00, 576.00 },
+ { 235671179390000, 482.00, 588.00 },
+ { 235671185417210, 481.31, 598.98 },
+ { 235671188173000, 481.00, 604.00 },
+ { 235671196371000, 480.00, 624.00 },
+ { 235671202084196, 479.27, 639.98 },
+ { 235671204235000, 479.00, 646.00 },
+ { 235671212554000, 478.00, 673.00 },
+ { 235671219471011, 476.39, 697.12 },
+ { 235671221159000, 476.00, 703.00 },
+ { 235671229592000, 474.00, 734.00 },
+ { 235671236281462, 472.43, 758.38 },
+ { 235671238098000, 472.00, 765.00 },
+ { 235671246532000, 470.00, 799.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) {
+ // Sailfish - fling down - slow - 3
+ Position values[] = {
+ { 170983201000, 557.00, 533.00 },
+ { 171000668000, 556.00, 534.00 },
+ { 171007359750, 554.73, 535.27 },
+ { 171011197000, 554.00, 536.00 },
+ { 171017660000, 552.00, 540.00 },
+ { 171024201831, 549.97, 544.73 },
+ { 171027333000, 549.00, 547.00 },
+ { 171034603000, 545.00, 557.00 },
+ { 171041043371, 541.98, 567.55 },
+ { 171043147000, 541.00, 571.00 },
+ { 171051052000, 536.00, 586.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) {
+ // Sailfish - fling down - faster - 1
+ Position values[] = {
+ { 235695280333000, 558.00, 451.00 },
+ { 235695283971237, 558.43, 454.45 },
+ { 235695289038000, 559.00, 462.00 },
+ { 235695297388000, 561.00, 478.00 },
+ { 235695300638465, 561.83, 486.25 },
+ { 235695305265000, 563.00, 498.00 },
+ { 235695313591000, 564.00, 521.00 },
+ { 235695317305492, 564.43, 532.68 },
+ { 235695322181000, 565.00, 548.00 },
+ { 235695330709000, 565.00, 577.00 },
+ { 235695333972227, 565.00, 588.10 },
+ { 235695339250000, 565.00, 609.00 },
+ { 235695347839000, 565.00, 642.00 },
+ { 235695351313257, 565.00, 656.18 },
+ { 235695356412000, 565.00, 677.00 },
+ { 235695364899000, 563.00, 710.00 },
+ { 235695368118682, 562.24, 722.52 },
+ { 235695373403000, 564.00, 744.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) {
+ // Sailfish - fling down - faster - 2
+ Position values[] = {
+ { 235709624766000, 535.00, 579.00 },
+ { 235709642256000, 534.00, 580.00 },
+ { 235709643350278, 533.94, 580.06 },
+ { 235709650760000, 532.00, 584.00 },
+ { 235709658615000, 530.00, 593.00 },
+ { 235709660170495, 529.60, 594.78 },
+ { 235709667095000, 527.00, 606.00 },
+ { 235709675616000, 524.00, 628.00 },
+ { 235709676983261, 523.52, 631.53 },
+ { 235709684289000, 521.00, 652.00 },
+ { 235709692763000, 518.00, 682.00 },
+ { 235709693804993, 517.63, 685.69 },
+ { 235709701438000, 515.00, 709.00 },
+ { 235709709830000, 512.00, 739.00 },
+ { 235709710626776, 511.72, 741.85 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) {
+ // Sailfish - fling down - faster - 3
+ Position values[] = {
+ { 235727628927000, 540.00, 440.00 },
+ { 235727636810000, 537.00, 454.00 },
+ { 235727646176000, 536.00, 454.00 },
+ { 235727653586628, 535.12, 456.65 },
+ { 235727654557000, 535.00, 457.00 },
+ { 235727663024000, 534.00, 465.00 },
+ { 235727670410103, 533.04, 479.45 },
+ { 235727670691000, 533.00, 480.00 },
+ { 235727679255000, 531.00, 501.00 },
+ { 235727687233704, 529.09, 526.73 },
+ { 235727687628000, 529.00, 528.00 },
+ { 235727696113000, 526.00, 558.00 },
+ { 235727704057546, 523.18, 588.98 },
+ { 235727704576000, 523.00, 591.00 },
+ { 235727713099000, 520.00, 626.00 },
+ { 235727720880776, 516.33, 655.36 },
+ { 235727721580000, 516.00, 658.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) {
+ // Sailfish - fling down - fast - 1
+ Position values[] = {
+ { 235762352849000, 467.00, 286.00 },
+ { 235762360250000, 443.00, 344.00 },
+ { 235762362787412, 434.77, 363.89 },
+ { 235762368807000, 438.00, 359.00 },
+ { 235762377220000, 425.00, 423.00 },
+ { 235762379608561, 421.31, 441.17 },
+ { 235762385698000, 412.00, 528.00 },
+ { 235762394133000, 406.00, 648.00 },
+ { 235762396429369, 404.37, 680.67 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) {
+ // Sailfish - fling down - fast - 2
+ Position values[] = {
+ { 235772487188000, 576.00, 204.00 },
+ { 235772495159000, 553.00, 236.00 },
+ { 235772503568000, 551.00, 240.00 },
+ { 235772508192247, 545.55, 254.17 },
+ { 235772512051000, 541.00, 266.00 },
+ { 235772520794000, 520.00, 337.00 },
+ { 235772525015263, 508.92, 394.43 },
+ { 235772529174000, 498.00, 451.00 },
+ { 235772537635000, 484.00, 589.00 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
+}
+
+
+TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) {
+ // Sailfish - fling down - fast - 3
+ Position values[] = {
+ { 507650295000, 628.00, 233.00 },
+ { 507658234000, 605.00, 269.00 },
+ { 507666784000, 601.00, 274.00 },
+ { 507669660483, 599.65, 275.68 },
+ { 507675427000, 582.00, 308.00 },
+ { 507683740000, 541.00, 404.00 },
+ { 507686506238, 527.36, 435.95 },
+ { 507692220000, 487.00, 581.00 },
+ { 507700707000, 454.00, 792.00 },
+ { 507703352649, 443.71, 857.77 },
+ };
+ size_t count = sizeof(values) / sizeof(Position);
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
+ computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
+}
+
+
+} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 59173cb..a2664f1 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -44,7 +44,7 @@
],
sanitize: {
- //misc_undefined: ["integer"],
+ integer_overflow: true,
},
srcs: [
@@ -71,6 +71,7 @@
shared_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
"libbase",
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 0eb08e5..1f746a2 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -39,9 +39,15 @@
Mapper::Mapper()
{
mMapper = IMapper::getService();
- if (mMapper == nullptr || mMapper->isRemote()) {
+ if (mMapper == nullptr) {
+ LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+ }
+ if (mMapper->isRemote()) {
LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
}
+
+ // IMapper 2.1 is optional
+ mMapperV2_1 = hardware::graphics::mapper::V2_1::IMapper::castFrom(mMapper);
}
Error Mapper::createDescriptor(
@@ -91,6 +97,50 @@
buffer, error);
}
+Error Mapper::validateBufferSize(buffer_handle_t bufferHandle,
+ const IMapper::BufferDescriptorInfo& descriptorInfo,
+ uint32_t stride) const
+{
+ if (mMapperV2_1 == nullptr) {
+ return Error::NONE;
+ }
+
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride);
+
+ return (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+}
+
+void Mapper::getTransportSize(buffer_handle_t bufferHandle,
+ uint32_t* outNumFds, uint32_t* outNumInts) const
+{
+ *outNumFds = uint32_t(bufferHandle->numFds);
+ *outNumInts = uint32_t(bufferHandle->numInts);
+
+ if (mMapperV2_1 == nullptr) {
+ return;
+ }
+
+ Error error;
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapperV2_1->getTransportSize(buffer,
+ [&](const auto& tmpError, const auto& tmpNumFds, const auto& tmpNumInts) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outNumFds = tmpNumFds;
+ *outNumInts = tmpNumInts;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+ ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d",
+ buffer, error);
+}
+
Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
const IMapper::Rect& accessRegion,
int acquireFence, void** outData) const
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index c880500..4ed2aa4 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -170,6 +170,8 @@
inUsage, &handle, &outStride, mId,
std::move(requestorName));
if (err == NO_ERROR) {
+ mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
+
width = static_cast<int>(inWidth);
height = static_cast<int>(inHeight);
format = inFormat;
@@ -199,7 +201,8 @@
if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) {
buffer_handle_t importedHandle;
- status_t err = mBufferMapper.importBuffer(handle, &importedHandle);
+ status_t err = mBufferMapper.importBuffer(handle, width, height,
+ layerCount, format, usage, stride, &importedHandle);
if (err != NO_ERROR) {
initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0);
@@ -212,6 +215,7 @@
}
handle = importedHandle;
+ mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
}
ANativeWindowBuffer::handle = handle;
@@ -323,11 +327,11 @@
}
size_t GraphicBuffer::getFlattenedSize() const {
- return static_cast<size_t>(13 + (handle ? handle->numInts : 0)) * sizeof(int);
+ return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
- return static_cast<size_t>(handle ? handle->numFds : 0);
+ return static_cast<size_t>(handle ? mTransportNumFds : 0);
}
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
@@ -353,18 +357,18 @@
buf[12] = int(usage >> 32); // high 32-bits
if (handle) {
- buf[10] = handle->numFds;
- buf[11] = handle->numInts;
- memcpy(fds, handle->data, static_cast<size_t>(handle->numFds) * sizeof(int));
+ buf[10] = int32_t(mTransportNumFds);
+ buf[11] = int32_t(mTransportNumInts);
+ memcpy(fds, handle->data, static_cast<size_t>(mTransportNumFds) * sizeof(int));
memcpy(buf + 13, handle->data + handle->numFds,
- static_cast<size_t>(handle->numInts) * sizeof(int));
+ static_cast<size_t>(mTransportNumInts) * sizeof(int));
}
buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
size -= sizeNeeded;
if (handle) {
- fds += handle->numFds;
- count -= static_cast<size_t>(handle->numFds);
+ fds += mTransportNumFds;
+ count -= static_cast<size_t>(mTransportNumFds);
}
return NO_ERROR;
@@ -457,7 +461,8 @@
if (handle != 0) {
buffer_handle_t importedHandle;
- status_t err = mBufferMapper.importBuffer(handle, &importedHandle);
+ status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height),
+ uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle);
if (err != NO_ERROR) {
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
@@ -470,6 +475,7 @@
native_handle_close(handle);
native_handle_delete(const_cast<native_handle_t*>(handle));
handle = importedHandle;
+ mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
}
buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d52c508..672dc36 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -52,17 +52,42 @@
}
status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
+ uint32_t width, uint32_t height, uint32_t layerCount,
+ PixelFormat format, uint64_t usage, uint32_t stride,
buffer_handle_t* outHandle)
{
ATRACE_CALL();
+ buffer_handle_t bufferHandle;
Gralloc2::Error error = mMapper->importBuffer(
- hardware::hidl_handle(rawHandle), outHandle);
+ hardware::hidl_handle(rawHandle), &bufferHandle);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGW("importBuffer(%p) failed: %d", rawHandle, error);
+ return static_cast<status_t>(error);
+ }
- ALOGW_IF(error != Gralloc2::Error::NONE, "importBuffer(%p) failed: %d",
- rawHandle, error);
+ Gralloc2::IMapper::BufferDescriptorInfo info = {};
+ info.width = width;
+ info.height = height;
+ info.layerCount = layerCount;
+ info.format = static_cast<Gralloc2::PixelFormat>(format);
+ info.usage = usage;
+ error = mMapper->validateBufferSize(bufferHandle, info, stride);
+ if (error != Gralloc2::Error::NONE) {
+ ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error);
+ freeBuffer(bufferHandle);
+ return static_cast<status_t>(error);
+ }
- return static_cast<status_t>(error);
+ *outHandle = bufferHandle;
+
+ return NO_ERROR;
+}
+
+void GraphicBufferMapper::getTransportSize(buffer_handle_t handle,
+ uint32_t* outTransportNumFds, uint32_t* outTransportNumInts)
+{
+ mMapper->getTransportSize(handle, outTransportNumFds, outTransportNumInts);
}
status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle)
@@ -99,7 +124,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);
}
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 8aee160..69c35f7 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -21,6 +21,7 @@
#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <android/hardware/graphics/mapper/2.1/IMapper.h>
#include <utils/StrongPointer.h>
namespace android {
@@ -55,6 +56,13 @@
void freeBuffer(buffer_handle_t bufferHandle) const;
+ Error validateBufferSize(buffer_handle_t bufferHandle,
+ const IMapper::BufferDescriptorInfo& descriptorInfo,
+ uint32_t stride) const;
+
+ void getTransportSize(buffer_handle_t bufferHandle,
+ uint32_t* outNumFds, uint32_t* outNumInts) const;
+
// The ownership of acquireFence is always transferred to the callee, even
// on errors.
Error lock(buffer_handle_t bufferHandle, uint64_t usage,
@@ -73,6 +81,7 @@
private:
sp<IMapper> mMapper;
+ sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1;
};
// A wrapper to IAllocator
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 95c2d22..e794462 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -230,6 +230,10 @@
GraphicBufferMapper& mBufferMapper;
ssize_t mInitCheck;
+ // numbers of fds/ints in native_handle_t to flatten
+ uint32_t mTransportNumFds;
+ uint32_t mTransportNumInts;
+
uint64_t mId;
// Stores the generation number of this buffer. If this number does not
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 06961b1..7cf003d 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -22,6 +22,7 @@
#include <memory>
+#include <ui/PixelFormat.h>
#include <utils/Singleton.h>
@@ -49,10 +50,15 @@
// The imported outHandle must be freed with freeBuffer when no longer
// needed. rawHandle is owned by the caller.
status_t importBuffer(buffer_handle_t rawHandle,
+ uint32_t width, uint32_t height, uint32_t layerCount,
+ PixelFormat format, uint64_t usage, uint32_t stride,
buffer_handle_t* outHandle);
status_t freeBuffer(buffer_handle_t handle);
+ void getTransportSize(buffer_handle_t handle,
+ uint32_t* outTransportNumFds, uint32_t* outTransportNumInts);
+
status_t lock(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, void** vaddr);
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 3e93788..0699fef 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -66,6 +66,11 @@
explicit operator bool() const { return epoll_fd_.IsValid(); }
+ int GetBufferId(size_t slot) const {
+ return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id()
+ : -1;
+ }
+
std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
return buffers_[slot];
}
@@ -218,7 +223,7 @@
// Tracks the buffers belonging to this queue. Buffers are stored according to
// "slot" in this vector. Each slot is a logical id of the buffer within this
// queue regardless of its queue position or presence in the ring buffer.
- std::vector<std::shared_ptr<BufferHubBuffer>> buffers_{kMaxQueueCapacity};
+ std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_;
// Buffers and related data that are available for dequeue.
RingBuffer<Entry> available_buffers_{kMaxQueueCapacity};
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 7fe9825..9fe161d 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -32,6 +32,7 @@
"dvr_display_manager.cpp",
"dvr_hardware_composer_client.cpp",
"dvr_performance.cpp",
+ "dvr_pose.cpp",
"dvr_surface.cpp",
"dvr_vsync.cpp",
]
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
index 4d9b215..1a99234 100644
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -44,7 +44,13 @@
}
void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
- delete write_buffer;
+ if (write_buffer != nullptr) {
+ ALOGW_IF(
+ write_buffer->slot != -1,
+ "dvrWriteBufferDestroy: Destroying a buffer associated with a valid "
+ "buffer queue slot. This may indicate possible leaks.");
+ delete write_buffer;
+ }
}
int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
@@ -107,7 +113,15 @@
*read_buffer = new DvrReadBuffer;
}
-void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; }
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) {
+ if (read_buffer != nullptr) {
+ ALOGW_IF(
+ read_buffer->slot != -1,
+ "dvrReadBufferDestroy: Destroying a buffer associated with a valid "
+ "buffer queue slot. This may indicate possible leaks.");
+ delete read_buffer;
+ }
+}
int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
return read_buffer && read_buffer->read_buffer;
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index 4adb5d2..035252d 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -63,7 +63,7 @@
}
int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer,
- int* out_fence_fd) {
+ int* out_fence_fd, size_t* out_slot) {
size_t slot;
pdx::LocalHandle fence;
std::shared_ptr<BufferProducer> buffer_producer;
@@ -141,6 +141,86 @@
write_buffer->write_buffer = std::move(buffer_producer);
*out_fence_fd = fence.Release();
+ if (out_slot) {
+ // TODO(b/65469368): Remove this null check once dvrWriteBufferQueueDequeue
+ // is deprecated.
+ *out_slot = slot;
+ }
+ return 0;
+}
+
+int DvrWriteBufferQueue::GainBuffer(int timeout,
+ DvrWriteBuffer** out_write_buffer,
+ DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd) {
+ DvrWriteBuffer write_buffer;
+ int fence_fd;
+ size_t slot;
+ const int ret = Dequeue(timeout, &write_buffer, &fence_fd, &slot);
+ if (ret < 0) {
+ ALOGE_IF(
+ ret != -ETIMEDOUT,
+ "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer, ret=%d",
+ ret);
+ return ret;
+ }
+
+ if (write_buffers_[slot] == nullptr) {
+ // Lazy initialization of a write_buffers_ slot. Note that a slot will only
+ // be dynamically allocated once during the entire cycle life of a queue.
+ write_buffers_[slot] = std::make_unique<DvrWriteBuffer>();
+ write_buffers_[slot]->slot = slot;
+ }
+
+ LOG_ALWAYS_FATAL_IF(
+ write_buffers_[slot]->write_buffer,
+ "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
+ write_buffers_[slot]->write_buffer = std::move(write_buffer.write_buffer);
+
+ *out_write_buffer = write_buffers_[slot].release();
+ *out_fence_fd = fence_fd;
+
+ return 0;
+}
+
+int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer,
+ const DvrNativeBufferMetadata* meta,
+ int ready_fence_fd) {
+ LOG_FATAL_IF(
+ (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()),
+ "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
+
+ // Some basic sanity checks before we put the buffer back into a slot.
+ size_t slot = static_cast<size_t>(write_buffer->slot);
+ if (write_buffers_[slot] != nullptr) {
+ ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot);
+ return -EINVAL;
+ }
+ if (write_buffer->write_buffer == nullptr) {
+ ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer.");
+ return -EINVAL;
+ }
+ if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) {
+ ALOGE(
+ "DvrWriteBufferQueue::PostBuffer: Buffer to be released does not "
+ "belong to this buffer queue.");
+ return -EINVAL;
+ }
+
+ pdx::LocalHandle fence(ready_fence_fd);
+ // TODO(b/65455724): All BufferHub operations should be async.
+ const int ret = write_buffer->write_buffer->Post(fence, meta, sizeof(*meta));
+ if (ret < 0) {
+ ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d",
+ ret);
+ return ret;
+ }
+
+ // Put the DvrWriteBuffer pointer back into its slot for reuse.
+ write_buffers_[slot].reset(write_buffer);
+ // It's import to reset the write buffer client now. It should stay invalid
+ // until next GainBuffer on the same slot.
+ write_buffers_[slot]->write_buffer = nullptr;
return 0;
}
@@ -236,7 +316,29 @@
if (!write_queue || !write_buffer || !out_fence_fd)
return -EINVAL;
- return write_queue->Dequeue(timeout, write_buffer, out_fence_fd);
+ // TODO(b/65469368): Deprecate this API once new GainBuffer API is in use.
+ return write_queue->Dequeue(timeout, write_buffer, out_fence_fd, nullptr);
+}
+
+int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
+ DvrWriteBuffer** out_write_buffer,
+ DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd) {
+ if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd)
+ return -EINVAL;
+
+ return write_queue->GainBuffer(timeout, out_write_buffer, out_meta,
+ out_fence_fd);
+}
+
+int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
+ DvrWriteBuffer* write_buffer,
+ const DvrNativeBufferMetadata* meta,
+ int ready_fence_fd) {
+ if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta)
+ return -EINVAL;
+
+ return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd);
}
int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue,
@@ -268,8 +370,8 @@
}
int DvrReadBufferQueue::Dequeue(int timeout, DvrReadBuffer* read_buffer,
- int* out_fence_fd, void* out_meta,
- size_t meta_size_bytes) {
+ int* out_fence_fd, size_t* out_slot,
+ void* out_meta, size_t meta_size_bytes) {
if (meta_size_bytes != consumer_queue_->metadata_size()) {
ALOGE(
"DvrReadBufferQueue::Dequeue: Invalid metadata size, expected (%zu), "
@@ -291,6 +393,95 @@
read_buffer->read_buffer = buffer_status.take();
*out_fence_fd = acquire_fence.Release();
+
+ if (out_slot) {
+ // TODO(b/65469368): Remove this null check once dvrReadBufferQueueDequeue
+ // is deprecated.
+ *out_slot = slot;
+ }
+ return 0;
+}
+
+int DvrReadBufferQueue::AcquireBuffer(int timeout,
+ DvrReadBuffer** out_read_buffer,
+ DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd) {
+ DvrReadBuffer read_buffer;
+ int fence_fd;
+ size_t slot;
+ const int ret = Dequeue(timeout, &read_buffer, &fence_fd, &slot, out_meta,
+ sizeof(*out_meta));
+ if (ret < 0) {
+ ALOGE_IF(
+ ret != -ETIMEDOUT,
+ "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer, error=%d",
+ ret);
+ return ret;
+ }
+
+ if (read_buffers_[slot] == nullptr) {
+ // Lazy initialization of a read_buffers_ slot. Note that a slot will only
+ // be dynamically allocated once during the entire cycle life of a queue.
+ read_buffers_[slot] = std::make_unique<DvrReadBuffer>();
+ read_buffers_[slot]->slot = slot;
+ }
+
+ LOG_FATAL_IF(
+ read_buffers_[slot]->read_buffer,
+ "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot);
+ read_buffers_[slot]->read_buffer = std::move(read_buffer.read_buffer);
+
+ *out_read_buffer = read_buffers_[slot].release();
+ *out_fence_fd = fence_fd;
+
+ return 0;
+}
+
+int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer,
+ const DvrNativeBufferMetadata* meta,
+ int release_fence_fd) {
+ LOG_FATAL_IF(
+ (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()),
+ "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
+
+ // Some basic sanity checks before we put the buffer back into a slot.
+ size_t slot = static_cast<size_t>(read_buffer->slot);
+ if (read_buffers_[slot] != nullptr) {
+ ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot);
+ return -EINVAL;
+ }
+ if (read_buffer->read_buffer == nullptr) {
+ ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer.");
+ return -EINVAL;
+ }
+ if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) {
+ ALOGE(
+ "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released does not "
+ "belong to this buffer queue.");
+ return -EINVAL;
+ }
+
+ pdx::LocalHandle fence(release_fence_fd);
+ int ret = 0;
+ if (fence) {
+ ret = read_buffer->read_buffer->Release(fence);
+ } else {
+ // TODO(b/65458354): Send metadata back to producer once shared memory based
+ // metadata is implemented.
+ // TODO(b/65455724): All BufferHub operations should be async.
+ ret = read_buffer->read_buffer->ReleaseAsync();
+ }
+ if (ret < 0) {
+ ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d",
+ ret);
+ return ret;
+ }
+
+ // Put the DvrReadBuffer pointer back into its slot for reuse.
+ read_buffers_[slot].reset(read_buffer);
+ // It's import to reset the read buffer client now. It should stay invalid
+ // until next AcquireBuffer on the same slot.
+ read_buffers_[slot]->read_buffer = nullptr;
return 0;
}
@@ -311,9 +502,11 @@
} else {
consumer_queue_->SetBufferRemovedCallback(
[callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) {
- DvrReadBuffer read_buffer{
- std::static_pointer_cast<BufferConsumer>(buffer)};
- callback(&read_buffer, context);
+ // When buffer is removed from the queue, the slot is already invalid.
+ auto read_buffer = std::make_unique<DvrReadBuffer>();
+ read_buffer->read_buffer =
+ std::static_pointer_cast<BufferConsumer>(buffer);
+ callback(read_buffer.release(), context);
});
}
}
@@ -366,8 +559,30 @@
if (meta_size_bytes != 0 && !out_meta)
return -EINVAL;
- return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta,
- meta_size_bytes);
+ // TODO(b/65469368): Deprecate this API once new AcquireBuffer API is in use.
+ return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, nullptr,
+ out_meta, meta_size_bytes);
+}
+
+int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
+ DvrReadBuffer** out_read_buffer,
+ DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd) {
+ if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd)
+ return -EINVAL;
+
+ return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta,
+ out_fence_fd);
+}
+
+int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
+ DvrReadBuffer* read_buffer,
+ const DvrNativeBufferMetadata* meta,
+ int release_fence_fd) {
+ if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta)
+ return -EINVAL;
+
+ return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd);
}
int dvrReadBufferQueueSetBufferAvailableCallback(
diff --git a/libs/vr/libdvr/dvr_buffer_queue_internal.h b/libs/vr/libdvr/dvr_buffer_queue_internal.h
index ffbe7a5..f9c0bfd 100644
--- a/libs/vr/libdvr/dvr_buffer_queue_internal.h
+++ b/libs/vr/libdvr/dvr_buffer_queue_internal.h
@@ -5,11 +5,23 @@
#include <private/dvr/buffer_hub_queue_client.h>
#include <sys/cdefs.h>
+#include <array>
#include <memory>
+#include "dvr_internal.h"
+
struct ANativeWindow;
+typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context);
+typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer,
+ void* context);
+
struct DvrWriteBufferQueue {
+ using BufferHubQueue = android::dvr::BufferHubQueue;
using ProducerQueue = android::dvr::ProducerQueue;
// Create a concrete object for DvrWriteBufferQueue.
@@ -30,19 +42,28 @@
int GetNativeWindow(ANativeWindow** out_window);
int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
- int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd);
+ int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd,
+ size_t* out_slot);
+ int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer,
+ DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
+ int PostBuffer(DvrWriteBuffer* write_buffer,
+ const DvrNativeBufferMetadata* meta, int ready_fence_fd);
int ResizeBuffer(uint32_t width, uint32_t height);
private:
std::shared_ptr<ProducerQueue> producer_queue_;
+ std::array<std::unique_ptr<DvrWriteBuffer>, BufferHubQueue::kMaxQueueCapacity>
+ write_buffers_;
uint32_t width_;
uint32_t height_;
uint32_t format_;
+
android::sp<android::Surface> native_window_;
};
struct DvrReadBufferQueue {
+ using BufferHubQueue = android::dvr::BufferHubQueue;
using ConsumerQueue = android::dvr::ConsumerQueue;
explicit DvrReadBufferQueue(
@@ -54,7 +75,11 @@
int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd,
- void* out_meta, size_t meta_size_bytes);
+ size_t* out_slot, void* out_meta, size_t meta_size_bytes);
+ int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer,
+ DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
+ int ReleaseBuffer(DvrReadBuffer* read_buffer,
+ const DvrNativeBufferMetadata* meta, int release_fence_fd);
void SetBufferAvailableCallback(
DvrReadBufferQueueBufferAvailableCallback callback, void* context);
void SetBufferRemovedCallback(
@@ -63,6 +88,8 @@
private:
std::shared_ptr<ConsumerQueue> consumer_queue_;
+ std::array<std::unique_ptr<DvrReadBuffer>, BufferHubQueue::kMaxQueueCapacity>
+ read_buffers_;
};
#endif // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
index 28b6c28..de8bb96 100644
--- a/libs/vr/libdvr/dvr_internal.h
+++ b/libs/vr/libdvr/dvr_internal.h
@@ -34,10 +34,20 @@
extern "C" {
struct DvrWriteBuffer {
+ // The slot nubmer of the buffer, a valid slot number must be in the range of
+ // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
+ // DvrWriteBuffer acquired from a DvrWriteBufferQueue.
+ int32_t slot = -1;
+
std::shared_ptr<android::dvr::BufferProducer> write_buffer;
};
struct DvrReadBuffer {
+ // The slot nubmer of the buffer, a valid slot number must be in the range of
+ // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
+ // DvrReadBuffer acquired from a DvrReadBufferQueue.
+ int32_t slot = -1;
+
std::shared_ptr<android::dvr::BufferConsumer> read_buffer;
};
diff --git a/libs/vr/libdvr/dvr_pose.cpp b/libs/vr/libdvr/dvr_pose.cpp
new file mode 100644
index 0000000..c379ef5
--- /dev/null
+++ b/libs/vr/libdvr/dvr_pose.cpp
@@ -0,0 +1,29 @@
+#include "include/dvr/dvr_pose.h"
+
+#include <memory>
+
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/pose_client_internal.h>
+
+#include "dvr_buffer_queue_internal.h"
+
+using android::dvr::ConsumerQueue;
+
+int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
+ DvrReadBufferQueue** queue_out) {
+ if (!client || !queue_out)
+ return -EINVAL;
+
+ ConsumerQueue* consumer_queue;
+ int status = android::dvr::dvrPoseClientGetDataReaderHandle(client,
+ data_type,
+ &consumer_queue);
+ if (status != 0) {
+ ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status);
+ return status;
+ }
+
+ std::shared_ptr<ConsumerQueue> consumer_queue_ptr{consumer_queue};
+ *queue_out = new DvrReadBufferQueue(consumer_queue_ptr);
+ return 0;
+}
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index e1dc58c..8f45ce7 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -23,6 +23,7 @@
typedef struct DvrDisplayManager DvrDisplayManager;
typedef struct DvrSurfaceState DvrSurfaceState;
typedef struct DvrPoseClient DvrPoseClient;
+typedef struct DvrPoseDataCaptureRequest DvrPoseDataCaptureRequest;
typedef struct DvrVSyncClient DvrVSyncClient;
typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
@@ -33,6 +34,7 @@
typedef struct DvrReadBufferQueue DvrReadBufferQueue;
typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
typedef struct DvrSurface DvrSurface;
typedef uint64_t DvrSurfaceAttributeType;
@@ -179,6 +181,13 @@
int timeout,
DvrWriteBuffer* out_buffer,
int* out_fence_fd);
+typedef int (*DvrWriteBufferQueueGainBufferPtr)(
+ DvrWriteBufferQueue* write_queue, int timeout,
+ DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd);
+typedef int (*DvrWriteBufferQueuePostBufferPtr)(
+ DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer,
+ const DvrNativeBufferMetadata* meta, int ready_fence_fd);
typedef int (*DvrWriteBufferQueueResizeBufferPtr)(
DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height);
typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue);
@@ -193,6 +202,13 @@
DvrReadBuffer* out_buffer,
int* out_fence_fd, void* out_meta,
size_t meta_size_bytes);
+typedef int (*DvrReadBufferQueueAcquireBufferPtr)(
+ DvrReadBufferQueue* read_queue, int timeout,
+ DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd);
+typedef int (*DvrReadBufferQueueReleaseBufferPtr)(
+ DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer,
+ const DvrNativeBufferMetadata* meta, int release_fence_fd);
typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context);
typedef int (*DvrReadBufferQueueSetBufferAvailableCallbackPtr)(
DvrReadBufferQueue* read_queue,
@@ -246,6 +262,15 @@
DvrPoseAsync* out_pose);
typedef int (*DvrPoseClientSensorsEnablePtr)(DvrPoseClient* client,
bool enabled);
+typedef int (*DvrPoseClientDataCapturePtr)(DvrPoseClient* client,
+ const DvrPoseDataCaptureRequest* request);
+typedef int (*DvrPoseClientDataReaderDestroyPtr)(DvrPoseClient* client,
+ uint64_t data_type);
+
+// dvr_pose.h
+typedef int (*DvrPoseClientGetDataReaderPtr)(DvrPoseClient* client,
+ uint64_t data_type,
+ DvrReadBufferQueue** read_queue);
// services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index f65bd1c..cce8c7e 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -166,3 +166,14 @@
// Gets an ANativeWindow from DvrWriteBufferQueue.
DVR_V1_API_ENTRY(WriteBufferQueueGetANativeWindow);
+
+// Dvr{Read,Write}BufferQueue API for asynchronous IPC.
+DVR_V1_API_ENTRY(WriteBufferQueueGainBuffer);
+DVR_V1_API_ENTRY(WriteBufferQueuePostBuffer);
+DVR_V1_API_ENTRY(ReadBufferQueueAcquireBuffer);
+DVR_V1_API_ENTRY(ReadBufferQueueReleaseBuffer);
+
+// Pose client
+DVR_V1_API_ENTRY(PoseClientGetDataReader);
+DVR_V1_API_ENTRY(PoseClientDataCapture);
+DVR_V1_API_ENTRY(PoseClientDataReaderDestroy);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
index 8b9e048..bf695c7 100644
--- a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
@@ -89,21 +89,44 @@
int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
DvrReadBufferQueue** out_read_queue);
-// Dequeue a buffer to write into.
+// @deprecated Please use dvrWriteBufferQueueGainBuffer instead.
+int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
+ DvrWriteBuffer* out_buffer, int* out_fence_fd);
+
+// Gains a buffer to write into.
//
-// @param write_queue The DvrWriteBufferQueue of interest.
+// @param write_queue The DvrWriteBufferQueue to gain buffer from.
// @param timeout Specifies the number of milliseconds that the method will
// block. Specifying a timeout of -1 causes it to block indefinitely,
// while specifying a timeout equal to zero cause it to return immediately,
// even if no buffers are available.
// @param out_buffer A targeting DvrWriteBuffer object to hold the output of the
-// dequeue operation. Must be created by |dvrWriteBufferCreateEmpty|.
+// dequeue operation.
+// @param out_meta A DvrNativeBufferMetadata object populated by the
+// corresponding dvrReadBufferQueueReleaseBuffer API.
// @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which
// signals the release of underlying buffer. The producer should wait until
// this fence clears before writing data into it.
// @return Zero on success, or negative error code.
-int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
- DvrWriteBuffer* out_buffer, int* out_fence_fd);
+int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
+ DvrWriteBuffer** out_write_buffer,
+ DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd);
+
+// Posts a buffer and signals its readiness to be read from.
+//
+// @param write_queue The DvrWriteBufferQueue to post buffer into.
+// @param write_buffer The buffer to be posted.
+// @param meta The buffer metadata describing the buffer.
+// @param ready_fence_fd A sync fence fd defined in NDK's sync.h API, which
+// signals the readdiness of underlying buffer. When a valid fence gets
+// passed in, the consumer will wait the fence to be ready before it starts
+// to ready from the buffer.
+// @return Zero on success, or negative error code.
+int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
+ DvrWriteBuffer* write_buffer,
+ const DvrNativeBufferMetadata* meta,
+ int ready_fence_fd);
// Overrides buffer dimension with new width and height.
//
@@ -153,28 +176,45 @@
int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
DvrReadBufferQueue** out_read_queue);
-// Dequeue a buffer to read from.
+// @deprecated Please use dvrReadBufferQueueAcquireBuffer instead.
+int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
+ DvrReadBuffer* out_buffer, int* out_fence_fd,
+ void* out_meta, size_t meta_size_bytes);
+
+// Dequeues a buffer to read from.
//
-// @param read_queue The DvrReadBufferQueue of interest.
+// @param read_queue The DvrReadBufferQueue to acquire buffer from.
// @param timeout Specifies the number of milliseconds that the method will
// block. Specifying a timeout of -1 causes it to block indefinitely,
// while specifying a timeout equal to zero cause it to return immediately,
// even if no buffers are available.
// @param out_buffer A targeting DvrReadBuffer object to hold the output of the
// dequeue operation. Must be created by |dvrReadBufferCreateEmpty|.
+// @param out_meta A DvrNativeBufferMetadata object populated by the
+// corresponding dvrWriteBufferQueuePostBuffer API.
// @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which
// signals the release of underlying buffer. The consumer should wait until
// this fence clears before reading data from it.
-// @param out_meta The memory area where a metadata object will be filled.
-// Can be nullptr iff |meta_size_bytes| is zero (i.e., there is no
-// metadata).
-// @param meta_size_bytes Size of the metadata object caller expects. If it
-// doesn't match the size of actually metadata transported by the buffer
-// queue, the method returns -EINVAL.
// @return Zero on success, or negative error code.
-int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
- DvrReadBuffer* out_buffer, int* out_fence_fd,
- void* out_meta, size_t meta_size_bytes);
+int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
+ DvrReadBuffer** out_read_buffer,
+ DvrNativeBufferMetadata* out_meta,
+ int* out_fence_fd);
+
+// Releases a buffer and signals its readiness to be written into.
+//
+// @param read_queue The DvrReadBufferQueue to release buffer into.
+// @param read_buffer The buffer to be released.
+// @param meta The buffer metadata describing the buffer.
+// @param release_fence_fd A sync fence fd defined in NDK's sync.h API, which
+// signals the readdiness of underlying buffer. When a valid fence gets
+// passed in, the producer will wait the fence to be ready before it starts
+// to write into the buffer again.
+// @return Zero on success, or negative error code.
+int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
+ DvrReadBuffer* read_buffer,
+ const DvrNativeBufferMetadata* meta,
+ int release_fence_fd);
// Callback function which will be called when a buffer is avaiable.
//
diff --git a/libs/vr/libdvr/include/dvr/dvr_pose.h b/libs/vr/libdvr/include/dvr/dvr_pose.h
index b3df028..8752751 100644
--- a/libs/vr/libdvr/include/dvr/dvr_pose.h
+++ b/libs/vr/libdvr/include/dvr/dvr_pose.h
@@ -15,6 +15,9 @@
#endif
#endif
+typedef struct DvrPoseClient DvrPoseClient;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
// Represents an estimated pose, accessed asynchronously through a shared ring
// buffer. No assumptions should be made about the data in padding space.
// The size of this struct is 128 bytes.
@@ -95,6 +98,57 @@
uint8_t padding[12];
} DvrPose;
+// Represents a data type that can be streamed from pose service.
+enum {
+ DVR_POSE_RAW_DATA_STEREO_IMAGE = (1ULL << 0),
+ DVR_POSE_RAW_DATA_POINT_CLOUD = (1ULL << 1),
+ DVR_POSE_RAW_DATA_FEATURES = (1ULL << 2),
+
+ // Always last.
+ DVR_POSE_RAW_DATA_COUNT = (1ULL << 3),
+};
+
+// A request to retrieve data from the pose service. Expects that a buffer
+// queue has been initialized through dvrPoseClientGetDataReader().
+typedef struct DvrPoseDataCaptureRequest {
+ // The type of data to capture. Refer to enum DVR_POSE_RAW_DATA_* for types.
+ uint64_t data_type;
+ // The sample interval. This can be used to skip samples. For example, a
+ // value of 5 will capture every fifth frame and discard the 4 frames in
+ // between. Set to 1 to capture all frames.
+ uint32_t sample_interval;
+ // The length of time to capture samples in milliseconds. Set to 0 to capture
+ // indefinitely.
+ uint32_t capture_time_ms;
+ // Reserved fields.
+ uint32_t reserved0;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ uint32_t reserved4;
+} DvrPoseDataCaptureRequest;
+
+// Gets a read buffer queue for the data type |data_type|. Each call returns a
+// different read buffer queue connected to the same write buffer queue. A
+// separate write buffer queue exists for each |data_type|.
+//
+// PoseService supports a single consumer per write buffer queue. The consumer
+// is expected to hold a single DvrReadBufferQueue at a time. Callers should
+// cache these instead of requesting new ones when possible. If the consumer
+// disconnects from the queue, it can regain a read buffer queue for the same
+// producer by calling this function.
+//
+// For data_type DVR_POSE_RAW_DATA_STEREO_IMAGE, each buffer consists of two
+// images formatted as a AHARDWAREBUFFER_FORMAT_BLOB, where height is 1 and
+// width is the total size of both images. The size of an individual image can
+// be found in the metadata struct DvrNativeBufferMetadata, where width is
+// |crop_right| and height is |crop_bottom|/2. Each image is contiguous in
+// memory with stride equal to width.
+int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
+ DvrReadBufferQueue** queue_out);
+
+// TODO(b/65067592): Move pose api's from pose_client.h to here.
+
__END_DECLS
#endif // ANDROID_DVR_PUBLIC_POSE_H_
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index ab2ee75..a9302a7 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -47,6 +47,7 @@
cflags: [
"-DLOG_TAG=\"dvr_api-test\"",
"-DTRACE=0",
+ "-Wno-missing-field-initializers",
"-O0",
"-g",
],
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
index 0b30c38..f1c5e48 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -27,8 +27,6 @@
static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
static constexpr size_t kQueueCapacity = 3;
-typedef uint64_t TestMeta;
-
class DvrBufferQueueTest : public ::testing::Test {
public:
static void BufferAvailableCallback(void* context) {
@@ -65,20 +63,20 @@
int buffer_removed_count_{0};
};
-TEST_F(DvrBufferQueueTest, TestWrite_QueueCreateDestroy) {
+TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(TestMeta), &write_queue_);
+ /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
dvrWriteBufferQueueDestroy(write_queue_);
write_queue_ = nullptr;
}
-TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) {
+TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(TestMeta), &write_queue_);
+ kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_);
@@ -87,10 +85,10 @@
ASSERT_EQ(kQueueCapacity, capacity);
}
-TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) {
+TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(TestMeta), &write_queue_);
+ /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
DvrReadBufferQueue* read_queue = nullptr;
@@ -102,10 +100,10 @@
dvrReadBufferQueueDestroy(read_queue);
}
-TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) {
+TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(TestMeta), &write_queue_);
+ /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
DvrReadBufferQueue* read_queue1 = nullptr;
@@ -124,102 +122,86 @@
dvrReadBufferQueueDestroy(read_queue2);
}
-TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) {
+TEST_F(DvrBufferQueueTest, GainBuffer) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(TestMeta), &write_queue_);
- ASSERT_EQ(0, ret);
+ kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+ ASSERT_EQ(ret, 0);
- DvrReadBuffer* read_buffer = nullptr;
- DvrWriteBuffer* write_buffer = nullptr;
+ DvrWriteBuffer* wb = nullptr;
+ EXPECT_FALSE(dvrWriteBufferIsValid(wb));
- EXPECT_FALSE(dvrReadBufferIsValid(read_buffer));
- EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
-
- dvrReadBufferCreateEmpty(&read_buffer);
- ASSERT_NE(nullptr, read_buffer);
-
- dvrWriteBufferCreateEmpty(&write_buffer);
- ASSERT_NE(nullptr, write_buffer);
-
- EXPECT_FALSE(dvrReadBufferIsValid(read_buffer));
- EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
-
- DvrReadBufferQueue* read_queue = nullptr;
-
- ASSERT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
- const int kTimeoutMs = 0;
+ DvrNativeBufferMetadata meta = {0};
int fence_fd = -1;
- ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeoutMs,
- write_buffer, &fence_fd));
- EXPECT_EQ(-1, fence_fd);
- EXPECT_TRUE(dvrWriteBufferIsValid(write_buffer));
-
- ASSERT_EQ(0, dvrWriteBufferClear(write_buffer));
- EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
+ ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta,
+ &fence_fd);
+ ASSERT_EQ(ret, 0);
+ EXPECT_EQ(fence_fd, -1);
+ EXPECT_NE(wb, nullptr);
+ EXPECT_TRUE(dvrWriteBufferIsValid(wb));
}
-TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) {
+TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(TestMeta), &write_queue_);
- ASSERT_EQ(0, ret);
+ kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
+ ASSERT_EQ(ret, 0);
- static constexpr int kTimeout = 0;
DvrReadBufferQueue* read_queue = nullptr;
DvrReadBuffer* rb = nullptr;
DvrWriteBuffer* wb = nullptr;
+ DvrNativeBufferMetadata meta1 = {0};
+ DvrNativeBufferMetadata meta2 = {0};
int fence_fd = -1;
ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(read_queue, nullptr);
dvrReadBufferQueueSetBufferAvailableCallback(read_queue,
&BufferAvailableCallback, this);
- dvrWriteBufferCreateEmpty(&wb);
- ASSERT_NE(nullptr, wb);
-
- dvrReadBufferCreateEmpty(&rb);
- ASSERT_NE(nullptr, rb);
-
// Gain buffer for writing.
- ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd);
- ASSERT_EQ(0, ret);
+ ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1,
+ &fence_fd);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(wb, nullptr);
ASSERT_TRUE(dvrWriteBufferIsValid(wb));
ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d",
wb, fence_fd);
android::base::unique_fd release_fence(fence_fd);
// Post buffer to the read_queue.
- TestMeta seq = 42U;
- ret = dvrWriteBufferPost(wb, /* fence */ -1, &seq, sizeof(seq));
- ASSERT_EQ(0, ret);
- dvrWriteBufferDestroy(wb);
+ meta1.timestamp = 42;
+ ret = dvrWriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1);
+ ASSERT_EQ(ret, 0);
+ ASSERT_FALSE(dvrWriteBufferIsValid(wb));
wb = nullptr;
// Acquire buffer for reading.
- TestMeta acquired_seq = 0U;
- ret = dvrReadBufferQueueDequeue(read_queue, kTimeout, rb, &fence_fd,
- &acquired_seq, sizeof(acquired_seq));
- ASSERT_EQ(0, ret);
+ ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/0, &rb, &meta2,
+ &fence_fd);
+ ASSERT_EQ(ret, 0);
+ ASSERT_NE(rb, nullptr);
// Dequeue is successfully, BufferAvailableCallback should be fired once.
- ASSERT_EQ(1, buffer_available_count_);
+ ASSERT_EQ(buffer_available_count_, 1);
ASSERT_TRUE(dvrReadBufferIsValid(rb));
- ASSERT_EQ(seq, acquired_seq);
+
+ // Metadata should be passed along from producer to consumer properly.
+ ASSERT_EQ(meta1.timestamp, meta2.timestamp);
+
ALOGD_IF(TRACE,
"TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb,
fence_fd);
android::base::unique_fd acquire_fence(fence_fd);
// Release buffer to the write_queue.
- ret = dvrReadBufferRelease(rb, -1);
- ASSERT_EQ(0, ret);
- dvrReadBufferDestroy(rb);
+ ret = dvrReadBufferQueueReleaseBuffer(read_queue, rb, &meta2,
+ /*release_fence_fd=*/-1);
+ ASSERT_EQ(ret, 0);
+ ASSERT_FALSE(dvrReadBufferIsValid(rb));
rb = nullptr;
// TODO(b/34387835) Currently buffer allocation has to happen after all queues
@@ -232,37 +214,18 @@
dvrReadBufferQueueDestroy(read_queue);
}
-TEST_F(DvrBufferQueueTest, TestGetANativeWindow) {
+TEST_F(DvrBufferQueueTest, GetANativeWindow) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(TestMeta), &write_queue_);
- ASSERT_EQ(0, ret);
-
- ANativeWindow* window = nullptr;
-
- // The |write_queue_| doesn't have proper metadata (must be
- // DvrNativeBufferMetadata) configured during creation.
- ret = dvrWriteBufferQueueGetANativeWindow(write_queue_, &window);
- ASSERT_EQ(-EINVAL, ret);
- ASSERT_EQ(nullptr, window);
- dvrWriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
-
- // A write queue with DvrNativeBufferMetadata should work fine.
- ASSERT_EQ(nullptr, write_queue_);
-
- ret = dvrWriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
/*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
ASSERT_NE(nullptr, write_queue_);
+ ANativeWindow* window = nullptr;
ret = dvrWriteBufferQueueGetANativeWindow(write_queue_, &window);
ASSERT_EQ(0, ret);
ASSERT_NE(nullptr, window);
- // TODO(b/64723700): Remove dependencies of Android platform bits so that we
- // can run dvr_buffer_queue-test in DTS.
uint32_t width = ANativeWindow_getWidth(window);
uint32_t height = ANativeWindow_getHeight(window);
uint32_t format = ANativeWindow_getFormat(window);
@@ -274,15 +237,15 @@
// Create buffer queue of three buffers and dequeue three buffers out of it.
// Before each dequeue operation, we resize the buffer queue and expect the
// queue always return buffer with desired dimension.
-TEST_F(DvrBufferQueueTest, TestResizeBuffer) {
+TEST_F(DvrBufferQueueTest, ResizeBuffer) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(TestMeta), &write_queue_);
+ kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
- static constexpr int kTimeout = 0;
int fence_fd = -1;
+ DvrNativeBufferMetadata meta = {0};
DvrReadBufferQueue* read_queue = nullptr;
DvrWriteBuffer* wb1 = nullptr;
DvrWriteBuffer* wb2 = nullptr;
@@ -300,13 +263,6 @@
dvrReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback,
this);
- dvrWriteBufferCreateEmpty(&wb1);
- ASSERT_NE(nullptr, wb1);
- dvrWriteBufferCreateEmpty(&wb2);
- ASSERT_NE(nullptr, wb2);
- dvrWriteBufferCreateEmpty(&wb3);
- ASSERT_NE(nullptr, wb3);
-
// Handle all pending events on the read queue.
ret = dvrReadBufferQueueHandleEvents(read_queue);
ASSERT_EQ(0, ret);
@@ -321,7 +277,8 @@
ASSERT_EQ(0, ret);
// Gain first buffer for writing. All buffers will be resized.
- ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb1, &fence_fd);
+ ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, &meta,
+ &fence_fd);
ASSERT_EQ(0, ret);
ASSERT_TRUE(dvrWriteBufferIsValid(wb1));
ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1);
@@ -347,7 +304,8 @@
ASSERT_EQ(0, ret);
// The next buffer we dequeued should have new width.
- ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb2, &fence_fd);
+ ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, &meta,
+ &fence_fd);
ASSERT_EQ(0, ret);
ASSERT_TRUE(dvrWriteBufferIsValid(wb2));
ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2,
@@ -373,7 +331,8 @@
ASSERT_EQ(0, ret);
// The next buffer we dequeued should have new width.
- ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb3, &fence_fd);
+ ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, &meta,
+ &fence_fd);
ASSERT_EQ(0, ret);
ASSERT_TRUE(dvrWriteBufferIsValid(wb3));
ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3,
@@ -396,78 +355,10 @@
dvrReadBufferQueueDestroy(read_queue);
}
-TEST_F(DvrBufferQueueTest, DequeueEmptyMetadata) {
- // Overrides default queue parameters: Empty metadata.
+TEST_F(DvrBufferQueueTest, ReadQueueEventFd) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/1, /*metadata_size=*/0, &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBuffer* rb = nullptr;
- DvrWriteBuffer* wb = nullptr;
- dvrReadBufferCreateEmpty(&rb);
- dvrWriteBufferCreateEmpty(&wb);
-
- DvrReadBufferQueue* read_queue = nullptr;
- EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
- const int kTimeoutMs = 0;
- int fence_fd = -1;
- EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd));
-
- EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, nullptr, 0));
- EXPECT_EQ(0, dvrWriteBufferClear(wb));
- dvrWriteBufferDestroy(wb);
- wb = nullptr;
-
- // When acquire buffer, it's legit to pass nullptr as out_meta iff metadata
- // size is Zero.
- EXPECT_EQ(0, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd,
- nullptr, 0));
- EXPECT_TRUE(dvrReadBufferIsValid(rb));
-}
-
-TEST_F(DvrBufferQueueTest, DequeueMismatchMetadata) {
- int ret = dvrWriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/1, sizeof(TestMeta), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBuffer* rb = nullptr;
- DvrWriteBuffer* wb = nullptr;
- dvrReadBufferCreateEmpty(&rb);
- dvrWriteBufferCreateEmpty(&wb);
-
- DvrReadBufferQueue* read_queue = nullptr;
- EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
- const int kTimeoutMs = 0;
- int fence_fd = -1;
- EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd));
-
- TestMeta seq = 42U;
- EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, &seq, sizeof(seq)));
- EXPECT_EQ(0, dvrWriteBufferClear(wb));
- dvrWriteBufferDestroy(wb);
- wb = nullptr;
-
- // Dequeue with wrong metadata will cause EINVAL.
- int8_t wrong_metadata;
- EXPECT_EQ(-EINVAL,
- dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd,
- &wrong_metadata, sizeof(wrong_metadata)));
- EXPECT_FALSE(dvrReadBufferIsValid(rb));
-
- // Dequeue with empty metadata will cause EINVAL.
- EXPECT_EQ(-EINVAL, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb,
- &fence_fd, nullptr, 0));
- EXPECT_FALSE(dvrReadBufferIsValid(rb));
-}
-
-TEST_F(DvrBufferQueueTest, TestReadQueueEventFd) {
- int ret = dvrWriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(TestMeta), &write_queue_);
+ kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
DvrReadBufferQueue* read_queue = nullptr;
@@ -483,10 +374,10 @@
// Verifies a Dvr{Read,Write}BufferQueue contains the same set of
// Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id,
// the corresponding AHardwareBuffer handle stays the same.
-TEST_F(DvrBufferQueueTest, TestStableBufferIdAndHardwareBuffer) {
+TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) {
int ret = dvrWriteBufferQueueCreate(
kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(TestMeta), &write_queue_);
+ kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
ASSERT_EQ(0, ret);
int fence_fd = -1;
@@ -497,25 +388,21 @@
std::array<DvrReadBuffer*, kQueueCapacity> rbs;
// Write buffers.
std::array<DvrWriteBuffer*, kQueueCapacity> wbs;
+ // Buffer metadata.
+ std::array<DvrNativeBufferMetadata, kQueueCapacity> metas;
// Hardware buffers for Read buffers.
std::unordered_map<int, AHardwareBuffer*> rhbs;
// Hardware buffers for Write buffers.
std::unordered_map<int, AHardwareBuffer*> whbs;
- for (size_t i = 0; i < kQueueCapacity; i++) {
- dvrReadBufferCreateEmpty(&rbs[i]);
- dvrWriteBufferCreateEmpty(&wbs[i]);
- }
-
constexpr int kNumTests = 100;
- constexpr int kTimeout = 0;
- TestMeta seq = 0U;
// This test runs the following operations many many times. Thus we prefer to
// use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output.
std::function<void(size_t i)> Gain = [&](size_t i) {
- ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wbs[i],
- &fence_fd));
+ int ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0,
+ &wbs[i], &metas[i], &fence_fd);
+ ASSERT_EQ(ret, 0);
ASSERT_LT(fence_fd, 0); // expect invalid fence.
ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i]));
int buffer_id = dvrWriteBufferGetId(wbs[i]);
@@ -540,15 +427,16 @@
std::function<void(size_t i)> Post = [&](size_t i) {
ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i]));
- seq++;
- ASSERT_EQ(0, dvrWriteBufferPost(wbs[i], /*fence=*/-1, &seq, sizeof(seq)));
+ metas[i].timestamp++;
+ int ret = dvrWriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i],
+ /*fence=*/-1);
+ ASSERT_EQ(ret, 0);
};
std::function<void(size_t i)> Acquire = [&](size_t i) {
- TestMeta out_seq = 0U;
- ASSERT_EQ(0,
- dvrReadBufferQueueDequeue(read_queue, kTimeout, rbs[i], &fence_fd,
- &out_seq, sizeof(out_seq)));
+ int ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/0,
+ &rbs[i], &metas[i], &fence_fd);
+ ASSERT_EQ(ret, 0);
ASSERT_LT(fence_fd, 0); // expect invalid fence.
ASSERT_TRUE(dvrReadBufferIsValid(rbs[i]));
@@ -574,8 +462,9 @@
std::function<void(size_t i)> Release = [&](size_t i) {
ASSERT_TRUE(dvrReadBufferIsValid(rbs[i]));
- seq++;
- ASSERT_EQ(0, dvrReadBufferRelease(rbs[i], /*fence=*/-1));
+ int ret = dvrReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i],
+ /*release_fence_fd=*/-1);
+ ASSERT_EQ(ret, 0);
};
// Scenario one:
@@ -630,12 +519,6 @@
ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i));
}
}
-
- // Clean up all read buffers and write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- dvrReadBufferDestroy(rbs[i]);
- dvrWriteBufferDestroy(wbs[i]);
- }
}
} // namespace
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
index 9d91617..3f785fa 100644
--- a/libs/vr/libpdx_uds/client_channel.cpp
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -90,10 +90,12 @@
size_t send_len = CountVectorSize(send_vector, send_count);
InitRequest(&transaction_state->request, opcode, send_len, max_recv_len,
false);
- auto status = SendData(socket_fd, transaction_state->request);
- if (status && send_len > 0)
- status = SendDataVector(socket_fd, send_vector, send_count);
- return status;
+ if (send_len == 0) {
+ send_vector = nullptr;
+ send_count = 0;
+ }
+ return SendData(socket_fd, transaction_state->request, send_vector,
+ send_count);
}
Status<void> ReceiveResponse(const BorrowedHandle& socket_fd,
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
index d75ce86..f85b3bb 100644
--- a/libs/vr/libpdx_uds/ipc_helper.cpp
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -20,6 +20,9 @@
namespace {
+constexpr size_t kMaxFdCount =
+ 256; // Total of 1KiB of data to transfer these FDs.
+
// Default implementations of Send/Receive interfaces to use standard socket
// send/sendmsg/recv/recvmsg functions.
class SocketSender : public SendInterface {
@@ -175,20 +178,31 @@
}
Status<void> SendPayload::Send(const BorrowedHandle& socket_fd,
- const ucred* cred) {
+ const ucred* cred, const iovec* data_vec,
+ size_t vec_count) {
+ if (file_handles_.size() > kMaxFdCount) {
+ ALOGE(
+ "SendPayload::Send: Trying to send too many file descriptors (%zu), "
+ "max allowed = %zu",
+ file_handles_.size(), kMaxFdCount);
+ return ErrorStatus{EINVAL};
+ }
+
SendInterface* sender = sender_ ? sender_ : &g_socket_sender;
MessagePreamble preamble;
preamble.magic = kMagicPreamble;
preamble.data_size = buffer_.size();
preamble.fd_count = file_handles_.size();
- Status<void> ret = SendAll(sender, socket_fd, &preamble, sizeof(preamble));
- if (!ret)
- return ret;
msghdr msg = {};
- iovec recv_vect = {buffer_.data(), buffer_.size()};
- msg.msg_iov = &recv_vect;
- msg.msg_iovlen = 1;
+ msg.msg_iovlen = 2 + vec_count;
+ msg.msg_iov = static_cast<iovec*>(alloca(sizeof(iovec) * msg.msg_iovlen));
+ msg.msg_iov[0].iov_base = &preamble;
+ msg.msg_iov[0].iov_len = sizeof(preamble);
+ msg.msg_iov[1].iov_base = buffer_.data();
+ msg.msg_iov[1].iov_len = buffer_.size();
+ for (size_t i = 0; i < vec_count; i++)
+ msg.msg_iov[i + 2] = data_vec[i];
if (cred || !file_handles_.empty()) {
const size_t fd_bytes = file_handles_.size() * sizeof(int);
@@ -270,7 +284,15 @@
ucred* cred) {
RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver;
MessagePreamble preamble;
- Status<void> ret = RecvAll(receiver, socket_fd, &preamble, sizeof(preamble));
+ msghdr msg = {};
+ iovec recv_vect = {&preamble, sizeof(preamble)};
+ msg.msg_iov = &recv_vect;
+ msg.msg_iovlen = 1;
+ const size_t receive_fd_bytes = kMaxFdCount * sizeof(int);
+ msg.msg_controllen = CMSG_SPACE(sizeof(ucred)) + CMSG_SPACE(receive_fd_bytes);
+ msg.msg_control = alloca(msg.msg_controllen);
+
+ Status<void> ret = RecvMsgAll(receiver, socket_fd, &msg);
if (!ret)
return ret;
@@ -284,23 +306,6 @@
file_handles_.clear();
read_pos_ = 0;
- msghdr msg = {};
- iovec recv_vect = {buffer_.data(), buffer_.size()};
- msg.msg_iov = &recv_vect;
- msg.msg_iovlen = 1;
-
- if (cred || preamble.fd_count) {
- const size_t receive_fd_bytes = preamble.fd_count * sizeof(int);
- msg.msg_controllen =
- (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
- (receive_fd_bytes == 0 ? 0 : CMSG_SPACE(receive_fd_bytes));
- msg.msg_control = alloca(msg.msg_controllen);
- }
-
- ret = RecvMsgAll(receiver, socket_fd, &msg);
- if (!ret)
- return ret;
-
bool cred_available = false;
file_handles_.reserve(preamble.fd_count);
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
@@ -320,6 +325,10 @@
cmsg = CMSG_NXTHDR(&msg, cmsg);
}
+ ret = RecvAll(receiver, socket_fd, buffer_.data(), buffer_.size());
+ if (!ret)
+ return ret;
+
if (cred && !cred_available) {
ALOGE("ReceivePayload::Receive: Failed to obtain message credentials");
ret.SetError(EIO);
diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
index bde16d3..664a0d1 100644
--- a/libs/vr/libpdx_uds/private/uds/ipc_helper.h
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -59,7 +59,8 @@
public:
SendPayload(SendInterface* sender = nullptr) : sender_{sender} {}
Status<void> Send(const BorrowedHandle& socket_fd);
- Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred);
+ Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred,
+ const iovec* data_vec = nullptr, size_t vec_count = 0);
// MessageWriter
void* GetNextWriteBufferSection(size_t size) override;
@@ -156,18 +157,22 @@
};
template <typename T>
-inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data) {
+inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data,
+ const iovec* data_vec = nullptr,
+ size_t vec_count = 0) {
SendPayload payload;
rpc::Serialize(data, &payload);
- return payload.Send(socket_fd);
+ return payload.Send(socket_fd, nullptr, data_vec, vec_count);
}
template <typename FileHandleType>
inline Status<void> SendData(const BorrowedHandle& socket_fd,
- const RequestHeader<FileHandleType>& request) {
+ const RequestHeader<FileHandleType>& request,
+ const iovec* data_vec = nullptr,
+ size_t vec_count = 0) {
SendPayload payload;
rpc::Serialize(request, &payload);
- return payload.Send(socket_fd, &request.cred);
+ return payload.Send(socket_fd, &request.cred, data_vec, vec_count);
}
Status<void> SendData(const BorrowedHandle& socket_fd, const void* data,
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
index fda9585..9614c6d 100644
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -9,8 +9,8 @@
namespace dvr {
AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
- LocalHandle acquire_fence)
- : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {}
+ LocalHandle acquire_fence, std::size_t slot)
+ : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
int* error) {
@@ -31,18 +31,20 @@
}
}
-AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other)
- : buffer_(std::move(other.buffer_)),
- acquire_fence_(std::move(other.acquire_fence_)) {}
+AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) {
+ *this = std::move(other);
+}
AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) {
if (this != &other) {
- Release(LocalHandle(kEmptyFence));
+ Release();
- buffer_ = std::move(other.buffer_);
- acquire_fence_ = std::move(other.acquire_fence_);
+ using std::swap;
+ swap(buffer_, other.buffer_);
+ swap(acquire_fence_, other.acquire_fence_);
+ swap(slot_, other.slot_);
}
return *this;
}
@@ -81,8 +83,6 @@
ALOGD_IF(TRACE, "AcquiredBuffer::Release: buffer_id=%d release_fence=%d",
buffer_ ? buffer_->id() : -1, release_fence.Get());
if (buffer_) {
- // Close the release fence since we can't transfer it with an async release.
- release_fence.Close();
const int ret = buffer_->ReleaseAsync();
if (ret < 0) {
ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
@@ -92,9 +92,10 @@
}
buffer_ = nullptr;
- acquire_fence_.Close();
}
+ acquire_fence_.Close();
+ slot_ = 0;
return 0;
}
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
index e0dc9f2..32e912a 100644
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -21,7 +21,7 @@
// this constructor; the constructor does not attempt to ACQUIRE the buffer
// itself.
AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
- pdx::LocalHandle acquire_fence);
+ pdx::LocalHandle acquire_fence, std::size_t slot = 0);
// Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
// be in the POSTED state prior to calling this constructor, as this
@@ -64,13 +64,18 @@
// to the producer. On success, the BufferConsumer and acquire fence are set
// to empty state; if release fails, the BufferConsumer and acquire fence are
// left in place and a negative error code is returned.
- int Release(pdx::LocalHandle release_fence);
+ int Release(pdx::LocalHandle release_fence = {});
+
+ // Returns the slot in the queue this buffer belongs to. Buffers that are not
+ // part of a queue return 0.
+ std::size_t slot() const { return slot_; }
private:
std::shared_ptr<BufferConsumer> buffer_;
// Mutable so that the fence can be closed when it is determined to be
// signaled during IsAvailable().
mutable pdx::LocalHandle acquire_fence_;
+ std::size_t slot_{0};
AcquiredBuffer(const AcquiredBuffer&) = delete;
void operator=(const AcquiredBuffer&) = delete;
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
index 40396b9..ef8cca3 100644
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -65,6 +65,7 @@
}
pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
+ ATRACE_NAME("DisplayManagerService::HandleMessage");
auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
switch (message.GetOp()) {
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index af18e21..ac68a5e 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -124,6 +124,8 @@
// surface-specific messages to the per-instance handlers.
Status<void> DisplayService::HandleMessage(pdx::Message& message) {
ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
+ ATRACE_NAME("DisplayService::HandleMessage");
+
switch (message.GetOp()) {
case DisplayProtocol::GetMetrics::Opcode:
DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
@@ -349,17 +351,9 @@
void DisplayService::UpdateActiveDisplaySurfaces() {
auto visible_surfaces = GetVisibleDisplaySurfaces();
-
- std::sort(visible_surfaces.begin(), visible_surfaces.end(),
- [](const std::shared_ptr<DisplaySurface>& a,
- const std::shared_ptr<DisplaySurface>& b) {
- return a->z_order() < b->z_order();
- });
-
ALOGD_IF(TRACE,
"DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
visible_surfaces.size());
-
hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
}
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
index 6853781..3d132c9 100644
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -382,7 +382,7 @@
}
acquired_buffers_.Append(
- AcquiredBuffer(buffer_consumer, std::move(acquire_fence)));
+ AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot));
}
}
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index f9a5dcd..44be0ab 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -5,12 +5,14 @@
#include <fcntl.h>
#include <log/log.h>
#include <poll.h>
+#include <stdint.h>
#include <sync/sync.h>
#include <sys/eventfd.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/system_properties.h>
#include <sys/timerfd.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <utils/Trace.h>
@@ -30,7 +32,9 @@
using android::hardware::Return;
using android::hardware::Void;
+using android::pdx::ErrorStatus;
using android::pdx::LocalHandle;
+using android::pdx::Status;
using android::pdx::rpc::EmptyVariant;
using android::pdx::rpc::IfAnyOf;
@@ -44,9 +48,8 @@
const char kBacklightBrightnessSysFile[] =
"/sys/class/leds/lcd-backlight/brightness";
-const char kPrimaryDisplayWaitPPEventFile[] = "/sys/class/graphics/fb0/wait_pp";
-
const char kDvrPerformanceProperty[] = "sys.dvr.performance";
+const char kDvrStandaloneProperty[] = "ro.boot.vr";
const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
@@ -83,10 +86,30 @@
return true;
}
-} // anonymous namespace
+// Utility to generate scoped tracers with arguments.
+// TODO(eieio): Move/merge this into utils/Trace.h?
+class TraceArgs {
+ public:
+ template <typename... Args>
+ TraceArgs(const char* format, Args&&... args) {
+ std::array<char, 1024> buffer;
+ snprintf(buffer.data(), buffer.size(), format, std::forward<Args>(args)...);
+ atrace_begin(ATRACE_TAG, buffer.data());
+ }
-// HardwareComposer static data;
-constexpr size_t HardwareComposer::kMaxHardwareLayers;
+ ~TraceArgs() { atrace_end(ATRACE_TAG); }
+
+ private:
+ TraceArgs(const TraceArgs&) = delete;
+ void operator=(const TraceArgs&) = delete;
+};
+
+// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro
+// defined in utils/Trace.h.
+#define TRACE_FORMAT(format, ...) \
+ TraceArgs PASTE(__tracer, __LINE__) { format, ##__VA_ARGS__ }
+
+} // anonymous namespace
HardwareComposer::HardwareComposer()
: initialized_(false), request_display_callback_(nullptr) {}
@@ -98,18 +121,20 @@
}
bool HardwareComposer::Initialize(
- Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback) {
+ Hwc2::Composer* composer, RequestDisplayCallback request_display_callback) {
if (initialized_) {
ALOGE("HardwareComposer::Initialize: already initialized.");
return false;
}
+ is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false);
+
request_display_callback_ = request_display_callback;
HWC::Error error = HWC::Error::None;
Hwc2::Config config;
- error = hidl->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
+ error = composer->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
if (error != HWC::Error::None) {
ALOGE("HardwareComposer: Failed to get current display config : %d",
@@ -117,7 +142,7 @@
return false;
}
- error = GetDisplayMetrics(hidl, HWC_DISPLAY_PRIMARY, config,
+ error = GetDisplayMetrics(composer, HWC_DISPLAY_PRIMARY, config,
&native_display_metrics_);
if (error != HWC::Error::None) {
@@ -140,6 +165,9 @@
display_transform_ = HWC_TRANSFORM_NONE;
display_metrics_ = native_display_metrics_;
+ // Setup the display metrics used by all Layer instances.
+ Layer::SetDisplayMetrics(native_display_metrics_);
+
post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
LOG_ALWAYS_FATAL_IF(
!post_thread_event_fd_,
@@ -198,9 +226,17 @@
}
void HardwareComposer::OnPostThreadResumed() {
- hidl_.reset(new Hwc2::Composer("default"));
- hidl_callback_ = new ComposerCallback;
- hidl_->registerCallback(hidl_callback_);
+ // Phones create a new composer client on resume and destroy it on pause.
+ // 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("default"));
+ composer_callback_ = new ComposerCallback;
+ composer_->registerCallback(composer_callback_);
+ Layer::SetComposer(composer_.get());
+ } else {
+ SetPowerMode(true);
+ }
EnableVsync(true);
@@ -217,19 +253,19 @@
void HardwareComposer::OnPostThreadPaused() {
retire_fence_fds_.clear();
- display_surfaces_.clear();
+ layers_.clear();
- for (size_t i = 0; i < kMaxHardwareLayers; ++i) {
- layers_[i].Reset();
- }
- active_layer_count_ = 0;
-
- if (hidl_) {
+ if (composer_) {
EnableVsync(false);
}
- hidl_callback_ = nullptr;
- hidl_.reset(nullptr);
+ if (!is_standalone_device_) {
+ composer_callback_ = nullptr;
+ composer_.reset(nullptr);
+ Layer::SetComposer(nullptr);
+ } else {
+ SetPowerMode(false);
+ }
// Trigger target-specific performance mode change.
property_set(kDvrPerformanceProperty, "idle");
@@ -239,29 +275,35 @@
uint32_t num_types;
uint32_t num_requests;
HWC::Error error =
- hidl_->validateDisplay(display, &num_types, &num_requests);
+ composer_->validateDisplay(display, &num_types, &num_requests);
if (error == HWC2_ERROR_HAS_CHANGES) {
// TODO(skiazyk): We might need to inspect the requested changes first, but
// so far it seems like we shouldn't ever hit a bad state.
// error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_,
// display);
- error = hidl_->acceptDisplayChanges(display);
+ error = composer_->acceptDisplayChanges(display);
}
return error;
}
HWC::Error HardwareComposer::EnableVsync(bool enabled) {
- return hidl_->setVsyncEnabled(
+ return composer_->setVsyncEnabled(
HWC_DISPLAY_PRIMARY,
(Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
: HWC2_VSYNC_DISABLE));
}
+HWC::Error HardwareComposer::SetPowerMode(bool active) {
+ HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off;
+ return composer_->setPowerMode(
+ HWC_DISPLAY_PRIMARY, power_mode.cast<Hwc2::IComposerClient::PowerMode>());
+}
+
HWC::Error HardwareComposer::Present(hwc2_display_t display) {
int32_t present_fence;
- HWC::Error error = hidl_->presentDisplay(display, &present_fence);
+ HWC::Error error = composer_->presentDisplay(display, &present_fence);
// According to the documentation, this fence is signaled at the time of
// vsync/DMA for physical displays.
@@ -275,21 +317,21 @@
return error;
}
-HWC::Error HardwareComposer::GetDisplayAttribute(Hwc2::Composer* hidl,
+HWC::Error HardwareComposer::GetDisplayAttribute(Hwc2::Composer* composer,
hwc2_display_t display,
hwc2_config_t config,
hwc2_attribute_t attribute,
int32_t* out_value) const {
- return hidl->getDisplayAttribute(
+ return composer->getDisplayAttribute(
display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
}
HWC::Error HardwareComposer::GetDisplayMetrics(
- Hwc2::Composer* hidl, hwc2_display_t display, hwc2_config_t config,
+ Hwc2::Composer* composer, hwc2_display_t display, hwc2_config_t config,
HWCDisplayMetrics* out_metrics) const {
HWC::Error error;
- error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_WIDTH,
+ error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_WIDTH,
&out_metrics->width);
if (error != HWC::Error::None) {
ALOGE(
@@ -298,7 +340,7 @@
return error;
}
- error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_HEIGHT,
+ error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_HEIGHT,
&out_metrics->height);
if (error != HWC::Error::None) {
ALOGE(
@@ -307,7 +349,7 @@
return error;
}
- error = GetDisplayAttribute(hidl, display, config,
+ error = GetDisplayAttribute(composer, display, config,
HWC2_ATTRIBUTE_VSYNC_PERIOD,
&out_metrics->vsync_period_ns);
if (error != HWC::Error::None) {
@@ -317,7 +359,7 @@
return error;
}
- error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_DPI_X,
+ error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_X,
&out_metrics->dpi.x);
if (error != HWC::Error::None) {
ALOGE(
@@ -326,7 +368,7 @@
return error;
}
- error = GetDisplayAttribute(hidl, display, config, HWC2_ATTRIBUTE_DPI_Y,
+ error = GetDisplayAttribute(composer, display, config, HWC2_ATTRIBUTE_DPI_Y,
&out_metrics->dpi.y);
if (error != HWC::Error::None) {
ALOGE(
@@ -349,10 +391,10 @@
<< std::endl;
stream << "Post thread resumed: " << post_thread_resumed_ << std::endl;
- stream << "Active layers: " << active_layer_count_ << std::endl;
+ stream << "Active layers: " << layers_.size() << std::endl;
stream << std::endl;
- for (size_t i = 0; i < active_layer_count_; i++) {
+ for (size_t i = 0; i < layers_.size(); i++) {
stream << "Layer " << i << ":";
stream << " type=" << layers_[i].GetCompositionType().to_string();
stream << " surface_id=" << layers_[i].GetSurfaceId();
@@ -363,7 +405,7 @@
if (post_thread_resumed_) {
stream << "Hardware Composer Debug Info:" << std::endl;
- stream << hidl_->dumpDebugInfo();
+ stream << composer_->dumpDebugInfo();
}
return stream.str();
@@ -373,8 +415,8 @@
ATRACE_NAME("HardwareComposer::PostLayers");
// Setup the hardware composer layers with current buffers.
- for (size_t i = 0; i < active_layer_count_; i++) {
- layers_[i].Prepare();
+ for (auto& layer : layers_) {
+ layer.Prepare();
}
HWC::Error error = Validate(HWC_DISPLAY_PRIMARY);
@@ -396,20 +438,18 @@
retire_fence_fds_.erase(retire_fence_fds_.begin());
}
- const bool is_frame_pending = IsFramePendingInDriver();
const bool is_fence_pending = static_cast<int32_t>(retire_fence_fds_.size()) >
post_thread_config_.allowed_pending_fence_count;
- if (is_fence_pending || is_frame_pending) {
+ if (is_fence_pending) {
ATRACE_INT("frame_skip_count", ++frame_skip_count_);
- ALOGW_IF(is_frame_pending, "Warning: frame already queued, dropping frame");
ALOGW_IF(is_fence_pending,
"Warning: dropping a frame to catch up with HWC (pending = %zd)",
retire_fence_fds_.size());
- for (size_t i = 0; i < active_layer_count_; i++) {
- layers_[i].Drop();
+ for (auto& layer : layers_) {
+ layer.Drop();
}
return;
} else {
@@ -419,7 +459,7 @@
}
#if TRACE > 1
- for (size_t i = 0; i < active_layer_count_; i++) {
+ for (size_t i = 0; i < layers_.size(); i++) {
ALOGI("HardwareComposer::PostLayers: layer=%zu buffer_id=%d composition=%s",
i, layers_[i].GetBufferId(),
layers_[i].GetCompositionType().to_string().c_str());
@@ -435,18 +475,18 @@
std::vector<Hwc2::Layer> out_layers;
std::vector<int> out_fences;
- error = hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
- &out_fences);
+ error = composer_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
+ &out_fences);
ALOGE_IF(error != HWC::Error::None,
"HardwareComposer::PostLayers: Failed to get release fences: %s",
error.to_string().c_str());
- // Perform post-frame bookkeeping. Unused layers are a no-op.
+ // Perform post-frame bookkeeping.
uint32_t num_elements = out_layers.size();
for (size_t i = 0; i < num_elements; ++i) {
- for (size_t j = 0; j < active_layer_count_; ++j) {
- if (layers_[j].GetLayerHandle() == out_layers[i]) {
- layers_[j].Finish(out_fences[i]);
+ for (auto& layer : layers_) {
+ if (layer.GetLayerHandle() == out_layers[i]) {
+ layer.Finish(out_fences[i]);
}
}
}
@@ -462,7 +502,7 @@
pending_surfaces_ = std::move(surfaces);
}
- if (request_display_callback_)
+ if (request_display_callback_ && (!is_standalone_device_ || !composer_))
request_display_callback_(!display_idle);
// Set idle state based on whether there are any surfaces to handle.
@@ -530,6 +570,9 @@
// Copy from latest record in shared_config_ring_ to local copy.
DvrConfig record;
if (shared_config_ring_.GetNewest(&shared_config_ring_sequence_, &record)) {
+ ALOGI("DvrConfig updated: sequence %u, post offset %d",
+ shared_config_ring_sequence_, record.frame_post_offset_ns);
+ ++shared_config_ring_sequence_;
post_thread_config_ = record;
}
}
@@ -565,67 +608,35 @@
} else if (pfd[0].revents != 0) {
return 0;
} else if (pfd[1].revents != 0) {
- ALOGI("VrHwcPost thread interrupted");
+ ALOGI("VrHwcPost thread interrupted: revents=%x", pfd[1].revents);
return kPostThreadInterrupted;
} else {
return 0;
}
}
-// Reads the value of the display driver wait_pingpong state. Returns 0 or 1
-// (the value of the state) on success or a negative error otherwise.
-// TODO(eieio): This is pretty driver specific, this should be moved to a
-// separate class eventually.
-int HardwareComposer::ReadWaitPPState() {
- // Gracefully handle when the kernel does not support this feature.
- if (!primary_display_wait_pp_fd_)
- return 0;
-
- const int wait_pp_fd = primary_display_wait_pp_fd_.Get();
- int ret, error;
-
- ret = lseek(wait_pp_fd, 0, SEEK_SET);
- if (ret < 0) {
- error = errno;
- ALOGE("HardwareComposer::ReadWaitPPState: Failed to seek wait_pp fd: %s",
- strerror(error));
- return -error;
- }
-
- char data = -1;
- ret = read(wait_pp_fd, &data, sizeof(data));
- if (ret < 0) {
- error = errno;
- ALOGE("HardwareComposer::ReadWaitPPState: Failed to read wait_pp state: %s",
- strerror(error));
- return -error;
- }
-
- switch (data) {
- case '0':
- return 0;
- case '1':
- return 1;
- default:
- ALOGE(
- "HardwareComposer::ReadWaitPPState: Unexpected value for wait_pp: %d",
- data);
- return -EINVAL;
- }
+Status<int64_t> HardwareComposer::GetVSyncTime() {
+ auto status = composer_callback_->GetVsyncTime(HWC_DISPLAY_PRIMARY);
+ ALOGE_IF(!status,
+ "HardwareComposer::GetVSyncTime: Failed to get vsync timestamp: %s",
+ status.GetErrorMessage().c_str());
+ return status;
}
// Waits for the next vsync and returns the timestamp of the vsync event. If
// vsync already passed since the last call, returns the latest vsync timestamp
// instead of blocking.
-int HardwareComposer::WaitForVSync(int64_t* timestamp) {
- int error = PostThreadPollInterruptible(
- hidl_callback_->GetVsyncEventFd(), POLLIN, /*timeout_ms*/ 1000);
- if (error == kPostThreadInterrupted || error < 0) {
+Status<int64_t> HardwareComposer::WaitForVSync() {
+ const int64_t predicted_vsync_time =
+ last_vsync_timestamp_ +
+ display_metrics_.vsync_period_ns * vsync_prediction_interval_;
+ const int error = SleepUntil(predicted_vsync_time);
+ if (error < 0) {
+ ALOGE("HardwareComposer::WaifForVSync:: Failed to sleep: %s",
+ strerror(-error));
return error;
- } else {
- *timestamp = hidl_callback_->GetVsyncTime();
- return 0;
}
+ return {predicted_vsync_time};
}
int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
@@ -643,8 +654,8 @@
return -error;
}
- return PostThreadPollInterruptible(
- vsync_sleep_timer_fd_, POLLIN, /*timeout_ms*/ -1);
+ return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN,
+ /*timeout_ms*/ -1);
}
void HardwareComposer::PostThread() {
@@ -667,15 +678,6 @@
strerror(errno));
#endif // ENABLE_BACKLIGHT_BRIGHTNESS
- // Open the wait pingpong status node for the primary display.
- // TODO(eieio): Move this into a platform-specific class.
- primary_display_wait_pp_fd_ =
- LocalHandle(kPrimaryDisplayWaitPPEventFile, O_RDONLY);
- ALOGW_IF(
- !primary_display_wait_pp_fd_,
- "HardwareComposer: Failed to open wait_pp node for primary display: %s",
- strerror(errno));
-
// Create a timerfd based on CLOCK_MONOTINIC.
vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
LOG_ALWAYS_FATAL_IF(
@@ -740,26 +742,41 @@
thread_policy_setup =
SetThreadPolicy("graphics:high", "/system/performance");
}
+
+ // Initialize the last vsync timestamp with the current time. The
+ // predictor below uses this time + the vsync interval in absolute time
+ // units for the initial delay. Once the driver starts reporting vsync the
+ // predictor will sync up with the real vsync.
+ last_vsync_timestamp_ = GetSystemClockNs();
}
int64_t vsync_timestamp = 0;
{
- std::array<char, 128> buf;
- snprintf(buf.data(), buf.size(), "wait_vsync|vsync=%d|",
- vsync_count_ + 1);
- ATRACE_NAME(buf.data());
+ TRACE_FORMAT("wait_vsync|vsync=%u;last_timestamp=%" PRId64
+ ";prediction_interval=%d|",
+ vsync_count_ + 1, last_vsync_timestamp_,
+ vsync_prediction_interval_);
- const int error = WaitForVSync(&vsync_timestamp);
+ auto status = WaitForVSync();
ALOGE_IF(
- error < 0,
+ !status,
"HardwareComposer::PostThread: Failed to wait for vsync event: %s",
- strerror(-error));
- // Don't bother processing this frame if a pause was requested
- if (error == kPostThreadInterrupted)
+ status.GetErrorMessage().c_str());
+
+ // If there was an error either sleeping was interrupted due to pausing or
+ // there was an error getting the latest timestamp.
+ if (!status)
continue;
+
+ // Predicted vsync timestamp for this interval. This is stable because we
+ // use absolute time for the wakeup timer.
+ vsync_timestamp = status.get();
}
- ++vsync_count_;
+ // Advance the vsync counter only if the system is keeping up with hardware
+ // vsync to give clients an indication of the delays.
+ if (vsync_prediction_interval_ == 1)
+ ++vsync_count_;
const bool layer_config_changed = UpdateLayerConfig();
@@ -809,6 +826,38 @@
}
}
+ {
+ auto status = GetVSyncTime();
+ if (!status) {
+ ALOGE("HardwareComposer::PostThread: Failed to get VSYNC time: %s",
+ status.GetErrorMessage().c_str());
+ }
+
+ // If we failed to read vsync there might be a problem with the driver.
+ // Since there's nothing we can do just behave as though we didn't get an
+ // updated vsync time and let the prediction continue.
+ const int64_t current_vsync_timestamp =
+ status ? status.get() : last_vsync_timestamp_;
+
+ const bool vsync_delayed =
+ last_vsync_timestamp_ == current_vsync_timestamp;
+ ATRACE_INT("vsync_delayed", vsync_delayed);
+
+ // If vsync was delayed advance the prediction interval and allow the
+ // fence logic in PostLayers() to skip the frame.
+ if (vsync_delayed) {
+ ALOGW(
+ "HardwareComposer::PostThread: VSYNC timestamp did not advance "
+ "since last frame: timestamp=%" PRId64 " prediction_interval=%d",
+ current_vsync_timestamp, vsync_prediction_interval_);
+ vsync_prediction_interval_++;
+ } else {
+ // We have an updated vsync timestamp, reset the prediction interval.
+ last_vsync_timestamp_ = current_vsync_timestamp;
+ vsync_prediction_interval_ = 1;
+ }
+ }
+
PostLayers();
}
}
@@ -827,38 +876,60 @@
ATRACE_NAME("UpdateLayerConfig_HwLayers");
- display_surfaces_.clear();
+ // Sort the new direct surface list by z-order to determine the relative order
+ // of the surfaces. This relative order is used for the HWC z-order value to
+ // insulate VrFlinger and HWC z-order semantics from each other.
+ std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
+ return a->z_order() < b->z_order();
+ });
- Layer* target_layer;
- size_t layer_index;
- for (layer_index = 0;
- layer_index < std::min(surfaces.size(), kMaxHardwareLayers);
- layer_index++) {
+ // Prepare a new layer stack, pulling in layers from the previous
+ // layer stack that are still active and updating their attributes.
+ std::vector<Layer> layers;
+ size_t layer_index = 0;
+ for (const auto& surface : surfaces) {
// The bottom layer is opaque, other layers blend.
HWC::BlendMode blending =
layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage;
- layers_[layer_index].Setup(surfaces[layer_index], native_display_metrics_,
- hidl_.get(), blending,
- display_transform_, HWC::Composition::Device,
- layer_index);
- display_surfaces_.push_back(surfaces[layer_index]);
+
+ // Try to find a layer for this surface in the set of active layers.
+ auto search =
+ std::lower_bound(layers_.begin(), layers_.end(), surface->surface_id());
+ const bool found = search != layers_.end() &&
+ search->GetSurfaceId() == surface->surface_id();
+ if (found) {
+ // Update the attributes of the layer that may have changed.
+ search->SetBlending(blending);
+ search->SetZOrder(layer_index); // Relative z-order.
+
+ // Move the existing layer to the new layer set and remove the empty layer
+ // object from the current set.
+ layers.push_back(std::move(*search));
+ layers_.erase(search);
+ } else {
+ // Insert a layer for the new surface.
+ layers.emplace_back(surface, blending, display_transform_,
+ HWC::Composition::Device, layer_index);
+ }
+
+ ALOGI_IF(
+ TRACE,
+ "HardwareComposer::UpdateLayerConfig: layer_index=%zu surface_id=%d",
+ layer_index, layers[layer_index].GetSurfaceId());
+
+ layer_index++;
}
- // Clear unused layers.
- for (size_t i = layer_index; i < kMaxHardwareLayers; i++)
- layers_[i].Reset();
+ // Sort the new layer stack by ascending surface id.
+ std::sort(layers.begin(), layers.end());
- active_layer_count_ = layer_index;
+ // Replace the previous layer set with the new layer set. The destructor of
+ // the previous set will clean up the remaining Layers that are not moved to
+ // the new layer set.
+ layers_ = std::move(layers);
+
ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
- active_layer_count_);
-
- // Any surfaces left over could not be assigned a hardware layer and will
- // not be displayed.
- ALOGW_IF(surfaces.size() != display_surfaces_.size(),
- "HardwareComposer::UpdateLayerConfig: More surfaces than layers: "
- "pending_surfaces=%zu display_surfaces=%zu",
- surfaces.size(), display_surfaces_.size());
-
+ layers_.size());
return true;
}
@@ -874,17 +945,28 @@
}
}
-HardwareComposer::ComposerCallback::ComposerCallback() {
- vsync_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
- LOG_ALWAYS_FATAL_IF(
- !vsync_event_fd_,
- "Failed to create vsync event fd : %s",
- strerror(errno));
-}
-
Return<void> HardwareComposer::ComposerCallback::onHotplug(
- Hwc2::Display /*display*/,
- IComposerCallback::Connection /*conn*/) {
+ Hwc2::Display display, IComposerCallback::Connection /*conn*/) {
+ // See if the driver supports the vsync_event node in sysfs.
+ if (display < HWC_NUM_PHYSICAL_DISPLAY_TYPES &&
+ !displays_[display].driver_vsync_event_fd) {
+ std::array<char, 1024> buffer;
+ snprintf(buffer.data(), buffer.size(),
+ "/sys/class/graphics/fb%" PRIu64 "/vsync_event", display);
+ if (LocalHandle handle{buffer.data(), O_RDONLY}) {
+ ALOGI(
+ "HardwareComposer::ComposerCallback::onHotplug: Driver supports "
+ "vsync_event node for display %" PRIu64,
+ display);
+ displays_[display].driver_vsync_event_fd = std::move(handle);
+ } else {
+ ALOGI(
+ "HardwareComposer::ComposerCallback::onHotplug: Driver does not "
+ "support vsync_event node for display %" PRIu64,
+ display);
+ }
+ }
+
return Void();
}
@@ -893,40 +975,94 @@
return hardware::Void();
}
-Return<void> HardwareComposer::ComposerCallback::onVsync(
- Hwc2::Display display, int64_t timestamp) {
- if (display == HWC_DISPLAY_PRIMARY) {
- std::lock_guard<std::mutex> lock(vsync_mutex_);
- vsync_time_ = timestamp;
- int error = eventfd_write(vsync_event_fd_.Get(), 1);
- LOG_ALWAYS_FATAL_IF(error != 0, "Failed writing to vsync event fd");
+Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
+ int64_t timestamp) {
+ TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
+ display, timestamp);
+ if (display < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+ displays_[display].callback_vsync_timestamp = timestamp;
+ } else {
+ ALOGW(
+ "HardwareComposer::ComposerCallback::onVsync: Received vsync on "
+ "non-physical display: display=%" PRId64,
+ display);
}
return Void();
}
-const pdx::LocalHandle&
-HardwareComposer::ComposerCallback::GetVsyncEventFd() const {
- return vsync_event_fd_;
+Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
+ Hwc2::Display display) {
+ if (display >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+ ALOGE(
+ "HardwareComposer::ComposerCallback::GetVsyncTime: Invalid physical "
+ "display requested: display=%" PRIu64,
+ display);
+ return ErrorStatus(EINVAL);
+ }
+
+ // See if the driver supports direct vsync events.
+ LocalHandle& event_fd = displays_[display].driver_vsync_event_fd;
+ if (!event_fd) {
+ // Fall back to returning the last timestamp returned by the vsync
+ // callback.
+ std::lock_guard<std::mutex> autolock(vsync_mutex_);
+ return displays_[display].callback_vsync_timestamp;
+ }
+
+ // When the driver supports the vsync_event sysfs node we can use it to
+ // determine the latest vsync timestamp, even if the HWC callback has been
+ // delayed.
+
+ // The driver returns data in the form "VSYNC=<timestamp ns>".
+ std::array<char, 32> data;
+ data.fill('\0');
+
+ // Seek back to the beginning of the event file.
+ int ret = lseek(event_fd.Get(), 0, SEEK_SET);
+ if (ret < 0) {
+ const int error = errno;
+ ALOGE(
+ "HardwareComposer::ComposerCallback::GetVsyncTime: Failed to seek "
+ "vsync event fd: %s",
+ strerror(error));
+ return ErrorStatus(error);
+ }
+
+ // Read the vsync event timestamp.
+ ret = read(event_fd.Get(), data.data(), data.size());
+ if (ret < 0) {
+ const int error = errno;
+ ALOGE_IF(error != EAGAIN,
+ "HardwareComposer::ComposerCallback::GetVsyncTime: Error "
+ "while reading timestamp: %s",
+ strerror(error));
+ return ErrorStatus(error);
+ }
+
+ int64_t timestamp;
+ ret = sscanf(data.data(), "VSYNC=%" PRIu64,
+ reinterpret_cast<uint64_t*>(×tamp));
+ if (ret < 0) {
+ const int error = errno;
+ ALOGE(
+ "HardwareComposer::ComposerCallback::GetVsyncTime: Error while "
+ "parsing timestamp: %s",
+ strerror(error));
+ return ErrorStatus(error);
+ }
+
+ return {timestamp};
}
-int64_t HardwareComposer::ComposerCallback::GetVsyncTime() {
- std::lock_guard<std::mutex> lock(vsync_mutex_);
- eventfd_t event;
- eventfd_read(vsync_event_fd_.Get(), &event);
- LOG_ALWAYS_FATAL_IF(vsync_time_ < 0,
- "Attempt to read vsync time before vsync event");
- int64_t return_val = vsync_time_;
- vsync_time_ = -1;
- return return_val;
-}
+Hwc2::Composer* Layer::composer_{nullptr};
+HWCDisplayMetrics Layer::display_metrics_{0, 0, {0, 0}, 0};
void Layer::Reset() {
- if (hidl_ != nullptr && hardware_composer_layer_) {
- hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+ if (hardware_composer_layer_) {
+ composer_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
hardware_composer_layer_ = 0;
}
- hidl_ = nullptr;
z_order_ = 0;
blending_ = HWC::BlendMode::None;
transform_ = HWC::Transform::None;
@@ -935,38 +1071,54 @@
source_ = EmptyVariant{};
acquire_fence_.Close();
surface_rect_functions_applied_ = false;
+ pending_visibility_settings_ = true;
+ cached_buffer_map_.clear();
}
-void Layer::Setup(const std::shared_ptr<DirectDisplaySurface>& surface,
- const HWCDisplayMetrics& display_metrics,
- Hwc2::Composer* hidl, HWC::BlendMode blending,
- HWC::Transform transform, HWC::Composition composition_type,
- size_t z_order) {
- Reset();
- hidl_ = hidl;
- z_order_ = z_order;
- blending_ = blending;
- transform_ = transform;
- composition_type_ = HWC::Composition::Invalid;
- target_composition_type_ = composition_type;
- source_ = SourceSurface{surface};
- CommonLayerSetup(display_metrics);
+Layer::Layer(const std::shared_ptr<DirectDisplaySurface>& surface,
+ HWC::BlendMode blending, HWC::Transform transform,
+ HWC::Composition composition_type, size_t z_order)
+ : z_order_{z_order},
+ blending_{blending},
+ transform_{transform},
+ target_composition_type_{composition_type},
+ source_{SourceSurface{surface}} {
+ CommonLayerSetup();
}
-void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer,
- const HWCDisplayMetrics& display_metrics,
- Hwc2::Composer* hidl, HWC::BlendMode blending,
- HWC::Transform transform, HWC::Composition composition_type,
- size_t z_order) {
- Reset();
- hidl_ = hidl;
- z_order_ = z_order;
- blending_ = blending;
- transform_ = transform;
- composition_type_ = HWC::Composition::Invalid;
- target_composition_type_ = composition_type;
- source_ = SourceBuffer{buffer};
- CommonLayerSetup(display_metrics);
+Layer::Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+ HWC::Transform transform, HWC::Composition composition_type,
+ size_t z_order)
+ : z_order_{z_order},
+ blending_{blending},
+ transform_{transform},
+ target_composition_type_{composition_type},
+ source_{SourceBuffer{buffer}} {
+ CommonLayerSetup();
+}
+
+Layer::~Layer() { Reset(); }
+
+Layer::Layer(Layer&& other) { *this = std::move(other); }
+
+Layer& Layer::operator=(Layer&& other) {
+ if (this != &other) {
+ Reset();
+ using std::swap;
+ swap(hardware_composer_layer_, other.hardware_composer_layer_);
+ swap(z_order_, other.z_order_);
+ swap(blending_, other.blending_);
+ swap(transform_, other.transform_);
+ swap(composition_type_, other.composition_type_);
+ swap(target_composition_type_, other.target_composition_type_);
+ swap(source_, other.source_);
+ swap(acquire_fence_, other.acquire_fence_);
+ swap(surface_rect_functions_applied_,
+ other.surface_rect_functions_applied_);
+ swap(pending_visibility_settings_, other.pending_visibility_settings_);
+ swap(cached_buffer_map_, other.cached_buffer_map_);
+ }
+ return *this;
}
void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) {
@@ -974,8 +1126,19 @@
std::get<SourceBuffer>(source_) = {buffer};
}
-void Layer::SetBlending(HWC::BlendMode blending) { blending_ = blending; }
-void Layer::SetZOrder(size_t z_order) { z_order_ = z_order; }
+void Layer::SetBlending(HWC::BlendMode blending) {
+ if (blending_ != blending) {
+ blending_ = blending;
+ pending_visibility_settings_ = true;
+ }
+}
+
+void Layer::SetZOrder(size_t z_order) {
+ if (z_order_ != z_order) {
+ z_order_ = z_order;
+ pending_visibility_settings_ = true;
+ }
+}
IonBuffer* Layer::GetBuffer() {
struct Visitor {
@@ -986,91 +1149,108 @@
return source_.Visit(Visitor{});
}
-void Layer::UpdateLayerSettings(const HWCDisplayMetrics& display_metrics) {
- if (!IsLayerSetup()) {
- ALOGE(
- "HardwareComposer::Layer::UpdateLayerSettings: Attempt to update "
- "unused Layer!");
- return;
- }
+void Layer::UpdateVisibilitySettings() {
+ if (pending_visibility_settings_) {
+ pending_visibility_settings_ = false;
+ HWC::Error error;
+ hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+
+ error = composer_->setLayerBlendMode(
+ display, hardware_composer_layer_,
+ blending_.cast<Hwc2::IComposerClient::BlendMode>());
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
+ error.to_string().c_str());
+
+ error =
+ composer_->setLayerZOrder(display, hardware_composer_layer_, z_order_);
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting z_ order: %s",
+ error.to_string().c_str());
+ }
+}
+
+void Layer::UpdateLayerSettings() {
HWC::Error error;
hwc2_display_t display = HWC_DISPLAY_PRIMARY;
- error = hidl_->setLayerCompositionType(
- display, hardware_composer_layer_,
- composition_type_.cast<Hwc2::IComposerClient::Composition>());
- ALOGE_IF(
- error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer composition type: %s",
- error.to_string().c_str());
-
- error = hidl_->setLayerBlendMode(
- display, hardware_composer_layer_,
- blending_.cast<Hwc2::IComposerClient::BlendMode>());
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
- error.to_string().c_str());
+ UpdateVisibilitySettings();
// TODO(eieio): Use surface attributes or some other mechanism to control
// the layer display frame.
- error = hidl_->setLayerDisplayFrame(
+ error = composer_->setLayerDisplayFrame(
display, hardware_composer_layer_,
- {0, 0, display_metrics.width, display_metrics.height});
+ {0, 0, display_metrics_.width, display_metrics_.height});
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer display frame: %s",
error.to_string().c_str());
- error = hidl_->setLayerVisibleRegion(
+ error = composer_->setLayerVisibleRegion(
display, hardware_composer_layer_,
- {{0, 0, display_metrics.width, display_metrics.height}});
+ {{0, 0, display_metrics_.width, display_metrics_.height}});
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer visible region: %s",
error.to_string().c_str());
- error = hidl_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f);
+ error =
+ composer_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f);
ALOGE_IF(error != HWC::Error::None,
"Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
error.to_string().c_str());
-
- error = hidl_->setLayerZOrder(display, hardware_composer_layer_, z_order_);
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting z_ order: %s",
- error.to_string().c_str());
}
-void Layer::CommonLayerSetup(const HWCDisplayMetrics& display_metrics) {
+void Layer::CommonLayerSetup() {
HWC::Error error =
- hidl_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_);
- ALOGE_IF(
- error != HWC::Error::None,
- "Layer::CommonLayerSetup: Failed to create layer on primary display: %s",
- error.to_string().c_str());
- UpdateLayerSettings(display_metrics);
+ composer_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_);
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::CommonLayerSetup: Failed to create layer on primary "
+ "display: %s",
+ error.to_string().c_str());
+ UpdateLayerSettings();
+}
+
+bool Layer::CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id) {
+ auto search = cached_buffer_map_.find(slot);
+ if (search != cached_buffer_map_.end() && search->second == buffer_id)
+ return true;
+
+ // Assign or update the buffer slot.
+ if (buffer_id >= 0)
+ cached_buffer_map_[slot] = buffer_id;
+ return false;
}
void Layer::Prepare() {
- int right, bottom;
+ int right, bottom, id;
sp<GraphicBuffer> handle;
+ std::size_t slot;
// Acquire the next buffer according to the type of source.
IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) {
- std::tie(right, bottom, handle, acquire_fence_) = source.Acquire();
+ std::tie(right, bottom, id, handle, acquire_fence_, slot) =
+ source.Acquire();
});
- // When a layer is first setup there may be some time before the first buffer
- // arrives. Setup the HWC layer as a solid color to stall for time until the
- // first buffer arrives. Once the first buffer arrives there will always be a
- // buffer for the frame even if it is old.
+ TRACE_FORMAT("Layer::Prepare|buffer_id=%d;slot=%zu|", id, slot);
+
+ // Update any visibility (blending, z-order) changes that occurred since
+ // last prepare.
+ UpdateVisibilitySettings();
+
+ // When a layer is first setup there may be some time before the first
+ // buffer arrives. Setup the HWC layer as a solid color to stall for time
+ // until the first buffer arrives. Once the first buffer arrives there will
+ // always be a buffer for the frame even if it is old.
if (!handle.get()) {
if (composition_type_ == HWC::Composition::Invalid) {
composition_type_ = HWC::Composition::SolidColor;
- hidl_->setLayerCompositionType(
+ composer_->setLayerCompositionType(
HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
composition_type_.cast<Hwc2::IComposerClient::Composition>());
Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
- hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
- layer_color);
+ composer_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+ layer_color);
} else {
// The composition type is already set. Nothing else to do until a
// buffer arrives.
@@ -1078,15 +1258,20 @@
} else {
if (composition_type_ != target_composition_type_) {
composition_type_ = target_composition_type_;
- hidl_->setLayerCompositionType(
+ composer_->setLayerCompositionType(
HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
composition_type_.cast<Hwc2::IComposerClient::Composition>());
}
+ // See if the HWC cache already has this buffer.
+ const bool cached = CheckAndUpdateCachedBuffer(slot, id);
+ if (cached)
+ handle = nullptr;
+
HWC::Error error{HWC::Error::None};
- error = hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY,
- hardware_composer_layer_, 0, handle,
- acquire_fence_.Get());
+ error =
+ composer_->setLayerBuffer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+ slot, handle, acquire_fence_.Get());
ALOGE_IF(error != HWC::Error::None,
"Layer::Prepare: Error setting layer buffer: %s",
@@ -1095,9 +1280,9 @@
if (!surface_rect_functions_applied_) {
const float float_right = right;
const float float_bottom = bottom;
- error = hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
- hardware_composer_layer_,
- {0, 0, float_right, float_bottom});
+ error = composer_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
+ hardware_composer_layer_,
+ {0, 0, float_right, float_bottom});
ALOGE_IF(error != HWC::Error::None,
"Layer::Prepare: Error setting layer source crop: %s",
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index fc0efee..7010db9 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -52,10 +52,7 @@
// source supplying buffers for the layer's contents.
class Layer {
public:
- Layer() {}
-
- // Releases any shared pointers and fence handles held by this instance.
- void Reset();
+ Layer() = default;
// Sets up the layer to use a display surface as its content source. The Layer
// automatically handles ACQUIRE/RELEASE phases for the surface's buffer train
@@ -66,10 +63,9 @@
// |composition_type| receives either HWC_FRAMEBUFFER for most layers or
// HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
// |index| is the index of this surface in the DirectDisplaySurface array.
- void Setup(const std::shared_ptr<DirectDisplaySurface>& surface,
- const HWCDisplayMetrics& display_metrics, Hwc2::Composer* hidl,
- HWC::BlendMode blending, HWC::Transform transform,
- HWC::Composition composition_type, size_t z_roder);
+ Layer(const std::shared_ptr<DirectDisplaySurface>& surface,
+ HWC::BlendMode blending, HWC::Transform transform,
+ HWC::Composition composition_type, size_t z_roder);
// Sets up the layer to use a direct buffer as its content source. No special
// handling of the buffer is performed; responsibility for updating or
@@ -79,10 +75,17 @@
// |transform| receives HWC_TRANSFORM_* values.
// |composition_type| receives either HWC_FRAMEBUFFER for most layers or
// HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
- void Setup(const std::shared_ptr<IonBuffer>& buffer,
- const HWCDisplayMetrics& display_metrics, Hwc2::Composer* hidl,
- HWC::BlendMode blending, HWC::Transform transform,
- HWC::Composition composition_type, size_t z_order);
+ Layer(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+ HWC::Transform transform, HWC::Composition composition_type,
+ size_t z_order);
+
+ Layer(Layer&&);
+ Layer& operator=(Layer&&);
+
+ ~Layer();
+
+ // Releases any shared pointers and fence handles held by this instance.
+ void Reset();
// Layers that use a direct IonBuffer should call this each frame to update
// which buffer will be used for the next PostLayers.
@@ -117,9 +120,6 @@
HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; }
bool IsLayerSetup() const { return !source_.empty(); }
- // Applies all of the settings to this layer using the hwc functions
- void UpdateLayerSettings(const HWCDisplayMetrics& display_metrics);
-
int GetSurfaceId() const {
int surface_id = -1;
pdx::rpc::IfAnyOf<SourceSurface>::Call(
@@ -138,10 +138,47 @@
return buffer_id;
}
- private:
- void CommonLayerSetup(const HWCDisplayMetrics& display_metrics);
+ // Compares Layers by surface id.
+ bool operator<(const Layer& other) const {
+ return GetSurfaceId() < other.GetSurfaceId();
+ }
+ bool operator<(int surface_id) const { return GetSurfaceId() < surface_id; }
- Hwc2::Composer* hidl_ = nullptr;
+ // Sets the composer instance used by all Layer instances.
+ static void SetComposer(Hwc2::Composer* composer) { composer_ = composer; }
+
+ // Sets the display metrics used by all Layer instances.
+ static void SetDisplayMetrics(HWCDisplayMetrics display_metrics) {
+ display_metrics_ = display_metrics;
+ }
+
+ private:
+ void CommonLayerSetup();
+
+ // Applies all of the settings to this layer using the hwc functions
+ void UpdateLayerSettings();
+
+ // Applies visibility settings that may have changed.
+ void UpdateVisibilitySettings();
+
+ // Checks whether the buffer, given by id, is associated with the given slot
+ // in the HWC buffer cache. If the slot is not associated with the given
+ // buffer the cache is updated to establish the association and the buffer
+ // should be sent to HWC using setLayerBuffer. Returns true if the association
+ // was already established, false if not. A buffer_id of -1 is never
+ // associated and always returns false.
+ bool CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id);
+
+ // Composer instance shared by all instances of Layer. This must be set
+ // whenever a new instance of the Composer is created. This may be set to
+ // nullptr as long as there are no instances of Layer that might need to use
+ // it.
+ static Hwc2::Composer* composer_;
+
+ // Display metrics shared by all instances of Layer. This must be set at least
+ // once during VrFlinger initialization and is expected to remain constant
+ // thereafter.
+ static HWCDisplayMetrics display_metrics_;
// The hardware composer layer and metrics to use during the prepare cycle.
hwc2_layer_t hardware_composer_layer_ = 0;
@@ -169,19 +206,21 @@
// the previous buffer is returned or an empty value if no buffer has ever
// been posted. When a new buffer is acquired the previous buffer's release
// fence is passed out automatically.
- std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+ std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
+ Acquire() {
if (surface->IsBufferAvailable()) {
acquired_buffer.Release(std::move(release_fence));
acquired_buffer = surface->AcquireCurrentBuffer();
ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id());
}
if (!acquired_buffer.IsEmpty()) {
- return std::make_tuple(acquired_buffer.buffer()->width(),
- acquired_buffer.buffer()->height(),
- acquired_buffer.buffer()->buffer()->buffer(),
- acquired_buffer.ClaimAcquireFence());
+ return std::make_tuple(
+ acquired_buffer.buffer()->width(),
+ acquired_buffer.buffer()->height(), acquired_buffer.buffer()->id(),
+ acquired_buffer.buffer()->buffer()->buffer(),
+ acquired_buffer.ClaimAcquireFence(), acquired_buffer.slot());
} else {
- return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+ return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
}
}
@@ -213,12 +252,13 @@
struct SourceBuffer {
std::shared_ptr<IonBuffer> buffer;
- std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+ std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
+ Acquire() {
if (buffer)
- return std::make_tuple(buffer->width(), buffer->height(),
- buffer->buffer(), pdx::LocalHandle{});
+ return std::make_tuple(buffer->width(), buffer->height(), -1,
+ buffer->buffer(), pdx::LocalHandle{}, 0);
else
- return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+ return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
}
void Finish(pdx::LocalHandle /*fence*/) {}
@@ -235,6 +275,13 @@
pdx::LocalHandle acquire_fence_;
bool surface_rect_functions_applied_ = false;
+ bool pending_visibility_settings_ = true;
+
+ // Map of buffer slot assignments that have already been established with HWC:
+ // slot -> buffer_id. When this map contains a matching slot and buffer_id the
+ // buffer argument to setLayerBuffer may be nullptr to avoid the cost of
+ // importing a buffer HWC already knows about.
+ std::map<std::size_t, int> cached_buffer_map_;
Layer(const Layer&) = delete;
void operator=(const Layer&) = delete;
@@ -254,14 +301,10 @@
using VSyncCallback = std::function<void(int, int64_t, int64_t, uint32_t)>;
using RequestDisplayCallback = std::function<void(bool)>;
- // Since there is no universal way to query the number of hardware layers,
- // just set it to 4 for now.
- static constexpr size_t kMaxHardwareLayers = 4;
-
HardwareComposer();
~HardwareComposer();
- bool Initialize(Hwc2::Composer* hidl,
+ bool Initialize(Hwc2::Composer* composer,
RequestDisplayCallback request_display_callback);
bool IsInitialized() const { return initialized_; }
@@ -299,30 +342,36 @@
void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
private:
- HWC::Error GetDisplayAttribute(Hwc2::Composer* hidl, hwc2_display_t display,
- hwc2_config_t config,
+ HWC::Error GetDisplayAttribute(Hwc2::Composer* composer,
+ hwc2_display_t display, hwc2_config_t config,
hwc2_attribute_t attributes,
int32_t* out_value) const;
- HWC::Error GetDisplayMetrics(Hwc2::Composer* hidl, hwc2_display_t display,
+ HWC::Error GetDisplayMetrics(Hwc2::Composer* composer, hwc2_display_t display,
hwc2_config_t config,
HWCDisplayMetrics* out_metrics) const;
HWC::Error EnableVsync(bool enabled);
+ HWC::Error SetPowerMode(bool active);
class ComposerCallback : public Hwc2::IComposerCallback {
public:
- ComposerCallback();
+ ComposerCallback() = default;
hardware::Return<void> onHotplug(Hwc2::Display display,
Connection conn) override;
hardware::Return<void> onRefresh(Hwc2::Display display) override;
hardware::Return<void> onVsync(Hwc2::Display display,
int64_t timestamp) override;
- const pdx::LocalHandle& GetVsyncEventFd() const;
- int64_t GetVsyncTime();
+
+ pdx::Status<int64_t> GetVsyncTime(Hwc2::Display display);
+
private:
std::mutex vsync_mutex_;
- pdx::LocalHandle vsync_event_fd_;
- int64_t vsync_time_ = -1;
+
+ struct Display {
+ pdx::LocalHandle driver_vsync_event_fd;
+ int64_t callback_vsync_timestamp{0};
+ };
+ std::array<Display, HWC_NUM_PHYSICAL_DISPLAY_TYPES> displays_;
};
HWC::Error Validate(hwc2_display_t display);
@@ -356,18 +405,16 @@
// the case of a timeout. If we're interrupted, kPostThreadInterrupted will be
// returned.
int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
- int requested_events,
- int timeout_ms);
+ int requested_events, int timeout_ms);
// WaitForVSync and SleepUntil are blocking calls made on the post thread that
// can be interrupted by a control thread. If interrupted, these calls return
// kPostThreadInterrupted.
int ReadWaitPPState();
- int WaitForVSync(int64_t* timestamp);
+ pdx::Status<int64_t> WaitForVSync();
+ pdx::Status<int64_t> GetVSyncTime();
int SleepUntil(int64_t wakeup_timestamp);
- bool IsFramePendingInDriver() { return ReadWaitPPState() == 1; }
-
// Reconfigures the layer stack if the display surfaces changed since the last
// frame. Called only from the post thread.
bool UpdateLayerConfig();
@@ -385,9 +432,10 @@
void UpdateConfigBuffer();
bool initialized_;
+ bool is_standalone_device_;
- std::unique_ptr<Hwc2::Composer> hidl_;
- sp<ComposerCallback> hidl_callback_;
+ std::unique_ptr<Hwc2::Composer> composer_;
+ sp<ComposerCallback> composer_callback_;
RequestDisplayCallback request_display_callback_;
// Display metrics of the physical display.
@@ -403,13 +451,9 @@
// thread and read by the post thread.
std::vector<std::shared_ptr<DirectDisplaySurface>> pending_surfaces_;
- // The surfaces displayed by the post thread. Used exclusively by the post
- // thread.
- std::vector<std::shared_ptr<DirectDisplaySurface>> display_surfaces_;
-
- // Layer array for handling buffer flow into hardware composer layers.
- std::array<Layer, kMaxHardwareLayers> layers_;
- size_t active_layer_count_ = 0;
+ // Layer set for handling buffer flow into hardware composer layers. This
+ // vector must be sorted by surface_id in ascending order.
+ std::vector<Layer> layers_;
// Handler to hook vsync events outside of this class.
VSyncCallback vsync_callback_;
@@ -419,8 +463,8 @@
std::thread post_thread_;
// Post thread state machine and synchronization primitives.
- PostThreadStateType post_thread_state_{
- PostThreadState::Idle | PostThreadState::Suspended};
+ PostThreadStateType post_thread_state_{PostThreadState::Idle |
+ PostThreadState::Suspended};
std::atomic<bool> post_thread_quiescent_{true};
bool post_thread_resumed_{false};
pdx::LocalHandle post_thread_event_fd_;
@@ -431,15 +475,15 @@
// Backlight LED brightness sysfs node.
pdx::LocalHandle backlight_brightness_fd_;
- // Primary display wait_pingpong state sysfs node.
- pdx::LocalHandle primary_display_wait_pp_fd_;
-
// VSync sleep timerfd.
pdx::LocalHandle vsync_sleep_timer_fd_;
// The timestamp of the last vsync.
int64_t last_vsync_timestamp_ = 0;
+ // The number of vsync intervals to predict since the last vsync.
+ int vsync_prediction_interval_ = 1;
+
// Vsync count since display on.
uint32_t vsync_count_ = 0;
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index fcf94f0..85dc586 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -64,9 +64,6 @@
ALOGI("Starting up VrFlinger...");
- setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
- set_sched_policy(0, SP_FOREGROUND);
-
// We need to be able to create endpoints with full perms.
umask(0000);
@@ -100,6 +97,9 @@
prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
ALOGI("Entering message loop.");
+ setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
+ set_sched_policy(0, SP_FOREGROUND);
+
int ret = dispatcher_->EnterDispatchLoop();
if (ret < 0) {
ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
index 3098b43..fdeb899 100644
--- a/libs/vr/libvrflinger/vsync_service.cpp
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -110,6 +110,7 @@
}
pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
+ ATRACE_NAME("VSyncService::HandleMessage");
switch (message.GetOp()) {
case VSyncProtocol::Wait::Opcode:
AddWaiter(message);
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
index d684ddc..bb25f1d 100644
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -157,6 +157,18 @@
// @return Zero on success
int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled);
+// Requests a burst of data samples from pose service. The data samples are
+// passed through a shared memory buffer obtained by calling
+// dvrPoseClientGetDataReader().
+//
+// @param DvrPoseDataCaptureRequest Parameters on how to capture data.
+// @return Zero on success.
+int dvrPoseClientDataCapture(DvrPoseClient* client,
+ const DvrPoseDataCaptureRequest* request);
+
+// Destroys the write buffer queue for the given |data_type|.
+int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
index e4455f1..7bf1cd4 100644
--- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
+++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
@@ -17,6 +17,9 @@
DVR_POSE_GET_CONTROLLER_RING_BUFFER,
DVR_POSE_LOG_CONTROLLER,
DVR_POSE_SENSORS_ENABLE,
+ DVR_POSE_GET_TANGO_READER,
+ DVR_POSE_DATA_CAPTURE,
+ DVR_POSE_TANGO_READER_DESTROY,
};
#ifdef __cplusplus
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000..39592bb
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+
+using android::dvr::ConsumerQueue;
+
+typedef struct DvrPoseClient DvrPoseClient;
+
+namespace android {
+namespace dvr {
+
+int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
+ ConsumerQueue **queue_out);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
index 546c2b6..4acc085 100644
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -9,10 +9,12 @@
#include <pdx/default_transport/client_channel_factory.h>
#include <pdx/file_handle.h>
#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
#include <private/dvr/display_client.h>
#include <private/dvr/pose-ipc.h>
#include <private/dvr/shared_buffer_helpers.h>
+using android::dvr::ConsumerQueue;
using android::pdx::LocalHandle;
using android::pdx::LocalChannelHandle;
using android::pdx::Status;
@@ -139,6 +141,44 @@
return ReturnStatusOrError(status);
}
+ int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
+ // Get buffer.
+ Transaction trans{*this};
+ Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+ DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0);
+
+ if (!status) {
+ ALOGE("PoseClient GetTangoReaderHandle() failed because: %s",
+ status.GetErrorMessage().c_str());
+ *queue_out = nullptr;
+ return -status.error();
+ }
+
+ std::unique_ptr<ConsumerQueue> consumer_queue =
+ ConsumerQueue::Import(status.take());
+ *queue_out = consumer_queue.release();
+ return 0;
+ }
+
+ int DataCapture(const DvrPoseDataCaptureRequest* request) {
+ Transaction trans{*this};
+ Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request,
+ sizeof(*request), nullptr, 0);
+ ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ int DataReaderDestroy(uint64_t data_type) {
+ Transaction trans{*this};
+ Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY,
+ &data_type, sizeof(data_type), nullptr,
+ 0);
+ ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
// Enables or disables all pose processing from sensors
int EnableSensors(bool enabled) {
Transaction trans{*this};
@@ -256,6 +296,11 @@
ControllerClientState controllers_[MAX_CONTROLLERS];
};
+int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
+ ConsumerQueue** queue_out) {
+ return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
+}
+
} // namespace dvr
} // namespace android
@@ -307,9 +352,17 @@
return PoseClient::FromC(client)->GetMode(mode);
}
-
int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) {
return PoseClient::FromC(client)->EnableSensors(enabled);
}
+int dvrPoseClientDataCapture(DvrPoseClient* client,
+ const DvrPoseDataCaptureRequest* request) {
+ return PoseClient::FromC(client)->DataCapture(request);
+}
+
+int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
+ return PoseClient::FromC(client)->DataReaderDestroy(data_type);
+}
+
} // extern "C"
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/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
index f6813fd..66836b5 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
@@ -21,7 +21,7 @@
#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..fb75d81 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
@@ -21,7 +21,7 @@
#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/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..03e16e9 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -21,7 +21,7 @@
#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/audiomanager/Android.bp b/services/audiomanager/Android.bp
index 22b084a..12ad47e 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -3,7 +3,6 @@
srcs: [
"IAudioManager.cpp",
- "IPlayer.cpp",
],
shared_libs: [
diff --git a/services/audiomanager/IPlayer.cpp b/services/audiomanager/IPlayer.cpp
deleted file mode 100644
index e8a9c34..0000000
--- a/services/audiomanager/IPlayer.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
-**
-** 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 "IPlayer"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <audiomanager/IPlayer.h>
-
-namespace android {
-
-enum {
- START = IBinder::FIRST_CALL_TRANSACTION,
- PAUSE = IBinder::FIRST_CALL_TRANSACTION + 1,
- STOP = IBinder::FIRST_CALL_TRANSACTION + 2,
- SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3,
- SET_PAN = IBinder::FIRST_CALL_TRANSACTION + 4,
- SET_START_DELAY_MS = IBinder::FIRST_CALL_TRANSACTION + 5,
- APPLY_VOLUME_SHAPER = IBinder::FIRST_CALL_TRANSACTION + 6,
-};
-
-class BpPlayer : public BpInterface<IPlayer>
-{
-public:
- explicit BpPlayer(const sp<IBinder>& impl)
- : BpInterface<IPlayer>(impl)
- {
- }
-
- virtual void start()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
- remote()->transact(START, data, &reply);
- }
-
- virtual void pause()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
- remote()->transact(PAUSE, data, &reply);
- }
-
- virtual void stop()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
- remote()->transact(STOP, data, &reply);
- }
-
- virtual void setVolume(float vol)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
- data.writeFloat(vol);
- remote()->transact(SET_VOLUME, data, &reply);
- }
-
- virtual void setPan(float pan)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
- data.writeFloat(pan);
- remote()->transact(SET_PAN, data, &reply);
- }
-
- virtual void setStartDelayMs(int32_t delayMs) {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
- data.writeInt32(delayMs);
- remote()->transact(SET_START_DELAY_MS, data, &reply);
- }
-
- virtual void applyVolumeShaper(
- const sp<VolumeShaper::Configuration>& configuration,
- const sp<VolumeShaper::Operation>& operation) {
- Parcel data, reply;
- data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
-
- status_t status = configuration.get() == nullptr
- ? data.writeInt32(0)
- : data.writeInt32(1)
- ?: configuration->writeToParcel(&data);
- if (status != NO_ERROR) {
- ALOGW("applyVolumeShaper failed configuration parceling: %d", status);
- return; // ignore error
- }
-
- status = operation.get() == nullptr
- ? status = data.writeInt32(0)
- : data.writeInt32(1)
- ?: operation->writeToParcel(&data);
- if (status != NO_ERROR) {
- ALOGW("applyVolumeShaper failed operation parceling: %d", status);
- return; // ignore error
- }
-
- status = remote()->transact(APPLY_VOLUME_SHAPER, data, &reply);
-
- ALOGW_IF(status != NO_ERROR, "applyVolumeShaper failed transact: %d", status);
- return; // one way transaction, ignore error
- }
-};
-
-IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer");
-
-// ----------------------------------------------------------------------
-
-status_t BnPlayer::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case START: {
- CHECK_INTERFACE(IPlayer, data, reply);
- start();
- return NO_ERROR;
- } break;
- case PAUSE: {
- CHECK_INTERFACE(IPlayer, data, reply);
- pause();
- return NO_ERROR;
- }
- case STOP: {
- CHECK_INTERFACE(IPlayer, data, reply);
- stop();
- return NO_ERROR;
- } break;
- case SET_VOLUME: {
- CHECK_INTERFACE(IPlayer, data, reply);
- setVolume(data.readFloat());
- return NO_ERROR;
- } break;
- case SET_PAN: {
- CHECK_INTERFACE(IPlayer, data, reply);
- setPan(data.readFloat());
- return NO_ERROR;
- } break;
- case SET_START_DELAY_MS: {
- CHECK_INTERFACE(IPlayer, data, reply);
- setStartDelayMs(data.readInt32());
- return NO_ERROR;
- } break;
- case APPLY_VOLUME_SHAPER: {
- CHECK_INTERFACE(IPlayer, data, reply);
- sp<VolumeShaper::Configuration> configuration;
- sp<VolumeShaper::Operation> operation;
-
- int32_t present;
- status_t status = data.readInt32(&present);
- if (status == NO_ERROR && present != 0) {
- configuration = new VolumeShaper::Configuration();
- status = configuration->readFromParcel(data);
- }
- status = status ?: data.readInt32(&present);
- if (status == NO_ERROR && present != 0) {
- operation = new VolumeShaper::Operation();
- status = operation->readFromParcel(data);
- }
- if (status == NO_ERROR) {
- // one way transaction, no error returned
- applyVolumeShaper(configuration, operation);
- }
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-} // namespace android
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..0312185 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -685,7 +685,7 @@
bool InputDispatcher::dispatchConfigurationChangedLocked(
nsecs_t currentTime, ConfigurationChangedEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
+ ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime);
#endif
// Reset key repeating in case a keyboard device was added or removed or something.
@@ -701,7 +701,8 @@
bool InputDispatcher::dispatchDeviceResetLocked(
nsecs_t currentTime, DeviceResetEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchDeviceReset - eventTime=%lld, deviceId=%d", entry->eventTime, entry->deviceId);
+ ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
+ entry->deviceId);
#endif
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
@@ -811,9 +812,9 @@
void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
- "repeatCount=%d, downTime=%lld",
+ "repeatCount=%d, downTime=%" PRId64,
prefix,
entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
@@ -884,10 +885,10 @@
void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+ "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
prefix,
entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
entry->action, entry->actionButton, entry->flags,
@@ -1132,8 +1133,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;
@@ -2233,7 +2232,7 @@
if (!cancelationEvents.isEmpty()) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync "
+ ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
"with reality: %s, mode=%d.",
connection->getInputChannelName(), cancelationEvents.size(),
options.reason, options.mode);
@@ -2369,7 +2368,7 @@
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyConfigurationChanged - eventTime=%lld", args->eventTime);
+ ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
#endif
bool needWake;
@@ -2387,8 +2386,9 @@
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
+ ALOGD("notifyKey - eventTime=%" PRId64
+ ", deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
+ "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
args->eventTime, args->deviceId, args->source, args->policyFlags,
args->action, args->flags, args->keyCode, args->scanCode,
args->metaState, args->downTime);
@@ -2482,9 +2482,9 @@
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
+ ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
"action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x,"
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+ "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
args->eventTime, args->deviceId, args->source, args->policyFlags,
args->action, args->actionButton, args->flags, args->metaState, args->buttonState,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
@@ -2562,9 +2562,9 @@
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchValues=0x%08x, switchMask=0x%08x",
- args->eventTime, args->policyFlags,
- args->switchValues, args->switchMask);
+ ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
+ "switchMask=0x%08x",
+ args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
#endif
uint32_t policyFlags = args->policyFlags;
@@ -2575,7 +2575,7 @@
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyDeviceReset - eventTime=%lld, deviceId=%d",
+ ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d",
args->eventTime, args->deviceId);
#endif
@@ -3743,7 +3743,7 @@
msg.appendFormat(", %d->%d", fallbackKeys.keyAt(i),
fallbackKeys.valueAt(i));
}
- ALOGD("Unhandled key event: %d currently tracked fallback keys%s.",
+ ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
fallbackKeys.size(), msg.string());
}
#endif
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 935d0f6..60f2286 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -257,7 +257,7 @@
const String8* uniqueDisplayId, DisplayViewport* outViewport) const {
const DisplayViewport* viewport = NULL;
if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) {
- for (DisplayViewport currentViewport : mVirtualDisplays) {
+ for (const DisplayViewport& currentViewport : mVirtualDisplays) {
if (currentViewport.uniqueId == *uniqueDisplayId) {
viewport = ¤tViewport;
break;
@@ -429,7 +429,7 @@
batchSize += 1;
}
#if DEBUG_RAW_EVENTS
- ALOGD("BatchSize: %d Count: %d", batchSize, count);
+ ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
@@ -1176,7 +1176,7 @@
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTS
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
+ ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
rawEvent->when);
#endif
@@ -1851,7 +1851,7 @@
#if DEBUG_POINTERS
if (newSlot) {
ALOGW("MultiTouch device emitted invalid slot index %d but it "
- "should be between 0 and %d; ignoring this slot.",
+ "should be between 0 and %zd; ignoring this slot.",
mCurrentSlot, mSlotCount - 1);
}
#endif
@@ -2147,9 +2147,9 @@
if (i != 0) {
patternStr.append(", ");
}
- patternStr.appendFormat("%lld", pattern[i]);
+ patternStr.appendFormat("%" PRId64, pattern[i]);
}
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d",
+ ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d",
getDeviceId(), patternStr.string(), repeat, token);
#endif
@@ -2198,8 +2198,7 @@
nsecs_t duration = mPattern[mIndex];
if (vibratorOn) {
#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld",
- getDeviceId(), duration);
+ ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
#endif
getEventHub()->vibrate(getDeviceId(), duration);
} else {
@@ -6631,7 +6630,7 @@
#if DEBUG_POINTER_ASSIGNMENT
ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ ALOGD(" heap[%zu]: cur=%d, last=%d, distance=%" PRId64,
i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
heap[i].distance);
}
@@ -6677,7 +6676,7 @@
#if DEBUG_POINTER_ASSIGNMENT
ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ ALOGD(" heap[%zu]: cur=%d, last=%d, distance=%" PRId64,
i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
heap[i].distance);
}
@@ -6703,7 +6702,7 @@
usedIdBits.markBit(id);
#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+ ALOGD("assignPointerIds - matched: cur=%d, last=%d, id=%d, distance=%" PRId64,
lastPointerIndex, currentPointerIndex, id, heap[0].distance);
#endif
break;
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index b54752b..3ae7972 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -49,7 +49,8 @@
|| layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
|| layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
|| layoutParamsType == TYPE_DOCK_DIVIDER
- || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY;
+ || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
+ || layoutParamsType == TYPE_INPUT_CONSUMER;
}
bool InputWindowInfo::supportsSplitTouch() const {
diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h
index 610290b..9eb2798 100644
--- a/services/inputflinger/InputWindow.h
+++ b/services/inputflinger/InputWindow.h
@@ -101,6 +101,7 @@
TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19,
TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
+ TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22,
TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24,
TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27,
TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32,
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 29d93f0..84a63d6 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -15,7 +15,6 @@
"libhardware",
"libhardware_legacy",
"libui",
- "libskia",
"libinput",
"libinputflinger",
"libinputservice",
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 38529b6..1f4427a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -14,7 +14,7 @@
FrameTracker.cpp \
GpuService.cpp \
Layer.cpp \
- LayerDim.cpp \
+ ColorLayer.cpp \
LayerRejecter.cpp \
LayerVector.cpp \
MessageQueue.cpp \
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 8ba6cb9..ea6541a 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -140,7 +140,8 @@
{
sp<Layer> parent = nullptr;
if (parentHandle != nullptr) {
- parent = getLayerUser(parentHandle);
+ auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get());
+ parent = layerHandle->owner.promote();
if (parent == nullptr) {
return NAME_NOT_FOUND;
}
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/ColorLayer.cpp
similarity index 79%
rename from services/surfaceflinger/LayerDim.cpp
rename to services/surfaceflinger/ColorLayer.cpp
index daebf8a..6923782 100644
--- a/services/surfaceflinger/LayerDim.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -16,7 +16,7 @@
// #define LOG_NDEBUG 0
#undef LOG_TAG
-#define LOG_TAG "LayerDim"
+#define LOG_TAG "ColorLayer"
#include <stdlib.h>
#include <stdint.h>
@@ -27,7 +27,7 @@
#include <ui/GraphicBuffer.h>
-#include "LayerDim.h"
+#include "ColorLayer.h"
#include "SurfaceFlinger.h"
#include "DisplayDevice.h"
#include "RenderEngine/RenderEngine.h"
@@ -35,31 +35,29 @@
namespace android {
// ---------------------------------------------------------------------------
-LayerDim::LayerDim(SurfaceFlinger* flinger, const sp<Client>& client,
+ColorLayer::ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags)
: Layer(flinger, client, name, w, h, flags) {
}
-LayerDim::~LayerDim() {
-}
-
-void LayerDim::onDraw(const sp<const DisplayDevice>& hw,
+void ColorLayer::onDraw(const sp<const DisplayDevice>& hw,
const Region& /* clip */, bool useIdentityTransform) const
{
const State& s(getDrawingState());
- if (s.alpha>0) {
+ if (s.color.a>0) {
Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2);
computeGeometry(hw, mesh, useIdentityTransform);
RenderEngine& engine(mFlinger->getRenderEngine());
- engine.setupDimLayerBlending(s.alpha);
+ engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
+ true /* disableTexture */, s.color);
engine.drawMesh(mesh);
engine.disableBlending();
}
}
-bool LayerDim::isVisible() const {
+bool ColorLayer::isVisible() const {
const Layer::State& s(getDrawingState());
- return !isHiddenByPolicy() && s.alpha;
+ return !isHiddenByPolicy() && s.color.a;
}
diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/ColorLayer.h
similarity index 81%
rename from services/surfaceflinger/LayerDim.h
rename to services/surfaceflinger/ColorLayer.h
index a0cfca9..ac3e2a9 100644
--- a/services/surfaceflinger/LayerDim.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_LAYER_DIM_H
-#define ANDROID_LAYER_DIM_H
+#ifndef ANDROID_COLOR_LAYER_H
+#define ANDROID_COLOR_LAYER_H
#include <stdint.h>
#include <sys/types.h>
@@ -26,14 +26,14 @@
namespace android {
-class LayerDim : public Layer
+class ColorLayer : public Layer
{
public:
- LayerDim(SurfaceFlinger* flinger, const sp<Client>& client,
+ ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags);
- virtual ~LayerDim();
+ virtual ~ColorLayer() = default;
- virtual const char* getTypeId() const { return "LayerDim"; }
+ virtual const char* getTypeId() const { return "ColorLayer"; }
virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip,
bool useIdentityTransform) const;
virtual bool isOpaque(const Layer::State&) const { return false; }
@@ -46,4 +46,4 @@
}; // namespace android
-#endif // ANDROID_LAYER_DIM_H
+#endif // ANDROID_COLOR_LAYER_H
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 433a224..7d6d988 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -751,7 +751,7 @@
}
Error error = kDefaultError;
- mClient->executeCommands(commandLength, commandHandles,
+ auto ret = mClient->executeCommands(commandLength, commandHandles,
[&](const auto& tmpError, const auto& tmpOutChanged,
const auto& tmpOutLength, const auto& tmpOutHandles)
{
@@ -784,6 +784,11 @@
error = Error::NO_RESOURCES;
}
});
+ // executeCommands can fail because of out-of-fd and we do not want to
+ // abort() in that case
+ if (!ret.isOk()) {
+ ALOGE("executeCommands failed because of %s", ret.description().c_str());
+ }
if (error == Error::NONE) {
std::vector<CommandReader::CommandError> commandErrors =
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 826b4a8..956f7f6 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -137,11 +137,7 @@
mCurrentState.requestedFinalCrop = mCurrentState.finalCrop;
mCurrentState.requestedCrop = mCurrentState.crop;
mCurrentState.z = 0;
-#ifdef USE_HWC2
- mCurrentState.alpha = 1.0f;
-#else
- mCurrentState.alpha = 0xFF;
-#endif
+ mCurrentState.color.a = 1.0f;
mCurrentState.layerStack = 0;
mCurrentState.flags = layerFlags;
mCurrentState.sequence = 0;
@@ -334,6 +330,10 @@
return mName;
}
+bool Layer::getPremultipledAlpha() const {
+ return mPremultipliedAlpha;
+}
+
status_t Layer::setBuffers( uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags)
{
@@ -683,7 +683,7 @@
" %s (%d)", mName.string(), to_string(blendMode).c_str(),
to_string(error).c_str(), static_cast<int32_t>(error));
#else
- if (!isOpaque(s) || getAlpha() != 0xFF) {
+ if (!isOpaque(s) || getAlpha() != 1.0f) {
layer.setBlending(mPremultipliedAlpha ?
HWC_BLENDING_PREMULT :
HWC_BLENDING_COVERAGE);
@@ -757,7 +757,7 @@
hwcInfo.sourceCrop = sourceCrop;
}
- float alpha = getAlpha();
+ float alpha = static_cast<float>(getAlpha());
error = hwcLayer->setPlaneAlpha(alpha);
ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set plane alpha %.3f: "
"%s (%d)", mName.string(), alpha, to_string(error).c_str(),
@@ -787,7 +787,7 @@
const Transform& tr(hw->getTransform());
layer.setFrame(tr.transform(frame));
layer.setCrop(computeCrop(hw));
- layer.setPlaneAlpha(getAlpha());
+ layer.setPlaneAlpha(static_cast<uint8_t>(std::round(255.0f*getAlpha())));
#endif
/*
@@ -904,8 +904,11 @@
if (mActiveBuffer == nullptr) {
setCompositionType(hwcId, HWC2::Composition::SolidColor);
- // For now, we only support black for DimLayer
- error = hwcLayer->setColor({0, 0, 0, 255});
+ half4 color = getColor();
+ error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f*color.r)),
+ static_cast<uint8_t>(std::round(255.0f * color.g)),
+ static_cast<uint8_t>(std::round(255.0f * color.b)),
+ 255});
if (error != HWC2::Error::None) {
ALOGE("[%s] Failed to set color: %s (%d)", mName.string(),
to_string(error).c_str(), static_cast<int32_t>(error));
@@ -1254,7 +1257,8 @@
texCoords[3] = vec2(right, 1.0f - top);
RenderEngine& engine(mFlinger->getRenderEngine());
- engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), getAlpha());
+ engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s),
+ false /* disableTexture */, getColor());
#ifdef USE_HWC2
engine.setSourceDataSpace(mCurrentState.dataSpace);
#endif
@@ -1687,23 +1691,28 @@
c.requested.w, c.requested.h);
}
- const bool resizePending = (c.requested.w != c.active.w) ||
- (c.requested.h != c.active.h);
+ // Don't let Layer::doTransaction update the drawing state
+ // if we have a pending resize, unless we are in fixed-size mode.
+ // the drawing state will be updated only once we receive a buffer
+ // with the correct size.
+ //
+ // In particular, we want to make sure the clip (which is part
+ // of the geometry state) is latched together with the size but is
+ // latched immediately when no resizing is involved.
+ //
+ // If a sideband stream is attached, however, we want to skip this
+ // optimization so that transactions aren't missed when a buffer
+ // never arrives
+ //
+ // In the case that we don't have a buffer we ignore other factors
+ // and avoid entering the resizePending state. At a high level the
+ // resizePending state is to avoid applying the state of the new buffer
+ // to the old buffer. However in the state where we don't have an old buffer
+ // there is no such concern but we may still be being used as a parent layer.
+ const bool resizePending = ((c.requested.w != c.active.w) ||
+ (c.requested.h != c.active.h)) && (mActiveBuffer != nullptr);
if (!isFixedSize()) {
if (resizePending && mSidebandStream == NULL) {
- // don't let Layer::doTransaction update the drawing state
- // if we have a pending resize, unless we are in fixed-size mode.
- // the drawing state will be updated only once we receive a buffer
- // with the correct size.
- //
- // in particular, we want to make sure the clip (which is part
- // of the geometry state) is latched together with the size but is
- // latched immediately when no resizing is involved.
- //
- // If a sideband stream is attached, however, we want to skip this
- // optimization so that transactions aren't missed when a buffer
- // never arrives
-
flags |= eDontUpdateGeometryState;
}
}
@@ -1877,19 +1886,30 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
-#ifdef USE_HWC2
bool Layer::setAlpha(float alpha) {
-#else
-bool Layer::setAlpha(uint8_t alpha) {
-#endif
- if (mCurrentState.alpha == alpha)
+ if (mCurrentState.color.a == alpha)
return false;
mCurrentState.sequence++;
- mCurrentState.alpha = alpha;
+ mCurrentState.color.a = alpha;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
+
+bool Layer::setColor(const half3& color) {
+ if (color.r == mCurrentState.color.r && color.g == mCurrentState.color.g
+ && color.b == mCurrentState.color.b)
+ return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.color.r = color.r;
+ mCurrentState.color.g = color.g;
+ mCurrentState.color.b = color.b;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
mCurrentState.sequence++;
mCurrentState.requested.transform.set(
@@ -2141,13 +2161,8 @@
}
bool Layer::isVisible() const {
-#ifdef USE_HWC2
return !(isHiddenByPolicy()) && getAlpha() > 0.0f
&& (mActiveBuffer != NULL || mSidebandStream != NULL);
-#else
- return !(isHiddenByPolicy()) && getAlpha()
- && (mActiveBuffer != NULL || mSidebandStream != NULL);
-#endif
}
bool Layer::allTransactionsSignaled() {
@@ -2439,7 +2454,7 @@
info.mHeight = ds.active.h;
info.mCrop = ds.crop;
info.mFinalCrop = ds.finalCrop;
- info.mAlpha = ds.alpha;
+ info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
info.mDataSpace = getDataSpace();
@@ -2467,7 +2482,6 @@
info.mContentDirty = contentDirty;
return info;
}
-
#ifdef USE_HWC2
void Layer::miniDumpHeader(String8& result) {
result.append("----------------------------------------");
@@ -2625,8 +2639,8 @@
return true;
}
-bool Layer::reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle) {
- if (newParentHandle == nullptr || childHandle == nullptr) {
+bool Layer::reparent(const sp<IBinder>& newParentHandle) {
+ if (newParentHandle == nullptr) {
return false;
}
@@ -2637,29 +2651,19 @@
return false;
}
- handle = static_cast<Handle*>(childHandle.get());
- sp<Layer> child = handle->owner.promote();
- if (child == nullptr) {
- ALOGE("Unable to promote child Layer handle");
- return false;
+ sp<Layer> parent = getParent();
+ if (parent != nullptr) {
+ parent->removeChild(this);
}
+ newParent->addChild(this);
- if (mCurrentChildren.indexOf(child) < 0) {
- ALOGE("Child layer is not child of current layer");
- return false;
- }
-
- sp<Client> parentClient(mClientRef.promote());
- sp<Client> childClient(child->mClientRef.promote());
+ sp<Client> client(mClientRef.promote());
sp<Client> newParentClient(newParent->mClientRef.promote());
- if (parentClient != childClient || childClient != newParentClient) {
- ALOGE("Current layer, child layer, and new parent layer must have the same client");
- return false;
+ if (client != newParentClient) {
+ client->setParentLayer(newParent);
}
- newParent->addChild(child);
- mCurrentChildren.remove(child);
return true;
}
@@ -2669,8 +2673,9 @@
return;
}
+ sp<Client> parentClient = mClientRef.promote();
sp<Client> client(child->mClientRef.promote());
- if (client != nullptr) {
+ if (client != nullptr && parentClient != client) {
client->detachLayer(child);
}
});
@@ -2800,23 +2805,17 @@
return t * getDrawingState().active.transform;
}
-#ifdef USE_HWC2
-float Layer::getAlpha() const {
+half Layer::getAlpha() const {
const auto& p = mDrawingParent.promote();
- float parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0;
- return parentAlpha * getDrawingState().alpha;
+ half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
+ return parentAlpha * getDrawingState().color.a;
}
-#else
-uint8_t Layer::getAlpha() const {
- const auto& p = mDrawingParent.promote();
- float parentAlpha = (p != nullptr) ? (p->getAlpha() / 255.0f) : 1.0;
- float drawingAlpha = getDrawingState().alpha / 255.0f;
- drawingAlpha = drawingAlpha * parentAlpha;
- return static_cast<uint8_t>(std::round(drawingAlpha * 255));
+half4 Layer::getColor() const {
+ const half4 color(getDrawingState().color);
+ return half4(color.r, color.g, color.b, getAlpha());
}
-#endif
void Layer::commitChildList() {
for (size_t i = 0; i < mCurrentChildren.size(); i++) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f94833b..921492b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -51,6 +51,8 @@
#include "RenderEngine/Mesh.h"
#include "RenderEngine/Texture.h"
+#include <math/vec4.h>
+
namespace android {
// ---------------------------------------------------------------------------
@@ -119,11 +121,6 @@
// to achieve mirroring.
uint32_t layerStack;
-#ifdef USE_HWC2
- float alpha;
-#else
- uint8_t alpha;
-#endif
uint8_t flags;
uint8_t mask;
uint8_t reserved[2];
@@ -158,6 +155,8 @@
// A list of surfaces whose Z-order is interpreted relative to ours.
SortedVector<wp<Layer>> zOrderRelatives;
+
+ half4 color;
};
// -----------------------------------------------------------------------
@@ -225,11 +224,8 @@
bool setLayer(int32_t z);
bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
-#ifdef USE_HWC2
bool setAlpha(float alpha);
-#else
- bool setAlpha(uint8_t alpha);
-#endif
+ bool setColor(const half3& color);
bool setTransparentRegionHint(const Region& transparent);
bool setFlags(uint8_t flags, uint8_t mask);
bool setLayerStack(uint32_t layerStack);
@@ -241,7 +237,7 @@
bool setOverrideScalingMode(int32_t overrideScalingMode);
void setInfo(uint32_t type, uint32_t appId);
bool reparentChildren(const sp<IBinder>& layer);
- bool reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle);
+ bool reparent(const sp<IBinder>& newParentHandle);
bool detachChildren();
// If we have received a new buffer this frame, we will pass its surface
@@ -509,11 +505,8 @@
// Returns the Alpha of the Surface, accounting for the Alpha
// of parent Surfaces in the hierarchy (alpha's will be multiplied
// down the hierarchy).
-#ifdef USE_HWC2
- float getAlpha() const;
-#else
- uint8_t getAlpha() const;
-#endif
+ half getAlpha() const;
+ half4 getColor() const;
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
@@ -683,9 +676,8 @@
sp<IGraphicBufferProducer> getProducer() const;
const String8& getName() const;
void notifyAvailableFrames();
-
PixelFormat getPixelFormat() const { return mFormat; }
-
+ bool getPremultipledAlpha() const;
private:
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index effd319..706960c 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -27,22 +27,15 @@
namespace android {
Description::Description() {
- mPlaneAlpha = 1.0f;
mPremultipliedAlpha = false;
mOpaque = true;
mTextureEnabled = false;
mColorMatrixEnabled = false;
-
- memset(mColor, 0, sizeof(mColor));
}
Description::~Description() {
}
-void Description::setPlaneAlpha(GLclampf planeAlpha) {
- mPlaneAlpha = planeAlpha;
-}
-
void Description::setPremultipliedAlpha(bool premultipliedAlpha) {
mPremultipliedAlpha = premultipliedAlpha;
}
@@ -60,11 +53,8 @@
mTextureEnabled = false;
}
-void Description::setColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
- mColor[0] = red;
- mColor[1] = green;
- mColor[2] = blue;
- mColor[3] = alpha;
+void Description::setColor(const half4& color) {
+ mColor = color;
}
void Description::setProjectionMatrix(const mat4& mtx) {
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 3beffdf..cbac855 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -35,8 +35,6 @@
friend class Program;
friend class ProgramCache;
- // value of the plane-alpha, between 0 and 1
- GLclampf mPlaneAlpha;
// whether textures are premultiplied
bool mPremultipliedAlpha;
// whether this layer is marked as opaque
@@ -46,8 +44,8 @@
Texture mTexture;
bool mTextureEnabled;
- // color used when texturing is disabled
- GLclampf mColor[4];
+ // color used when texturing is disabled or when setting alpha.
+ half4 mColor;
// projection matrix
mat4 mProjectionMatrix;
@@ -60,12 +58,11 @@
Description();
~Description();
- void setPlaneAlpha(GLclampf planeAlpha);
void setPremultipliedAlpha(bool premultipliedAlpha);
void setOpaque(bool opaque);
void setTexture(const Texture& texture);
void disableTexture();
- void setColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+ void setColor(const half4& color);
void setProjectionMatrix(const mat4& mtx);
void setColorMatrix(const mat4& mtx);
const mat4& getColorMatrix() const;
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 37a530b..daaa11e 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -204,25 +204,17 @@
mVpHeight = vph;
}
-#ifdef USE_HWC2
void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha,
- bool opaque, float alpha) {
-#else
-void GLES20RenderEngine::setupLayerBlending(
- bool premultipliedAlpha, bool opaque, int alpha) {
-#endif
-
+ bool opaque, bool disableTexture, const half4& color) {
mState.setPremultipliedAlpha(premultipliedAlpha);
mState.setOpaque(opaque);
-#ifdef USE_HWC2
- mState.setPlaneAlpha(alpha);
+ mState.setColor(color);
- if (alpha < 1.0f || !opaque) {
-#else
- mState.setPlaneAlpha(alpha / 255.0f);
+ if (disableTexture) {
+ mState.disableTexture();
+ }
- if (alpha < 0xFF || !opaque) {
-#endif
+ if (color.a < 1.0f || !opaque) {
glEnable(GL_BLEND);
glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
@@ -231,33 +223,6 @@
}
#ifdef USE_HWC2
-void GLES20RenderEngine::setupDimLayerBlending(float alpha) {
-#else
-void GLES20RenderEngine::setupDimLayerBlending(int alpha) {
-#endif
- mState.setPlaneAlpha(1.0f);
- mState.setPremultipliedAlpha(true);
- mState.setOpaque(false);
-#ifdef USE_HWC2
- mState.setColor(0, 0, 0, alpha);
-#else
- mState.setColor(0, 0, 0, alpha/255.0f);
-#endif
- mState.disableTexture();
-
-#ifdef USE_HWC2
- if (alpha == 1.0f) {
-#else
- if (alpha == 0xFF) {
-#endif
- glDisable(GL_BLEND);
- } else {
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- }
-}
-
-#ifdef USE_HWC2
void GLES20RenderEngine::setColorMode(android_color_mode mode) {
ALOGV("setColorMode: %s (0x%x)", decodeColorMode(mode).c_str(), mode);
@@ -355,10 +320,9 @@
}
void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
- mState.setPlaneAlpha(1.0f);
mState.setPremultipliedAlpha(true);
mState.setOpaque(false);
- mState.setColor(r, g, b, a);
+ mState.setColor(half4(r, g, b, a));
mState.disableTexture();
glDisable(GL_BLEND);
}
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index eaf94af..5ac12fc 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -68,10 +68,9 @@
virtual void setViewportAndProjection(size_t vpw, size_t vph,
Rect sourceCrop, size_t hwh, bool yswap,
Transform::orientation_flags rotation);
-#ifdef USE_HWC2
virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- float alpha) override;
- virtual void setupDimLayerBlending(float alpha) override;
+ bool disableTexture, const half4& color) override;
+#ifdef USE_HWC2
// Color management related functions and state
void setColorMode(android_color_mode mode);
@@ -92,10 +91,6 @@
// Currently only supporting sRGB and DisplayP3 color spaces
mat4 mSrgbToDisplayP3;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
- int alpha);
- virtual void setupDimLayerBlending(int alpha);
#endif
bool mPlatformHasWideColor = false;
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp
index 48a8da5..e95a6c5 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/services/surfaceflinger/RenderEngine/Program.cpp
@@ -22,6 +22,7 @@
#include "Program.h"
#include "ProgramCache.h"
#include "Description.h"
+#include <math/mat4.h>
namespace android {
@@ -63,7 +64,6 @@
mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
- mAlphaPlaneLoc = glGetUniformLocation(programId, "alphaPlane");
// set-up the default values for our uniforms
glUseProgram(programId);
@@ -132,11 +132,9 @@
glUniform1i(mSamplerLoc, 0);
glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray());
}
- if (mAlphaPlaneLoc >= 0) {
- glUniform1f(mAlphaPlaneLoc, desc.mPlaneAlpha);
- }
if (mColorLoc >= 0) {
- glUniform4fv(mColorLoc, 1, desc.mColor);
+ const float* color = &static_cast<details::TVec4<float> const &>(desc.mColor)[0];
+ glUniform4fv(mColorLoc, 1, color);
}
if (mColorMatrixLoc >= 0) {
glUniformMatrix4fv(mColorMatrixLoc, 1, GL_FALSE, desc.mColorMatrix.asArray());
diff --git a/services/surfaceflinger/RenderEngine/Program.h b/services/surfaceflinger/RenderEngine/Program.h
index 36bd120..a2ae2ee 100644
--- a/services/surfaceflinger/RenderEngine/Program.h
+++ b/services/surfaceflinger/RenderEngine/Program.h
@@ -79,9 +79,6 @@
/* location of the sampler uniform */
GLint mSamplerLoc;
- /* location of the alpha plane uniform */
- GLint mAlphaPlaneLoc;
-
/* location of the color uniform */
GLint mColorLoc;
};
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 06b2252..b437545 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -89,7 +89,7 @@
void ProgramCache::primeCache() {
uint32_t shaderCount = 0;
uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK |
- Key::PLANE_ALPHA_MASK | Key::TEXTURE_MASK;
+ Key::ALPHA_MASK | Key::TEXTURE_MASK;
// Prime the cache for all combinations of the above masks,
// leaving off the experimental color matrix mask options.
@@ -122,8 +122,8 @@
description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT :
description.mTexture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D :
Key::TEXTURE_OFF)
- .set(Key::PLANE_ALPHA_MASK,
- (description.mPlaneAlpha < 1) ? Key::PLANE_ALPHA_LT_ONE : Key::PLANE_ALPHA_EQ_ONE)
+ .set(Key::ALPHA_MASK,
+ (description.mColor.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
.set(Key::BLEND_MASK,
description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
.set(Key::OPACITY_MASK,
@@ -168,12 +168,12 @@
} else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
fs << "uniform sampler2D sampler;"
<< "varying vec2 outTexCoords;";
- } else if (needs.getTextureTarget() == Key::TEXTURE_OFF) {
+ }
+
+ if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
fs << "uniform vec4 color;";
}
- if (needs.hasPlaneAlpha()) {
- fs << "uniform float alphaPlane;";
- }
+
if (needs.hasColorMatrix()) {
fs << "uniform mat4 colorMatrix;";
}
@@ -225,18 +225,19 @@
if (needs.isTexturing()) {
fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
} else {
- fs << "gl_FragColor = color;";
+ fs << "gl_FragColor.rgb = color.rgb;";
+ fs << "gl_FragColor.a = 1.0;";
}
if (needs.isOpaque()) {
fs << "gl_FragColor.a = 1.0;";
}
- if (needs.hasPlaneAlpha()) {
- // modulate the alpha value with planeAlpha
+ if (needs.hasAlpha()) {
+ // modulate the current alpha value with alpha set
if (needs.isPremultiplied()) {
// ... and the color too if we're premultiplied
- fs << "gl_FragColor *= alphaPlane;";
+ fs << "gl_FragColor *= color.a;";
} else {
- fs << "gl_FragColor.a *= alphaPlane;";
+ fs << "gl_FragColor.a *= color.a;";
}
}
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h
index 5b0fbcd..ff5cf0f 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.h
@@ -57,9 +57,9 @@
OPACITY_TRANSLUCENT = 0x00000000,
OPACITY_MASK = 0x00000002,
- PLANE_ALPHA_LT_ONE = 0x00000004,
- PLANE_ALPHA_EQ_ONE = 0x00000000,
- PLANE_ALPHA_MASK = 0x00000004,
+ ALPHA_LT_ONE = 0x00000004,
+ ALPHA_EQ_ONE = 0x00000000,
+ ALPHA_MASK = 0x00000004,
TEXTURE_OFF = 0x00000000,
TEXTURE_EXT = 0x00000008,
@@ -95,8 +95,8 @@
inline bool isOpaque() const {
return (mKey & OPACITY_MASK) == OPACITY_OPAQUE;
}
- inline bool hasPlaneAlpha() const {
- return (mKey & PLANE_ALPHA_MASK) == PLANE_ALPHA_LT_ONE;
+ inline bool hasAlpha() const {
+ return (mKey & ALPHA_MASK) == ALPHA_LT_ONE;
}
inline bool hasColorMatrix() const {
return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON;
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 9544579..fa65979 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -25,6 +25,7 @@
#include <EGL/eglext.h>
#include <math/mat4.h>
#include <Transform.h>
+#include <gui/SurfaceControl.h>
#define EGL_NO_CONFIG ((EGLConfig)0)
@@ -98,16 +99,13 @@
virtual void checkErrors() const;
virtual void setViewportAndProjection(size_t vpw, size_t vph,
Rect sourceCrop, size_t hwh, bool yswap, Transform::orientation_flags rotation) = 0;
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque,
+ bool disableTexture, const half4& color) = 0;
#ifdef USE_HWC2
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, float alpha) = 0;
- virtual void setupDimLayerBlending(float alpha) = 0;
virtual void setColorMode(android_color_mode mode) = 0;
virtual void setSourceDataSpace(android_dataspace source) = 0;
virtual void setWideColor(bool hasWideColor) = 0;
virtual bool usesWideColor() = 0;
-#else
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0;
- virtual void setupDimLayerBlending(int alpha) = 0;
#endif
virtual void setupLayerTexturing(const Texture& texture) = 0;
virtual void setupLayerBlackedOut() = 0;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e9d8c96..f04cb88 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -73,7 +73,7 @@
#include "EventThread.h"
#include "Layer.h"
#include "LayerVector.h"
-#include "LayerDim.h"
+#include "ColorLayer.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
@@ -630,9 +630,18 @@
if (useVrFlinger) {
auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
- ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
- mVrFlingerRequestsDisplay = requestDisplay;
- signalTransaction();
+ // This callback is called from the vr flinger dispatch thread. We
+ // need to call signalTransaction(), which requires holding
+ // mStateLock when we're not on the main thread. Acquiring
+ // mStateLock from the vr flinger dispatch thread might trigger a
+ // deadlock in surface flinger (see b/66916578), so post a message
+ // to be handled on the main thread instead.
+ sp<LambdaMessage> message = new LambdaMessage([=]() {
+ ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
+ mVrFlingerRequestsDisplay = requestDisplay;
+ signalTransaction();
+ });
+ postMessageAsync(message);
};
mVrFlinger = dvr::VrFlinger::Create(mHwc->getComposer(),
vrFlingerRequestDisplayCallback);
@@ -719,6 +728,8 @@
FrameEvent::DEQUEUE_READY,
FrameEvent::RELEASE,
};
+ ConditionalLock _l(mStateLock,
+ std::this_thread::get_id() != mMainThreadId);
if (!getHwComposer().hasCapability(
HWC2::Capability::PresentFenceIsNotReliable)) {
outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
@@ -766,6 +777,8 @@
configs->clear();
+ ConditionalLock _l(mStateLock,
+ std::this_thread::get_id() != mMainThreadId);
for (const auto& hwConfig : getHwComposer().getConfigs(type)) {
DisplayInfo info = DisplayInfo();
@@ -789,7 +802,7 @@
info.density = density;
// TODO: this needs to go away (currently needed only by webkit)
- sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+ sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
info.orientation = hw->getOrientation();
} else {
// TODO: where should this value come from?
@@ -932,7 +945,12 @@
return type;
}
- std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type);
+ std::vector<android_color_mode_t> modes;
+ {
+ ConditionalLock _l(mStateLock,
+ std::this_thread::get_id() != mMainThreadId);
+ modes = getHwComposer().getColorModes(type);
+ }
outColorModes->clear();
std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
@@ -1340,7 +1358,7 @@
if (sequenceId != mComposerSequenceId) {
return;
}
- repaintEverything();
+ repaintEverythingLocked();
}
void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
@@ -1728,7 +1746,7 @@
// rebuild the visible layer list per screen
if (CC_UNLIKELY(mVisibleRegionsDirty)) {
- ATRACE_CALL();
+ ATRACE_NAME("rebuildLayerStacks VR Dirty");
mVisibleRegionsDirty = false;
invalidateHwcGeometry();
@@ -2426,7 +2444,7 @@
// compute the opaque region
const int32_t layerOrientation = tr.getOrientation();
- if (s.alpha == 1.0f && !translucent &&
+ if (layer->getAlpha() == 1.0f && !translucent &&
((layerOrientation & Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
opaqueRegion = visibleRegion;
@@ -2714,7 +2732,7 @@
case HWC2::Composition::SolidColor: {
const Layer::State& state(layer->getDrawingState());
if (layer->getClearClientTarget(hwcId) && !firstLayer &&
- layer->isOpaque(state) && (state.alpha == 1.0f)
+ layer->isOpaque(state) && (state.color.a == 1.0f)
&& hasClientComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
@@ -3054,6 +3072,10 @@
if (layer->setAlpha(s.alpha))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eColorChanged) {
+ if (layer->setColor(s.color))
+ flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eMatrixChanged) {
if (layer->setMatrix(s.matrix))
flags |= eTraversalNeeded;
@@ -3110,10 +3132,8 @@
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
- // Always re-parent the children that explicitly requested to get
- // re-parented before the general re-parent of all children.
- if (what & layer_state_t::eReparentChild) {
- if (layer->reparentChild(s.parentHandleForChild, s.childHandle)) {
+ if (what & layer_state_t::eReparent) {
+ if (layer->reparent(s.parentHandleForChild)) {
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -3159,8 +3179,8 @@
uniqueName, w, h, flags, format,
handle, gbp, &layer);
break;
- case ISurfaceComposerClient::eFXSurfaceDim:
- result = createDimLayer(client,
+ case ISurfaceComposerClient::eFXSurfaceColor:
+ result = createColorLayer(client,
uniqueName, w, h, flags,
handle, gbp, &layer);
break;
@@ -3242,11 +3262,11 @@
return err;
}
-status_t SurfaceFlinger::createDimLayer(const sp<Client>& client,
+status_t SurfaceFlinger::createColorLayer(const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
- *outLayer = new LayerDim(this, client, name, w, h, flags);
+ *outLayer = new ColorLayer(this, client, name, w, h, flags);
*handle = (*outLayer)->getHandle();
*gbp = (*outLayer)->getProducer();
return NO_ERROR;
@@ -3362,7 +3382,7 @@
mVisibleRegionsDirty = true;
mHasPoweredOff = true;
- repaintEverything();
+ repaintEverythingLocked();
struct sched_param param = {0};
param.sched_priority = 1;
@@ -4006,6 +4026,7 @@
return NO_ERROR;
}
case 1005:{ // force transaction
+ Mutex::Autolock _l(mStateLock);
setTransactionFlags(
eTransactionNeeded|
eDisplayTransactionNeeded|
@@ -4142,11 +4163,17 @@
return err;
}
-void SurfaceFlinger::repaintEverything() {
+void SurfaceFlinger::repaintEverythingLocked() {
android_atomic_or(1, &mRepaintEverything);
signalTransaction();
}
+void SurfaceFlinger::repaintEverything() {
+ ConditionalLock _l(mStateLock,
+ std::this_thread::get_id() != mMainThreadId);
+ repaintEverythingLocked();
+}
+
// Checks that the requested width and height are valid and updates them to the display dimensions
// if they are set to 0
static status_t updateDimensionsLocked(const sp<const DisplayDevice>& displayDevice,
@@ -4578,7 +4605,7 @@
ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
layer->isVisible() ? '+' : '-',
i, layer->getName().string(), layer->getLayerStack(), state.z,
- layer->isVisible(), state.flags, state.alpha);
+ layer->isVisible(), state.flags, static_cast<float>(state.color.a));
i++;
});
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 058f4a1..e87d35f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -84,7 +84,7 @@
class DisplayEventConnection;
class EventThread;
class Layer;
-class LayerDim;
+class ColorLayer;
class Surface;
class RenderEngine;
class EventControlThread;
@@ -192,6 +192,8 @@
// force full composition on all displays
void repaintEverything();
+ // Can only be called from the main thread or with mStateLock held
+ void repaintEverythingLocked();
// returns the default Display
sp<const DisplayDevice> getDefaultDisplayDevice() const {
@@ -344,7 +346,9 @@
* Message handling
*/
void waitForEvent();
+ // Can only be called from the main thread or with mStateLock held
void signalTransaction();
+ // Can only be called from the main thread or with mStateLock held
void signalLayerUpdate();
void signalRefresh();
@@ -387,6 +391,7 @@
*/
uint32_t getTransactionFlags(uint32_t flags);
uint32_t peekTransactionFlags();
+ // Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
void commitTransaction();
uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s);
@@ -405,7 +410,7 @@
sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
sp<Layer>* outLayer);
- status_t createDimLayer(const sp<Client>& client, const String8& name,
+ status_t createColorLayer(const sp<Client>& client, const String8& name,
uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle,
sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
@@ -643,8 +648,26 @@
// access must be protected by mInvalidateLock
volatile int32_t mRepaintEverything;
- // The current hardware composer interface. When switching into and out of
- // vr, our HWComposer instance will be recreated.
+ // The current hardware composer interface.
+ //
+ // The following thread safety rules apply when accessing mHwc, either
+ // directly or via getHwComposer():
+ //
+ // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
+ // only when switching into and out of vr. Recreating mHwc must only be
+ // done on the main thread.
+ //
+ // 2. When accessing mHwc on the main thread, it's not necessary to acquire
+ // mStateLock.
+ //
+ // 3. When accessing mHwc on a thread other than the main thread, we always
+ // need to acquire mStateLock. This is because the main thread could be
+ // in the process of destroying the current mHwc instance.
+ //
+ // The above thread safety rules only apply to SurfaceFlinger.cpp. In
+ // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
+ // destroy it, so it's always safe to access mHwc from any thread without
+ // acquiring mStateLock.
std::unique_ptr<HWComposer> mHwc;
#ifdef USE_HWC2
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 71aa52d..b002138 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -71,7 +71,7 @@
#include "EventThread.h"
#include "Layer.h"
#include "LayerVector.h"
-#include "LayerDim.h"
+#include "ColorLayer.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
@@ -179,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");
@@ -2022,7 +2024,7 @@
// compute the opaque region
const int32_t layerOrientation = tr.getOrientation();
- if (s.alpha==255 && !translucent &&
+ if (layer->getAlpha()==1.0f && !translucent &&
((layerOrientation & Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
opaqueRegion = visibleRegion;
@@ -2295,7 +2297,7 @@
const Layer::State& state(layer->getDrawingState());
if ((cur->getHints() & HWC_HINT_CLEAR_FB)
&& i
- && layer->isOpaque(state) && (state.alpha == 0xFF)
+ && layer->isOpaque(state) && (state.color.a == 1.0f)
&& hasGlesComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
@@ -2620,9 +2622,14 @@
}
}
if (what & layer_state_t::eAlphaChanged) {
- if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
+ if (layer->setAlpha(s.alpha))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eColorChanged) {
+ if (layer->setColor(s.color)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eMatrixChanged) {
if (layer->setMatrix(s.matrix))
flags |= eTraversalNeeded;
@@ -2679,10 +2686,8 @@
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
- // Always re-parent the children that explicitly requested to get
- // re-parented before the general re-parent of all children.
- if (what & layer_state_t::eReparentChild) {
- if (layer->reparentChild(s.parentHandleForChild, s.childHandle)) {
+ if (what & layer_state_t::eReparent) {
+ if (layer->reparent(s.parentHandleForChild)) {
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -2728,8 +2733,8 @@
uniqueName, w, h, flags, format,
handle, gbp, &layer);
break;
- case ISurfaceComposerClient::eFXSurfaceDim:
- result = createDimLayer(client,
+ case ISurfaceComposerClient::eFXSurfaceColor:
+ result = createColorLayer(client,
uniqueName, w, h, flags,
handle, gbp, &layer);
break;
@@ -2804,11 +2809,11 @@
return err;
}
-status_t SurfaceFlinger::createDimLayer(const sp<Client>& client,
+status_t SurfaceFlinger::createColorLayer(const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
- *outLayer = new LayerDim(this, client, name, w, h, flags);
+ *outLayer = new ColorLayer(this, client, name, w, h, flags);
*handle = (*outLayer)->getHandle();
*gbp = (*outLayer)->getProducer();
return NO_ERROR;
@@ -4089,10 +4094,10 @@
if (layer->getLayerStack() == hw->getLayerStack() && state.z >= minLayerZ &&
state.z <= maxLayerZ) {
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
+ ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
layer->isVisible() ? '+' : '-',
i, layer->getName().string(), layer->getLayerStack(), state.z,
- layer->isVisible(), state.flags, state.alpha);
+ layer->isVisible(), state.flags, static_cast<float>(state.color.a));
i++;
});
}
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index db489b2..eeb4929 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -98,7 +98,7 @@
addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
layer->mCurrentState.active.transform.ty());
addDepthLocked(transaction, layerId, layer->mCurrentState.z);
- addAlphaLocked(transaction, layerId, layer->mCurrentState.alpha);
+ addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
addCropLocked(transaction, layerId, layer->mCurrentState.crop);
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 6be708a..5c188dc 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*:CropLatchingTest.*:GeometryLatchingTest.*"
+ "filter": "LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*:CropLatchingTest.*:GeometryLatchingTest.*:LayerColorTest.*"
}
}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index d80ed92..d285785 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -28,6 +28,7 @@
#include <ui/DisplayInfo.h>
#include <math.h>
+#include <math/vec3.h>
namespace android {
@@ -788,6 +789,41 @@
}
}
+TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
+ sp<ScreenCapture> sc;
+
+ sp<SurfaceControl> childNoBuffer =
+ mComposerClient->createSurface(String8("Bufferless child"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> childBuffer = mComposerClient->createSurface(
+ String8("Buffered child"), 20, 20,
+ PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+ fillSurfaceRGBA8(childBuffer, 200, 200, 200);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ childNoBuffer->show();
+ childBuffer->show();
+ SurfaceComposerClient::closeGlobalTransaction();
+
+
+ {
+ ScreenCapture::captureScreen(&sc);
+ sc->expectChildColor(73, 73);
+ sc->expectFGColor(74, 74);
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ childNoBuffer->setSize(20, 20);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ ScreenCapture::captureScreen(&sc);
+ sc->expectChildColor(73, 73);
+ sc->expectChildColor(74, 74);
+ }
+}
+
class ChildLayerTest : public LayerUpdateTest {
protected:
void SetUp() override {
@@ -990,7 +1026,7 @@
}
}
-TEST_F(ChildLayerTest, DetachChildren) {
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
SurfaceComposerClient::openGlobalTransaction();
mChild->show();
mChild->setPosition(10, 10);
@@ -1015,6 +1051,52 @@
mChild->hide();
SurfaceComposerClient::closeGlobalTransaction(true);
+ // Since the child has the same client as the parent, it will not get
+ // detached and will be hidden.
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectFGColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
+ sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> mChildNewClient = mNewComposerClient->createSurface(
+ String8("New Child Test Surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+
+ ASSERT_TRUE(mChildNewClient != NULL);
+ ASSERT_TRUE(mChildNewClient->isValid());
+
+ fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mChild->hide();
+ mChildNewClient->show();
+ mChildNewClient->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mFGSurfaceControl->detachChildren();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mChildNewClient->hide();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
// Nothing should have changed.
{
ScreenCapture::captureScreen(&mCapture);
@@ -1123,7 +1205,7 @@
fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
}
-TEST_F(ChildLayerTest, ReparentChild) {
+TEST_F(ChildLayerTest, Reparent) {
SurfaceComposerClient::openGlobalTransaction();
mChild->show();
mChild->setPosition(10, 10);
@@ -1139,7 +1221,7 @@
// And 10 more pixels we should be back to the foreground surface
mCapture->expectFGColor(84, 84);
}
- mFGSurfaceControl->reparentChild(mBGSurfaceControl->getHandle(), mChild->getHandle());
+ mChild->reparent(mBGSurfaceControl->getHandle());
{
ScreenCapture::captureScreen(&mCapture);
mCapture->expectFGColor(64, 64);
@@ -1152,6 +1234,69 @@
}
}
+TEST_F(ChildLayerTest, ReparentToNoParent) {
+ SurfaceComposerClient::openGlobalTransaction();
+ mChild->show();
+ mChild->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+ mChild->reparent(nullptr);
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // Nothing should have changed.
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectChildColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
+TEST_F(ChildLayerTest, ReparentFromNoParent) {
+ sp<SurfaceControl> newSurface = mComposerClient->createSurface(
+ String8("New Surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(newSurface != NULL);
+ ASSERT_TRUE(newSurface->isValid());
+
+ fillSurfaceRGBA8(newSurface, 63, 195, 63);
+ SurfaceComposerClient::openGlobalTransaction();
+ mChild->hide();
+ newSurface->show();
+ newSurface->setPosition(10, 10);
+ newSurface->setLayer(INT32_MAX-2);
+ mFGSurfaceControl->setPosition(64, 64);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // At 10, 10 we should see the new surface
+ mCapture->checkPixel(10, 10, 63, 195, 63);
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ newSurface->reparent(mFGSurfaceControl->getHandle());
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
+ // mFGSurface, putting it at 74, 74.
+ mCapture->expectFGColor(64, 64);
+ mCapture->checkPixel(74, 74, 63, 195, 63);
+ mCapture->expectFGColor(84, 84);
+ }
+}
+
TEST_F(ChildLayerTest, NestedChildren) {
sp<SurfaceControl> grandchild = mComposerClient->createSurface(
String8("Grandchild surface"),
@@ -1167,4 +1312,97 @@
}
}
+class LayerColorTest : public LayerUpdateTest {
+ protected:
+ void SetUp() override {
+ LayerUpdateTest::SetUp();
+
+ mLayerColorControl = mComposerClient->createSurface(
+ String8("Layer color surface"),
+ 128, 128, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ ASSERT_TRUE(mLayerColorControl != NULL);
+ ASSERT_TRUE(mLayerColorControl->isValid());
+
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mLayerColorControl->setLayer(INT32_MAX-1));
+ ASSERT_EQ(NO_ERROR, mLayerColorControl->setPosition(140, 140));
+ ASSERT_EQ(NO_ERROR, mLayerColorControl->hide());
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide());
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ }
+
+ void TearDown() override {
+ LayerUpdateTest::TearDown();
+ mLayerColorControl = 0;
+ }
+
+ sp<SurfaceControl> mLayerColorControl;
+};
+
+TEST_F(LayerColorTest, ColorLayerNoAlpha) {
+ sp<ScreenCapture> sc;
+
+ {
+ SCOPED_TRACE("before setColor");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(145, 145);
+ }
+
+
+ SurfaceComposerClient::openGlobalTransaction();
+ half3 color(43.0f/255.0f, 207.0f/255.0f, 131.0f/255.0f);
+ mLayerColorControl->setColor(color);
+ mLayerColorControl->show();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ {
+ // There should now be a color
+ SCOPED_TRACE("after setColor");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(145, 145, 43, 207, 131);
+ }
+}
+
+TEST_F(LayerColorTest, ColorLayerWithAlpha) {
+ sp<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before setColor");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(145, 145);
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ half3 color(43.0f/255.0f, 207.0f/255.0f, 131.0f/255.0f);
+ mLayerColorControl->setColor(color);
+ mLayerColorControl->setAlpha(.75f);
+ mLayerColorControl->show();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ {
+ // There should now be a color with .75 alpha
+ SCOPED_TRACE("after setColor");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(145, 145, 48, 171, 147);
+ }
+}
+
+TEST_F(LayerColorTest, ColorLayerWithNoColor) {
+ sp<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before setColor");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(145, 145);
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mLayerColorControl->show();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ {
+ // There should now be set to 0,0,0 (black) as default.
+ SCOPED_TRACE("after setColor");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(145, 145, 0, 0, 0);
+ }
+}
+
}
diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk
index 6d20349..010ac9c 100644
--- a/services/surfaceflinger/tests/hwc2/Android.mk
+++ b/services/surfaceflinger/tests/hwc2/Android.mk
@@ -37,7 +37,7 @@
libgui \
liblog \
libsync \
- libskia \
+ libhwui \
android.hardware.graphics.common@1.0
LOCAL_STATIC_LIBRARIES := \
libbase \
diff --git a/services/utils/Android.bp b/services/utils/Android.bp
new file mode 100644
index 0000000..4673491
--- /dev/null
+++ b/services/utils/Android.bp
@@ -0,0 +1,35 @@
+// 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.
+
+//
+// Static library used in testing and executables
+//
+cc_library_static {
+ name: "libserviceutils",
+
+ cflags: [
+ "-Wall",
+ "-Wno-unused-parameter",
+ "-Werror",
+ ],
+
+ srcs: [
+ "PriorityDumper.cpp",
+ ],
+
+ clang: true,
+ export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/services/utils/PriorityDumper.cpp b/services/utils/PriorityDumper.cpp
new file mode 100644
index 0000000..555cf03
--- /dev/null
+++ b/services/utils/PriorityDumper.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "include/PriorityDumper.h"
+
+namespace android {
+
+static void getStrippedArgs(Vector<String16>& dest, const Vector<String16>& source,
+ std::size_t numArgsToStrip) {
+ for (auto it = source.begin() + numArgsToStrip; it != source.end(); it++) {
+ dest.add(*it);
+ }
+}
+
+void priorityDump(PriorityDumper& dumper, int fd, const Vector<String16>& args) {
+ if (args.size() >= 2 && args[0] == PRIORITY_ARG) {
+ String16 priority = args[1];
+ Vector<String16> strippedArgs;
+ getStrippedArgs(strippedArgs, args, 2);
+ if (priority == PRIORITY_ARG_CRITICAL) {
+ dumper.dumpCritical(fd, strippedArgs);
+ } else if (priority == PRIORITY_ARG_HIGH) {
+ dumper.dumpHigh(fd, strippedArgs);
+ } else if (priority == PRIORITY_ARG_NORMAL) {
+ dumper.dumpNormal(fd, strippedArgs);
+ } else {
+ dumper.dump(fd, args);
+ }
+ } else {
+ dumper.dump(fd, args);
+ }
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/utils/include/PriorityDumper.h b/services/utils/include/PriorityDumper.h
new file mode 100644
index 0000000..23e900d
--- /dev/null
+++ b/services/utils/include/PriorityDumper.h
@@ -0,0 +1,62 @@
+/*
+ * 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_UTILS_PRIORITYDUMP_H
+#define ANDROID_UTILS_PRIORITYDUMP_H
+
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+constexpr const char16_t PRIORITY_ARG[] = u"--dump-priority";
+constexpr const char16_t PRIORITY_ARG_CRITICAL[] = u"CRITICAL";
+constexpr const char16_t PRIORITY_ARG_HIGH[] = u"HIGH";
+constexpr const char16_t PRIORITY_ARG_NORMAL[] = u"NORMAL";
+
+// Helper class to split dumps into various priority buckets.
+class PriorityDumper {
+public:
+ // Dumps CRITICAL priority sections.
+ virtual void dumpCritical(int fd, const Vector<String16>& args) {}
+
+ // Dumps HIGH priority sections.
+ virtual void dumpHigh(int fd, const Vector<String16>& args) {}
+
+ // Dumps normal priority sections.
+ virtual void dumpNormal(int fd, const Vector<String16>& args) {}
+
+ // Dumps all sections.
+ // This method is called when priorityDump is called without priority
+ // arguments. By default, it calls all three dump methods.
+ virtual void dump(int fd, const Vector<String16>& args) {
+ dumpCritical(fd, args);
+ dumpHigh(fd, args);
+ dumpNormal(fd, args);
+ }
+ virtual ~PriorityDumper() = default;
+};
+
+// Parses the argument list checking if the first argument is --dump_priority and
+// the second argument is the priority type (HIGH, CRITICAL or NORMAL). If the
+// arguments are found, they are stripped and the appropriate PriorityDumper
+// method is called.
+// If --dump_priority argument is not passed, all supported sections are dumped.
+void priorityDump(PriorityDumper& dumper, int fd, const Vector<String16>& args);
+
+}; // namespace android
+
+#endif // ANDROID_UTILS_PRIORITYDUMP_H
diff --git a/services/utils/tests/Android.bp b/services/utils/tests/Android.bp
new file mode 100644
index 0000000..5a9dc0a
--- /dev/null
+++ b/services/utils/tests/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+// Build unit tests.
+
+cc_test {
+ name: "prioritydumper_test",
+ test_suites: ["device-tests"],
+ srcs: [ "PriorityDumper_test.cpp"],
+ shared_libs: [
+ "libutils",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+ static_libs = [
+ "libgmock",
+ "libserviceutils"
+ ],
+ clang: true,
+}
\ No newline at end of file
diff --git a/services/utils/tests/AndroidTest.xml b/services/utils/tests/AndroidTest.xml
new file mode 100644
index 0000000..83c890d
--- /dev/null
+++ b/services/utils/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for prioritydumper_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="prioritydumper_test->/data/local/tmp/prioritydumper_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="prioritydumper_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/utils/tests/PriorityDumper_test.cpp b/services/utils/tests/PriorityDumper_test.cpp
new file mode 100644
index 0000000..c5a121e
--- /dev/null
+++ b/services/utils/tests/PriorityDumper_test.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 "PriorityDumper.h"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+using ::testing::ElementsAreArray;
+using ::testing::Mock;
+using ::testing::Test;
+
+class PriorityDumperMock : public PriorityDumper {
+public:
+ MOCK_METHOD2(dumpCritical, void(int, const Vector<String16>&));
+ MOCK_METHOD2(dumpHigh, void(int, const Vector<String16>&));
+ MOCK_METHOD2(dumpNormal, void(int, const Vector<String16>&));
+ MOCK_METHOD2(dump, void(int, const Vector<String16>&));
+};
+
+class DumpAllMock : public PriorityDumper {
+public:
+ MOCK_METHOD2(dumpCritical, void(int, const Vector<String16>&));
+ MOCK_METHOD2(dumpHigh, void(int, const Vector<String16>&));
+ MOCK_METHOD2(dumpNormal, void(int, const Vector<String16>&));
+};
+
+class PriorityDumperTest : public Test {
+public:
+ PriorityDumperTest() : dumper_(), dumpAlldumper_(), fd(1) {}
+ PriorityDumperMock dumper_;
+ DumpAllMock dumpAlldumper_;
+ int fd;
+};
+
+static void addAll(Vector<String16>& av, const std::vector<std::string>& v) {
+ for (auto element : v) {
+ av.add(String16(element.c_str()));
+ }
+}
+
+TEST_F(PriorityDumperTest, noArgsPassed) {
+ Vector<String16> args;
+ EXPECT_CALL(dumper_, dump(fd, ElementsAreArray(args)));
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, noPriorityArgsPassed) {
+ Vector<String16> args;
+ addAll(args, {"bunch", "of", "args"});
+ EXPECT_CALL(dumper_, dump(fd, ElementsAreArray(args)));
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, priorityArgsOnly) {
+ Vector<String16> args;
+ addAll(args, {"--dump-priority", "CRITICAL"});
+ Vector<String16> strippedArgs;
+ EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs)));
+
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, dumpCritical) {
+ Vector<String16> args;
+ addAll(args, {"--dump-priority", "CRITICAL", "args", "left", "behind"});
+ Vector<String16> strippedArgs;
+ addAll(strippedArgs, {"args", "left", "behind"});
+
+ EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs)));
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, dumpHigh) {
+ Vector<String16> args;
+ addAll(args, {"--dump-priority", "HIGH", "args", "left", "behind"});
+ Vector<String16> strippedArgs;
+ addAll(strippedArgs, {"args", "left", "behind"});
+
+ EXPECT_CALL(dumper_, dumpHigh(fd, ElementsAreArray(strippedArgs)));
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, dumpNormal) {
+ Vector<String16> args;
+ addAll(args, {"--dump-priority", "NORMAL", "args", "left", "behind"});
+ Vector<String16> strippedArgs;
+ addAll(strippedArgs, {"args", "left", "behind"});
+
+ EXPECT_CALL(dumper_, dumpNormal(fd, ElementsAreArray(strippedArgs)));
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, dumpAll) {
+ Vector<String16> args;
+ addAll(args, {"args", "left", "behind"});
+
+ EXPECT_CALL(dumpAlldumper_, dumpCritical(fd, ElementsAreArray(args)));
+ EXPECT_CALL(dumpAlldumper_, dumpHigh(fd, ElementsAreArray(args)));
+ EXPECT_CALL(dumpAlldumper_, dumpNormal(fd, ElementsAreArray(args)));
+
+ priorityDump(dumpAlldumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, priorityArgWithPriorityMissing) {
+ Vector<String16> args;
+ addAll(args, {"--dump-priority"});
+ EXPECT_CALL(dumper_, dump(fd, ElementsAreArray(args)));
+
+ priorityDump(dumper_, fd, args);
+}
+
+TEST_F(PriorityDumperTest, priorityArgWithInvalidPriority) {
+ Vector<String16> args;
+ addAll(args, {"--dump-priority", "REALLY_HIGH"});
+ EXPECT_CALL(dumper_, dump(fd, ElementsAreArray(args)));
+
+ priorityDump(dumper_, fd, args);
+}
\ No newline at end of file
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/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a346c0a..665a32b 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -640,11 +640,9 @@
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
const VkSurfaceFormatKHR kWideColorFormats[] = {
- {VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
- {VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
- {VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+ {VK_FORMAT_R8G8B8A8_UNORM,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
+ {VK_FORMAT_R8G8B8A8_SRGB,
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
};
const uint32_t kNumWideColorFormats =