Merge "Revert "Add isUidActiveOrForeground for camera/audio to use."" into rvc-dev am: ae1e9f614a am: ea3dfdbb13
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/11369230
Change-Id: I0fe3d154e7e46ac5016139576590897741c39b7f
diff --git a/.gitignore b/.gitignore
index 0d20b64..685e379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
+*.iml
*.pyc
+.idea/
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0473bb8..a686dfb 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/idlcli/
include/input/
+ include/powermanager/
libs/binder/fuzzer/
libs/binder/ndk/
libs/binderthreadstate/
@@ -18,7 +19,9 @@
opengl/libs/
services/bufferhub/
services/inputflinger/
+ services/powermanager/
services/surfaceflinger/
+ services/vibratorservice/
services/vr/
vulkan/
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8173c89..5db44c7 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -58,6 +58,14 @@
},
{
"name": "libsurfaceflinger_unittest"
+ },
+ {
+ "name": "CtsGraphicsTestCases",
+ "options": [
+ {
+ "include-filter": "android.graphics.cts.VulkanPreTransformTest"
+ }
+ ]
}
]
}
diff --git a/build/phone-hdpi-512-dalvik-heap.mk b/build/phone-hdpi-512-dalvik-heap.mk
index 102c3f1..f9a12ef 100644
--- a/build/phone-hdpi-512-dalvik-heap.mk
+++ b/build/phone-hdpi-512-dalvik-heap.mk
@@ -17,10 +17,10 @@
# Provides overrides to configure the Dalvik heap for a standard high density
# phone with around 512MB total RAM.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=5m \
- dalvik.vm.heapgrowthlimit=48m \
- dalvik.vm.heapsize=128m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=2m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=5m \
+ dalvik.vm.heapgrowthlimit?=48m \
+ dalvik.vm.heapsize?=128m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=2m
diff --git a/build/phone-hdpi-dalvik-heap.mk b/build/phone-hdpi-dalvik-heap.mk
index cc0ac90..71cce74 100644
--- a/build/phone-hdpi-dalvik-heap.mk
+++ b/build/phone-hdpi-dalvik-heap.mk
@@ -16,9 +16,9 @@
# Provides overrides to configure the Dalvik heap for a standard high density phone.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=5m \
- dalvik.vm.heapsize=32m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=2m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=5m \
+ dalvik.vm.heapsize?=32m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=2m
diff --git a/build/phone-xhdpi-1024-dalvik-heap.mk b/build/phone-xhdpi-1024-dalvik-heap.mk
index 221227d..a522a7d 100644
--- a/build/phone-xhdpi-1024-dalvik-heap.mk
+++ b/build/phone-xhdpi-1024-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a xhdpi phone
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=8m \
- dalvik.vm.heapgrowthlimit=96m \
- dalvik.vm.heapsize=256m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=8m \
+ dalvik.vm.heapgrowthlimit?=96m \
+ dalvik.vm.heapsize?=256m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=8m
diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk
index 7ccfc13..f38d2f2 100644
--- a/build/phone-xhdpi-2048-dalvik-heap.mk
+++ b/build/phone-xhdpi-2048-dalvik-heap.mk
@@ -17,10 +17,10 @@
# Provides overrides to configure the Dalvik heap for a 2GB phone
# 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=8m \
- dalvik.vm.heapgrowthlimit=192m \
- dalvik.vm.heapsize=512m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=8m \
+ dalvik.vm.heapgrowthlimit?=192m \
+ dalvik.vm.heapsize?=512m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=8m
diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk
index 2b84841..a6ff5a8 100644
--- a/build/phone-xhdpi-4096-dalvik-heap.mk
+++ b/build/phone-xhdpi-4096-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a 4GB phone
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=8m \
- dalvik.vm.heapgrowthlimit=192m \
- dalvik.vm.heapsize=512m \
- dalvik.vm.heaptargetutilization=0.6 \
- dalvik.vm.heapminfree=8m \
- dalvik.vm.heapmaxfree=16m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=8m \
+ dalvik.vm.heapgrowthlimit?=192m \
+ dalvik.vm.heapsize?=512m \
+ dalvik.vm.heaptargetutilization?=0.6 \
+ dalvik.vm.heapminfree?=8m \
+ dalvik.vm.heapmaxfree?=16m
diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk
index 2bacc4a..f08830b 100644
--- a/build/phone-xhdpi-6144-dalvik-heap.mk
+++ b/build/phone-xhdpi-6144-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a 6GB phone
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=16m \
- dalvik.vm.heapgrowthlimit=256m \
- dalvik.vm.heapsize=512m \
- dalvik.vm.heaptargetutilization=0.5 \
- dalvik.vm.heapminfree=8m \
- dalvik.vm.heapmaxfree=32m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=16m \
+ dalvik.vm.heapgrowthlimit?=256m \
+ dalvik.vm.heapsize?=512m \
+ dalvik.vm.heaptargetutilization?=0.5 \
+ dalvik.vm.heapminfree?=8m \
+ dalvik.vm.heapmaxfree?=32m
diff --git a/build/tablet-10in-xhdpi-2048-dalvik-heap.mk b/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
index 1721fcc..48c6ea6 100644
--- a/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
+++ b/build/tablet-10in-xhdpi-2048-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a standard tablet device.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=16m \
- dalvik.vm.heapgrowthlimit=192m \
- dalvik.vm.heapsize=512m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=16m \
+ dalvik.vm.heapgrowthlimit?=192m \
+ dalvik.vm.heapsize?=512m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=8m
diff --git a/build/tablet-7in-hdpi-1024-dalvik-heap.mk b/build/tablet-7in-hdpi-1024-dalvik-heap.mk
index 7fd34b5..d0027dc 100644
--- a/build/tablet-7in-hdpi-1024-dalvik-heap.mk
+++ b/build/tablet-7in-hdpi-1024-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a standard tablet device.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=8m \
- dalvik.vm.heapgrowthlimit=80m \
- dalvik.vm.heapsize=384m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=8m \
+ dalvik.vm.heapgrowthlimit?=80m \
+ dalvik.vm.heapsize?=384m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=8m
diff --git a/build/tablet-7in-xhdpi-2048-dalvik-heap.mk b/build/tablet-7in-xhdpi-2048-dalvik-heap.mk
index e0f20c1..7c06b4b 100644
--- a/build/tablet-7in-xhdpi-2048-dalvik-heap.mk
+++ b/build/tablet-7in-xhdpi-2048-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a 320dpi 7" tablet device.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=16m \
- dalvik.vm.heapgrowthlimit=192m \
- dalvik.vm.heapsize=512m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=8m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=16m \
+ dalvik.vm.heapgrowthlimit?=192m \
+ dalvik.vm.heapsize?=512m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=8m
diff --git a/build/tablet-dalvik-heap.mk b/build/tablet-dalvik-heap.mk
index f577fb8..1688665 100644
--- a/build/tablet-dalvik-heap.mk
+++ b/build/tablet-dalvik-heap.mk
@@ -16,10 +16,10 @@
# Provides overrides to configure the Dalvik heap for a standard tablet device.
-PRODUCT_PROPERTY_OVERRIDES += \
- dalvik.vm.heapstartsize=5m \
- dalvik.vm.heapgrowthlimit=48m \
- dalvik.vm.heapsize=256m \
- dalvik.vm.heaptargetutilization=0.75 \
- dalvik.vm.heapminfree=512k \
- dalvik.vm.heapmaxfree=2m
+PRODUCT_VENDOR_PROPERTIES += \
+ dalvik.vm.heapstartsize?=5m \
+ dalvik.vm.heapgrowthlimit?=48m \
+ dalvik.vm.heapsize?=256m \
+ dalvik.vm.heaptargetutilization?=0.75 \
+ dalvik.vm.heapminfree?=512k \
+ dalvik.vm.heapmaxfree?=2m
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 28fdaa4..fc3572c 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -239,6 +239,10 @@
{ OPT, "events/kmem/ion_heap_shrink/enable" },
{ OPT, "events/ion/ion_stat/enable" },
} },
+ { "thermal", "Thermal event", 0, {
+ { REQ, "events/thermal/thermal_temperature/enable" },
+ { OPT, "events/thermal/cdev_update/enable" },
+ } },
};
struct TracingVendorCategory {
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 9b2f4a8..d2ccef1 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -125,6 +125,8 @@
chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable
chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable
chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total/enable
+ chmod 0666 /sys/kernel/tracing/events/gpu_mem/gpu_mem_total/enable
# disk
chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
@@ -164,6 +166,12 @@
chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable
chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable
+ # thermal
+ chmod 0666 /sys/kernel/debug/tracing/events/thermal/thermal_temperature/enable
+ chmod 0666 /sys/kernel/tracing/events/thermal/thermal_temperature/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable
+ chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable
+
# Tracing disabled by default
write /sys/kernel/debug/tracing/tracing_on 0
write /sys/kernel/tracing/tracing_on 0
diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp
index 840ae47..e5c52d8 100644
--- a/cmds/bugreport/bugreport.cpp
+++ b/cmds/bugreport/bugreport.cpp
@@ -27,12 +27,20 @@
// dumpstate, then connect to the dumpstate local client to read the
// output. All of the dumpstate output is written to stdout, including
// any errors encountered while reading/writing the output.
-int main() {
-
+int main(int argc, char* /*argv*/[]) {
fprintf(stderr, "=============================================================================\n");
- fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
+ fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n");
+ fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n");
+ fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n");
+ fprintf(stderr, "WARNING: On the device use: bugreportz\n");
+ fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n");
fprintf(stderr, "=============================================================================\n\n\n");
+ if (argc != 1) {
+ fprintf(stderr, "usage: bugreport\n");
+ exit(1);
+ }
+
// Start the dumpstate service.
property_set("ctl.start", "dumpstate");
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index 40346be..1d48e08 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -30,7 +30,7 @@
static void show_usage() {
fprintf(stderr,
- "usage: bugreportz [-h | -v]\n"
+ "usage: bugreportz [-hpv]\n"
" -h: to display this help message\n"
" -p: display progress\n"
" -v: to display the version\n"
@@ -64,6 +64,12 @@
}
}
+ // We don't support any non-option arguments.
+ if (optind != argc) {
+ show_usage();
+ return EXIT_FAILURE;
+ }
+
// TODO: code below was copy-and-pasted from bugreport.cpp (except by the
// timeout value);
// should be reused instead.
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index acca11a..4058934 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -110,29 +110,20 @@
],
required: [
"atrace",
- "df",
- "getprop",
"ip",
"iptables",
- "ip6tables",
- "kill",
"librank",
"logcat",
"lpdump",
"lpdumpd",
- "lsmod",
- "lsof",
- "netstat",
- "printenv",
"procrank",
"screencap",
"showmap",
"ss",
"storaged",
- "top",
- "uptime",
+ "toolbox",
+ "toybox",
"vdc",
- "vril-dump",
],
init_rc: ["dumpstate.rc"],
}
@@ -161,6 +152,8 @@
"tests/dumpstate_smoke_test.cpp",
],
static_libs: ["libgmock"],
+ test_config: "dumpstate_smoke_test.xml",
+ test_suites: ["device-tests"],
}
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 083944f..8b03cfd 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -1,7 +1,21 @@
{
"presubmit": [
+ // TODO(159590499) add BugreportManagerTestCases
+ {
+ "name": "dumpstate_smoke_test"
+ },
{
"name": "dumpstate_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "BugreportManagerTestCases"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/packages/Shell"
+ }
]
-}
\ No newline at end of file
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9ba4819..581d3de 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -209,6 +209,10 @@
return fd;
}
+static int OpenForWrite(std::string path) {
+ return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
static int OpenForRead(std::string path) {
return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
@@ -274,6 +278,27 @@
return version_code;
}
+static bool PathExists(const std::string& path) {
+ struct stat sb;
+ return stat(path.c_str(), &sb) == 0;
+}
+
+static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) {
+ if (input_file == output_file) {
+ MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n",
+ output_file.c_str());
+ return false;
+ }
+ else if (PathExists(output_file)) {
+ MYLOGD("Cannot overwrite an existing file (%s)\n", output_file.c_str());
+ return false;
+ }
+
+ MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str());
+ android::base::unique_fd out_fd(OpenForWrite(output_file));
+ return CopyFileToFd(input_file, out_fd.get());
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -305,8 +330,9 @@
/*
* Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
- * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
- * is set, the vector only contains files that were written in the last 30 minutes.
+ * The returned vector is sorted by the mtimes of the dumps with descending
+ * order. If |limit_by_mtime| is set, the vector only contains files that
+ * were written in the last 30 minutes.
*/
static std::vector<DumpData> GetDumpFds(const std::string& dir_path,
const std::string& file_prefix,
@@ -353,6 +379,10 @@
dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime});
}
+ if (!dump_data.empty()) {
+ std::sort(dump_data.begin(), dump_data.end(),
+ [](const auto& a, const auto& b) { return a.mtime > b.mtime; });
+ }
return dump_data;
}
@@ -1362,6 +1392,46 @@
printf("\n");
}
+static void DumpstateLimitedOnly() {
+ // Trimmed-down version of dumpstate to only include a whitelisted
+ // set of logs (system log, event log, and system server / system app
+ // crashes, and networking logs). See b/136273873 and b/138459828
+ // for context.
+ DurationReporter duration_reporter("DUMPSTATE");
+ unsigned long timeout_ms;
+ // calculate timeout
+ timeout_ms = logcat_timeout({"main", "system", "crash"});
+ RunCommand("SYSTEM LOG",
+ {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+ timeout_ms = logcat_timeout({"events"});
+ RunCommand(
+ "EVENT LOG",
+ {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+
+ printf("========================================================\n");
+ printf("== Networking Service\n");
+ printf("========================================================\n");
+
+ RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"},
+ CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+
+ printf("========================================================\n");
+ printf("== Dropbox crashes\n");
+ printf("========================================================\n");
+
+ RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
+ RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
+
+ printf("========================================================\n");
+ printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+ ds.progress_->GetMax(), ds.progress_->GetInitialMax());
+ printf("========================================================\n");
+ printf("== dumpstate: done (id %d)\n", ds.id_);
+ printf("========================================================\n");
+}
+
// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
// via the consent they are shown. Ignores other errors that occur while running various
// commands. The consent checking is currently done around long running tasks, which happen to
@@ -2047,11 +2117,12 @@
static void ShowUsage() {
fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
- "[-z] [-s] [-S] [-q] [-P] [-R] [-V version]\n"
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
+ "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
" -h: display this help message\n"
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to custom directory (only in limited mode)\n"
" -d: append date to filename\n"
" -p: capture screenshot to filename.png\n"
" -z: generate zipped file\n"
@@ -2061,6 +2132,7 @@
" -P: send broadcast when started and do progress updates\n"
" -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
" -w: start binder service and make it wait for a call to startBugreport\n"
+ " -L: output limited information that is safe for submission in feedback reports\n"
" -v: prints the dumpstate header and exit\n");
}
@@ -2221,6 +2293,13 @@
do_text_file = false;
}
}
+
+ std::string final_path = ds.path_;
+ if (ds.options_->OutputToCustomFile()) {
+ final_path = ds.GetPath(ds.options_->out_dir, ".zip");
+ android::os::CopyFileToFile(ds.path_, final_path);
+ }
+
if (ds.options_->use_control_socket) {
if (do_text_file) {
dprintf(ds.control_socket_fd_,
@@ -2228,7 +2307,7 @@
"for more details\n",
ds.log_path_.c_str());
} else {
- dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
+ dprintf(ds.control_socket_fd_, "OK:%s\n", final_path.c_str());
}
}
}
@@ -2306,13 +2385,12 @@
"do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
"is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
"wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
- "args: %s\n",
+ "limited_only: %d args: %s\n",
options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
options.do_screenshot, options.is_remote_mode, options.show_header_only,
- options.do_start_service,
- options.telephony_only, options.wifi_only, options.do_progress_updates,
- options.bugreport_fd.get(), options.bugreport_mode.c_str(),
- toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
+ options.do_start_service, options.telephony_only, options.wifi_only,
+ options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
+ toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
}
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2334,11 +2412,12 @@
Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
RunStatus status = RunStatus::OK;
int c;
- while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) {
switch (c) {
// clang-format off
case 'd': do_add_date = true; break;
case 'z': do_zip_file = true; break;
+ case 'o': out_dir = optarg; break;
case 's': use_socket = true; break;
case 'S': use_control_socket = true; break;
case 'v': show_header_only = true; break;
@@ -2346,6 +2425,7 @@
case 'p': do_screenshot = true; break;
case 'P': do_progress_updates = true; break;
case 'R': is_remote_mode = true; break;
+ case 'L': limited_only = true; break;
case 'V': break; // compatibility no-op
case 'w':
// This was already processed
@@ -2459,8 +2539,8 @@
* If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
* gets added to the archive.
*
- * Bugreports are first generated in a local directory and later copied to the caller's fd if
- * supplied.
+ * Bugreports are first generated in a local directory and later copied to the caller's fd
+ * or directory if supplied.
*/
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
const std::string& calling_package) {
@@ -2630,6 +2710,7 @@
// duration is logged into MYLOG instead.
PrintHeader();
+ // TODO(b/158737089) reduce code repetition in if branches
if (options_->telephony_only) {
MaybeTakeEarlyScreenshot();
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2641,6 +2722,11 @@
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateWifiOnly();
+ } else if (options_->limited_only) {
+ MaybeTakeEarlyScreenshot();
+ onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
+ MaybeCheckUserConsent(calling_uid, calling_package);
+ DumpstateLimitedOnly();
} else {
// Invoke critical dumpsys first to preserve system state, before doing anything else.
RunDumpsysCritical();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 28d8936..0d25d30 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -377,6 +377,8 @@
bool do_start_service = false;
bool telephony_only = false;
bool wifi_only = false;
+ // Trimmed-down version of dumpstate to only include whitelisted logs.
+ bool limited_only = false;
// Whether progress updates should be published.
bool do_progress_updates = false;
// The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
@@ -384,10 +386,12 @@
// The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
- // File descriptor to output zip file.
+ // File descriptor to output zip file. Takes precedence over out_dir.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
android::base::unique_fd screenshot_fd;
+ // Custom output directory.
+ std::string out_dir;
// Bugreport mode of the bugreport.
std::string bugreport_mode;
// Command-line arguments as string
@@ -413,6 +417,12 @@
// specified, it is preferred. If not bugreport is written to /bugreports.
return !use_socket;
}
+
+ /* Returns if options specified require writing to custom file location */
+ bool OutputToCustomFile() {
+ // Custom location is only honored in limited mode.
+ return limited_only && !out_dir.empty() && bugreport_fd.get() == -1;
+ }
};
// TODO: initialize fields on constructor
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
new file mode 100644
index 0000000..0aff200
--- /dev/null
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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 dumpstate_smoke_test">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="dumpstate_smoke_test" />
+ <option name="native-test-timeout" value="600000" />
+ </test>
+</configuration>
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index 68d3733..ec89c0d 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -30,7 +30,7 @@
bool do_wait = false;
int c;
// Keep flags in sync with Dumpstate::DumpOptions::Initialize.
- while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) {
+ while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1 && !do_wait) {
switch (c) {
case 'w':
do_wait = true;
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 6f2d754..bb0e5ad 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -209,13 +209,12 @@
static std::shared_ptr<std::vector<SectionInfo>> sections;
static Dumpstate& ds;
static std::chrono::milliseconds duration;
- static void SetUpTestCase() {
+ static void GenerateBugreport() {
// clang-format off
char* argv[] = {
(char*)"dumpstate",
(char*)"-d",
- (char*)"-z",
- (char*)"-B"
+ (char*)"-z"
};
// clang-format on
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
@@ -236,20 +235,20 @@
std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
+ GenerateBugreport();
EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
}
-TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
+TEST_F(ZippedBugreportGenerationTest, Is3MBMBinSize) {
struct stat st;
EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
EXPECT_GE(st.st_size, 3000000 /* 3MB */);
- EXPECT_LE(st.st_size, 30000000 /* 30MB */);
}
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
+TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
<< duration.count() << " s.";
- EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
+ EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
<< duration.count() << " s.";
}
@@ -266,7 +265,8 @@
CloseArchive(handle);
}
- void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
+ void FileExists(const char* filename, uint32_t minsize,
+ uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
ZipEntry entry;
GetEntry(handle, filename, &entry);
EXPECT_GT(entry.uncompressed_length, minsize);
@@ -285,7 +285,7 @@
main_entry.uncompressed_length);
// contains main entry file
- FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
+ FileExists(bugreport_txt_name.c_str(), 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
@@ -301,8 +301,9 @@
}
TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
- FileExists("dumpstate_board.bin", 1000000U, 80000000U);
- FileExists("dumpstate_board.txt", 100000U, 1000000U);
+ // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
+ // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
+ // FileExists("dumpstate_board.txt", 100000U, 1000000U);
}
TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 9871a3b..c7df1bb 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -172,6 +172,7 @@
EXPECT_FALSE(options_.do_add_date);
EXPECT_FALSE(options_.do_zip_file);
+ EXPECT_EQ("", options_.out_dir);
EXPECT_FALSE(options_.use_socket);
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
@@ -179,6 +180,7 @@
EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
+ EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -206,6 +208,7 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -231,6 +234,7 @@
EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
+ EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -249,6 +253,7 @@
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
EXPECT_FALSE(options_.do_start_service);
+ EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
@@ -266,6 +271,7 @@
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
@@ -282,6 +288,7 @@
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
@@ -299,6 +306,7 @@
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
@@ -316,6 +324,7 @@
EXPECT_FALSE(options_.show_header_only);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
@@ -333,6 +342,39 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
+ EXPECT_FALSE(options_.limited_only);
+}
+
+TEST_F(DumpOptionsTest, InitializeLimitedOnlyBugreport) {
+ // clang-format off
+ char* argv[] = {
+ const_cast<char*>("dumpstatez"),
+ const_cast<char*>("-S"),
+ const_cast<char*>("-d"),
+ const_cast<char*>("-z"),
+ const_cast<char*>("-q"),
+ const_cast<char*>("-L"),
+ const_cast<char*>("-o abc")
+ };
+ // clang-format on
+
+ Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
+
+ EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+ EXPECT_TRUE(options_.do_add_date);
+ EXPECT_TRUE(options_.do_zip_file);
+ EXPECT_TRUE(options_.use_control_socket);
+ EXPECT_FALSE(options_.do_vibrate);
+ EXPECT_TRUE(options_.limited_only);
+ EXPECT_EQ(" abc", std::string(options_.out_dir));
+
+ // Other options retain default values
+ EXPECT_FALSE(options_.show_header_only);
+ EXPECT_FALSE(options_.do_screenshot);
+ EXPECT_FALSE(options_.do_progress_updates);
+ EXPECT_FALSE(options_.is_remote_mode);
+ EXPECT_FALSE(options_.use_socket);
+ EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeDefaultBugReport) {
@@ -361,6 +403,7 @@
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
EXPECT_FALSE(options_.wifi_only);
+ EXPECT_FALSE(options_.limited_only);
}
TEST_F(DumpOptionsTest, InitializePartial1) {
@@ -390,6 +433,7 @@
EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
+ EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
@@ -419,6 +463,7 @@
EXPECT_FALSE(options_.do_zip_file);
EXPECT_FALSE(options_.use_socket);
EXPECT_FALSE(options_.use_control_socket);
+ EXPECT_FALSE(options_.limited_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index a427c8d..1327cfd 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -230,7 +230,7 @@
}
const size_t N = services.size();
- if (N > 1) {
+ if (N > 1 || showListOnly) {
// first print a list of the current services
std::cout << "Currently running services:" << std::endl;
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index b9395ba..3467898 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -206,10 +206,7 @@
}
void AssertRunningServices(const std::vector<std::string>& services) {
- std::string expected;
- if (services.size() > 1) {
- expected.append("Currently running services:\n");
- }
+ std::string expected = "Currently running services:\n";
for (const std::string& service : services) {
expected.append(" ").append(service).append("\n");
}
@@ -263,6 +260,21 @@
AssertRunningServices({"Locksmith", "Valet"});
}
+TEST_F(DumpsysTest, ListServicesOneRegistered) {
+ ExpectListServices({"Locksmith"});
+ ExpectCheckService("Locksmith");
+
+ CallMain({"-l"});
+
+ AssertRunningServices({"Locksmith"});
+}
+
+TEST_F(DumpsysTest, ListServicesEmpty) {
+ CallMain({"-l"});
+
+ AssertRunningServices({});
+}
+
// Tests 'dumpsys -l' when a service is not running
TEST_F(DumpsysTest, ListRunningServices) {
ExpectListServices({"Locksmith", "Valet"});
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 402767a..64bfdf9 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -37,10 +37,16 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandAlwaysOnDisable.cpp",
+ "vibrator/CommandAlwaysOnEnable.cpp",
"vibrator/CommandCompose.cpp",
"vibrator/CommandGetCapabilities.cpp",
"vibrator/CommandGetCompositionDelayMax.cpp",
"vibrator/CommandGetCompositionSizeMax.cpp",
+ "vibrator/CommandGetPrimitiveDuration.cpp",
+ "vibrator/CommandGetSupportedAlwaysOnEffects.cpp",
+ "vibrator/CommandGetSupportedEffects.cpp",
+ "vibrator/CommandGetSupportedPrimitives.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index a8e5954..b874455 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -17,6 +17,7 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#include <android/binder_enums.h>
#include <hidl/HidlSupport.h>
#include <iomanip>
@@ -66,7 +67,7 @@
} // namespace overrides
-template <typename T, typename R = hardware::hidl_enum_range<T>>
+template <typename T, typename R = ndk::enum_range<T>>
inline std::istream &operator>>(std::istream &stream, T &out) {
using overrides::operator>>;
auto validRange = R();
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index ca5142d..6c30a9e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -16,8 +16,12 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#include <future>
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include "utils.h"
@@ -101,6 +105,18 @@
namespace V1_3 = ::android::hardware::vibrator::V1_3;
namespace aidl = ::aidl::android::hardware::vibrator;
+class VibratorCallback : public aidl::BnVibratorCallback {
+public:
+ ndk::ScopedAStatus onComplete() override {
+ mPromise.set_value();
+ return ndk::ScopedAStatus::ok();
+ }
+ void waitForComplete() { mPromise.get_future().wait(); }
+
+private:
+ std::promise<void> mPromise;
+};
+
} // namespace vibrator
} // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
new file mode 100644
index 0000000..9afa300
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandAlwaysOnDisable : public Command {
+ std::string getDescription() const override { return "Disarm always-on haptic source."; }
+
+ std::string getUsageSummary() const override { return "<id>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
new file mode 100644
index 0000000..bb7f9f2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+using aidl::EffectStrength;
+
+class CommandAlwaysOnEnable : public Command {
+ std::string getDescription() const override {
+ return "Arm always-on haptic source with an effect.";
+ }
+
+ std::string getUsageSummary() const override { return "<id> <effect> <strength>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ {"<effect>", {"Effect ID."}},
+ {"<strength>", {"0-2."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (auto effect = args.pop<decltype(mEffect)>()) {
+ mEffect = *effect;
+ std::cout << "Effect: " << toString(mEffect) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Effect!" << std::endl;
+ return USAGE;
+ }
+ if (auto strength = args.pop<decltype(mStrength)>()) {
+ mStrength = *strength;
+ std::cout << "Strength: " << toString(mStrength) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Strength!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+ Effect mEffect;
+ EffectStrength mStrength;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 4721a5f..eb9008b 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -28,19 +28,33 @@
class CommandCompose : public Command {
std::string getDescription() const override { return "Compose vibration."; }
- std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+ std::string getUsageSummary() const override {
+ return "[options] <delay> <primitive> <scale> ...";
+ }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<delay>", {"In milliseconds"}},
{"<primitive>", {"Primitive ID."}},
- {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+ {"<scale>", {"0.0 (inclusive) - 1.0 (inclusive)."}},
{"...", {"May repeat multiple times."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
while (!args.empty()) {
CompositeEffect effect;
if (auto delay = args.pop<decltype(effect.delayMs)>()) {
@@ -50,16 +64,15 @@
std::cerr << "Missing or Invalid Delay!" << std::endl;
return USAGE;
}
- // TODO: Use range validation when supported by AIDL
- if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
- effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+ if (auto primitive = args.pop<decltype(effect.primitive)>()) {
+ effect.primitive = *primitive;
std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
} else {
std::cerr << "Missing or Invalid Primitive!" << std::endl;
return USAGE;
}
if (auto scale = args.pop<decltype(effect.scale)>();
- scale && *scale > 0.0 && scale <= 1.0) {
+ scale && *scale >= 0.0 && scale <= 1.0) {
effect.scale = *scale;
std::cout << "Scale: " << effect.scale << std::endl;
} else {
@@ -76,21 +89,33 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ auto hal = getHal<aidl::IVibrator>();
+
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
- return ret;
+ std::shared_ptr<VibratorCallback> callback;
+
+ if (mBlocking) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+
+ if (status.isOk() && callback) {
+ callback->waitForComplete();
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
+ bool mBlocking;
std::vector<CompositeEffect> mComposite;
};
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
new file mode 100644
index 0000000..460d39e
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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 <future>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetPrimitiveDuration : public Command {
+ std::string getDescription() const override {
+ return "Retrieve effect primitive's duration in milliseconds.";
+ }
+
+ std::string getUsageSummary() const override { return "<primitive>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<primitive>", {"Primitive ID."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto primitive = args.pop<decltype(mPrimitive)>()) {
+ mPrimitive = *primitive;
+ std::cout << "Primitive: " << toString(mPrimitive) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Primitive!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t duration;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Duration: " << duration << std::endl;
+
+ return ret;
+ }
+
+ CompositePrimitive mPrimitive;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>(
+ "getPrimitiveDuration");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
new file mode 100644
index 0000000..edfcd91
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedAlwaysOnEffects : public Command {
+ std::string getDescription() const override { return "List of supported always-on effects."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>(
+ "getSupportedAlwaysOnEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
new file mode 100644
index 0000000..7658f22
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::Effect;
+
+class CommandGetSupportedEffects : public Command {
+ std::string getDescription() const override { return "List supported effects."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>(
+ "getSupportedEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
new file mode 100644
index 0000000..d101681
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetSupportedPrimitives : public Command {
+ std::string getDescription() const override { return "List of supported effect primitive."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ std::vector<CompositePrimitive> primitives;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Primitives:" << std::endl;
+ for (auto &e : primitives) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>(
+ "getSupportedPrimitives");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 4e7e493..8212fc1 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -26,16 +31,28 @@
class CommandOn : public Command {
std::string getDescription() const override { return "Turn on vibrator."; }
- std::string getUsageSummary() const override { return "<duration>"; }
+ std::string getUsageSummary() const override { return "[options] <duration>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<duration>", {"In milliseconds."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto duration = args.pop<decltype(mDuration)>()) {
mDuration = *duration;
} else {
@@ -52,9 +69,21 @@
Status doMain(Args && /*args*/) override {
std::string statusStr;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
+
statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
@@ -65,11 +94,20 @@
return UNAVAILABLE;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(mDuration));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
return ret;
}
+ bool mBlocking;
uint32_t mDuration;
};
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 69c7e37..c897686 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -51,16 +56,17 @@
static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
-using V1_0::EffectStrength;
-using V1_3::Effect;
+using aidl::Effect;
+using aidl::EffectStrength;
class CommandPerform : public Command {
std::string getDescription() const override { return "Perform vibration effect."; }
- std::string getUsageSummary() const override { return "<effect> <strength>"; }
+ std::string getUsageSummary() const override { return "[options] <effect> <strength>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<effect>", {"Effect ID."}},
{"<strength>", {"0-2."}},
};
@@ -68,6 +74,17 @@
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto effect = args.pop<decltype(mEffect)>()) {
mEffect = *effect;
std::cout << "Effect: " << toString(mEffect) << std::endl;
@@ -93,12 +110,23 @@
std::string statusStr;
uint32_t lengthMs;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
int32_t aidlLengthMs;
- auto status =
- hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
- static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+ auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
+ &aidlLengthMs);
+
statusStr = status.getDescription();
lengthMs = static_cast<uint32_t>(aidlLengthMs);
ret = status.isOk() ? OK : ERROR;
@@ -111,17 +139,20 @@
};
if (auto hal = getHal<V1_3::IVibrator>()) {
- hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
- static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_2::IVibrator>()) {
- hidlRet = hal->call(&V1_2::IVibrator::perform_1_2,
- static_cast<V1_2::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_1::IVibrator>()) {
hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
- static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback);
+ static_cast<V1_1::Effect_1_1>(mEffect),
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else if (auto hal = getHal<V1_0::IVibrator>()) {
hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
- mStrength, callback);
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else {
return UNAVAILABLE;
}
@@ -130,12 +161,21 @@
ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(lengthMs));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
std::cout << "Length: " << lengthMs << std::endl;
return ret;
}
+ bool mBlocking;
Effect mEffect;
EffectStrength mStrength;
};
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
index 6e079eb..b17cba1 100644
--- a/cmds/installd/CrateManager.cpp
+++ b/cmds/installd/CrateManager.cpp
@@ -86,7 +86,7 @@
}
void CrateManager::traverseAllPackagesForUser(
- const std::unique_ptr<std::string>& uuid, userid_t userId,
+ const std::optional<std::string>& uuid, userid_t userId,
std::function<void(FTSENT*)>& onHandlingPackage) {
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
@@ -96,21 +96,21 @@
void CrateManager::createCrate(
CratedFolder cratedFolder,
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+ std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) {
const char* path = cratedFolder->fts_path;
if (path == nullptr || *path == '\0') {
return;
}
- std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
- crateMetadata->uid = cratedFolder->fts_statp->st_uid;
- crateMetadata->packageName = mPackageName;
- crateMetadata->id = getValidatedCratedPath(path);
+ CrateMetadata crateMetadata;
+ crateMetadata.uid = cratedFolder->fts_statp->st_uid;
+ crateMetadata.packageName = mPackageName;
+ crateMetadata.id = getValidatedCratedPath(path);
- onCreateCrate(cratedFolder, crateMetadata);
+ onCreateCrate(cratedFolder, std::move(crateMetadata));
}
-void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) {
std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
createCrate(cratedFolder, onCreateCrate);
};
@@ -118,11 +118,11 @@
}
#if CRATE_DEBUG
-void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+void CrateManager::dump(const CrateMetadata& CrateMetadata) {
LOG(DEBUG) << "CrateMetadata = {"
- << "uid : \"" << CrateMetadata->uid
- << "\", packageName : \"" << CrateMetadata->packageName
- << "\", id : \"" << CrateMetadata->id
+ << "uid : \"" << CrateMetadata.uid
+ << "\", packageName : \"" << CrateMetadata.packageName
+ << "\", id : \"" << CrateMetadata.id
<< "\"}";
}
#endif
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
index 4332d4c..1f30b5d 100644
--- a/cmds/installd/CrateManager.h
+++ b/cmds/installd/CrateManager.h
@@ -25,7 +25,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -55,18 +55,18 @@
CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
~CrateManager();
- void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+ void traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate);
static void traverseChildDir(const std::string& targetDir,
std::function<void(FTSENT*)>& onVisitChildDir);
static void traverseAllPackagesForUser(
- const std::unique_ptr<std::string>& uuid,
+ const std::optional<std::string>& uuid,
userid_t userId,
std::function<void(FTSENT*)>& onHandlingPackage);
#if CRATE_DEBUG
- static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+ static void dump(const CrateMetadata& CrateMetadata);
#endif
private:
std::string mRoot;
@@ -75,7 +75,7 @@
void createCrate(
CratedFolder cratedFolder,
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+ std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate);
};
} // namespace installd
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 7d1c1ad..e7014c8 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -166,7 +166,7 @@
}
}
-binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
+binder::Status checkArgumentUuid(const std::optional<std::string>& uuid) {
if (!uuid || is_valid_filename(*uuid)) {
return ok();
} else {
@@ -175,7 +175,7 @@
}
}
-binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) {
+binder::Status checkArgumentUuidTestOrNull(const std::optional<std::string>& uuid) {
if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) {
return ok();
} else {
@@ -214,7 +214,7 @@
return ok();
}
-binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) {
+binder::Status checkArgumentPath(const std::optional<std::string>& path) {
if (path) {
return checkArgumentPath(*path);
} else {
@@ -421,8 +421,8 @@
}
binder::Status InstalldNativeService::createAppDataBatched(
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids,
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& packageNames,
+ const std::optional<std::vector<std::optional<std::string>>>& uuids,
+ const std::optional<std::vector<std::optional<std::string>>>& packageNames,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
int64_t* _aidl_return) {
@@ -432,10 +432,11 @@
ATRACE_BEGIN("createAppDataBatched");
binder::Status ret;
for (size_t i = 0; i < uuids->size(); i++) {
- if (!packageNames->at(i)) {
+ std::optional<std::string> packageName = packageNames->at(i);
+ if (!packageName) {
continue;
}
- ret = createAppData(uuids->at(i), *packageNames->at(i), userId, flags, appIds[i],
+ ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
seInfos[i], targetSdkVersions[i], _aidl_return);
if (!ret.isOk()) {
ATRACE_END();
@@ -446,7 +447,7 @@
return ok();
}
-binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -527,7 +528,7 @@
return ok();
}
-binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -588,7 +589,7 @@
return res;
}
-binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -708,7 +709,7 @@
return res;
}
-binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -780,7 +781,7 @@
return (gid != -1) ? gid : uid;
}
-binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::fixupAppData(const std::optional<std::string>& uuid,
int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -900,7 +901,7 @@
}
binder::Status InstalldNativeService::snapshotAppData(
- const std::unique_ptr<std::string>& volumeUuid,
+ const std::optional<std::string>& volumeUuid,
const std::string& packageName, int32_t user, int32_t snapshotId,
int32_t storageFlags, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -1027,7 +1028,7 @@
}
binder::Status InstalldNativeService::restoreAppDataSnapshot(
- const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName,
+ const std::optional<std::string>& volumeUuid, const std::string& packageName,
const int32_t appId, const std::string& seInfo, const int32_t user,
const int32_t snapshotId, int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
@@ -1074,6 +1075,7 @@
res = error(rc, "Failed copying " + from_ce + " to " + to_ce);
return res;
}
+ delete_dir_contents_and_dir(from_ce, true /* ignore_if_missing */);
}
if (needs_de_rollback) {
@@ -1090,6 +1092,7 @@
res = error(rc, "Failed copying " + from_de + " to " + to_de);
return res;
}
+ delete_dir_contents_and_dir(from_de, true /* ignore_if_missing */);
}
// Finally, restore the SELinux label on the app data.
@@ -1097,7 +1100,7 @@
}
binder::Status InstalldNativeService::destroyAppDataSnapshot(
- const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
+ const std::optional<std::string> &volumeUuid, const std::string& packageName,
const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId,
int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
@@ -1130,7 +1133,7 @@
}
binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified(
- const std::unique_ptr<std::string> &volumeUuid, const int32_t userId,
+ const std::optional<std::string> &volumeUuid, const int32_t user,
const std::vector<int32_t>& retainSnapshotIds) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
@@ -1138,7 +1141,7 @@
const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
- auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId);
+ auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user);
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir);
if (!dir) {
@@ -1157,7 +1160,7 @@
std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(),
snapshot_id) == retainSnapshotIds.end()) {
auto rollback_path = create_data_misc_ce_rollback_path(
- volume_uuid, userId, snapshot_id);
+ volume_uuid, user, snapshot_id);
int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */);
if (res != 0) {
return error(res, "Failed clearing snapshot " + rollback_path);
@@ -1167,8 +1170,8 @@
return ok();
}
-binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
- const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::string>& fromUuid,
+ const std::optional<std::string>& toUuid, const std::string& packageName,
int32_t appId, const std::string& seInfo,
int32_t targetSdkVersion, const std::string& fromCodePath) {
ENFORCE_UID(AID_SYSTEM);
@@ -1275,7 +1278,7 @@
return res;
}
-binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid,
int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -1293,7 +1296,7 @@
return ok();
}
-binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -1330,13 +1333,13 @@
return res;
}
-binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid,
int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
std::lock_guard<std::recursive_mutex> lock(mLock);
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto data_path = create_data_path(uuid_);
auto noop = (flags & FLAG_FREE_CACHE_NOOP);
@@ -1734,7 +1737,7 @@
fts_close(fts);
}
-binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
int32_t appId, const std::vector<int64_t>& ceDataInodes,
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
@@ -1774,7 +1777,7 @@
memset(&stats, 0, sizeof(stats));
memset(&extStats, 0, sizeof(extStats));
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (!IsQuotaSupported(uuidString)) {
@@ -1961,7 +1964,7 @@
return sizes;
}
-binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getUserSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -1981,7 +1984,7 @@
memset(&stats, 0, sizeof(stats));
memset(&extStats, 0, sizeof(extStats));
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (!IsQuotaSupported(uuidString)) {
@@ -2093,7 +2096,7 @@
return ok();
}
-binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getExternalSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -2108,7 +2111,7 @@
LOG(INFO) << "Measuring external " << userId;
#endif
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
int64_t totalSize = 0;
@@ -2210,9 +2213,9 @@
}
binder::Status InstalldNativeService::getAppCrates(
- const std::unique_ptr<std::string>& uuid,
+ const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
for (const auto& packageName : packageNames) {
@@ -2221,15 +2224,15 @@
#ifdef ENABLE_STORAGE_CRATES
std::lock_guard<std::recursive_mutex> lock(mLock);
- auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+ auto retVector = std::vector<std::optional<CrateMetadata>>();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
- [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate =
+ [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void {
if (cratedFolder == nullptr) {
return;
}
- retVector->push_back(std::move(crateMetadata));
+ retVector.push_back(std::move(crateMetadata));
};
for (const auto& packageName : packageNames) {
@@ -2241,15 +2244,15 @@
}
#if CRATE_DEBUG
- LOG(WARNING) << "retVector->size() =" << retVector->size();
- for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
- CrateManager::dump(*iter);
+ LOG(WARNING) << "retVector.size() =" << retVector.size();
+ for (auto& item : retVector) {
+ CrateManager::dump(*item);
}
#endif
*_aidl_return = std::move(retVector);
#else // ENABLE_STORAGE_CRATES
- *_aidl_return = nullptr;
+ _aidl_return->reset();
/* prevent compile warning fail */
if (userId < 0) {
@@ -2260,22 +2263,22 @@
}
binder::Status InstalldNativeService::getUserCrates(
- const std::unique_ptr<std::string>& uuid, int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ const std::optional<std::string>& uuid, int32_t userId,
+ std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
#ifdef ENABLE_STORAGE_CRATES
std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+ auto retVector = std::vector<std::optional<CrateMetadata>>();
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
- [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate =
+ [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void {
if (cratedFolder == nullptr) {
return;
}
- retVector->push_back(std::move(crateMetadata));
+ retVector.push_back(std::move(crateMetadata));
};
std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
@@ -2285,15 +2288,15 @@
CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
#if CRATE_DEBUG
- LOG(DEBUG) << "retVector->size() =" << retVector->size();
- for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
- CrateManager::dump(*iter);
+ LOG(DEBUG) << "retVector.size() =" << retVector.size();
+ for (auto& item : retVector) {
+ CrateManager::dump(*item);
}
#endif
*_aidl_return = std::move(retVector);
#else // ENABLE_STORAGE_CRATES
- *_aidl_return = nullptr;
+ _aidl_return->reset();
/* prevent compile warning fail */
if (userId < 0) {
@@ -2303,7 +2306,7 @@
return ok();
}
-binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -2374,19 +2377,19 @@
return ok();
}
-static const char* getCStr(const std::unique_ptr<std::string>& data,
+static const char* getCStr(const std::optional<std::string>& data,
const char* default_value = nullptr) {
- return data == nullptr ? default_value : data->c_str();
+ return data ? data->c_str() : default_value;
}
binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
- const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
- const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
- const std::unique_ptr<std::string>& classLoaderContext,
- const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
- const std::unique_ptr<std::string>& profileName,
- const std::unique_ptr<std::string>& dexMetadataPath,
- const std::unique_ptr<std::string>& compilationReason) {
+ const std::optional<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::optional<std::string>& uuid,
+ const std::optional<std::string>& classLoaderContext,
+ const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
+ const std::optional<std::string>& profileName,
+ const std::optional<std::string>& dexMetadataPath,
+ const std::optional<std::string>& compilationReason) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PATH(apkPath);
@@ -2432,7 +2435,7 @@
}
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
- const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+ const std::optional<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -2523,7 +2526,7 @@
return res;
}
-binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo) {
ENFORCE_UID(AID_SYSTEM);
@@ -2641,7 +2644,7 @@
}
binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
- const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) {
+ const std::string& instructionSet, const std::optional<std::string>& outputPath) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PATH(apkPath);
CHECK_ARGUMENT_PATH(outputPath);
@@ -2793,7 +2796,7 @@
binder::Status InstalldNativeService::reconcileSecondaryDexFile(
const std::string& dexPath, const std::string& packageName, int32_t uid,
- const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+ const std::vector<std::string>& isas, const std::optional<std::string>& volumeUuid,
int32_t storage_flag, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(volumeUuid);
@@ -2808,7 +2811,7 @@
binder::Status InstalldNativeService::hashSecondaryDexFile(
const std::string& dexPath, const std::string& packageName, int32_t uid,
- const std::unique_ptr<std::string>& volumeUuid, int32_t storageFlag,
+ const std::optional<std::string>& volumeUuid, int32_t storageFlag,
std::vector<uint8_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(volumeUuid);
@@ -2867,7 +2870,7 @@
// Mount volume's CE and DE storage to mirror
binder::Status InstalldNativeService::tryMountDataMirror(
- const std::unique_ptr<std::string>& uuid) {
+ const std::optional<std::string>& uuid) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
if (!sAppDataIsolationEnabled) {
@@ -2931,7 +2934,7 @@
// Unmount volume's CE and DE storage from mirror
binder::Status InstalldNativeService::onPrivateVolumeRemoved(
- const std::unique_ptr<std::string>& uuid) {
+ const std::optional<std::string>& uuid) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
if (!sAppDataIsolationEnabled) {
@@ -2975,7 +2978,7 @@
}
std::string InstalldNativeService::findDataMediaPath(
- const std::unique_ptr<std::string>& uuid, userid_t userid) {
+ const std::optional<std::string>& uuid, userid_t userid) {
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
@@ -2988,15 +2991,14 @@
}
binder::Status InstalldNativeService::isQuotaSupported(
- const std::unique_ptr<std::string>& uuid, bool* _aidl_return) {
- auto uuidString = uuid ? *uuid : "";
- *_aidl_return = IsQuotaSupported(uuidString);
+ const std::optional<std::string>& uuid, bool* _aidl_return) {
+ *_aidl_return = IsQuotaSupported(uuid.value_or(""));
return ok();
}
binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName,
int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
- const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) {
+ const std::optional<std::string>& dexMetadata, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(codePath);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 8e7d98b..9819327 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -40,81 +40,81 @@
static char const* getServiceName() { return "installd"; }
virtual status_t dump(int fd, const Vector<String16> &args) override;
- binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ binder::Status createUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t userSerial, int32_t flags);
- binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
binder::Status createAppDataBatched(
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids,
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& packageNames,
+ const std::optional<std::vector<std::optional<std::string>>>& uuids,
+ const std::optional<std::vector<std::optional<std::string>>>& packageNames,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
int64_t* _aidl_return);
- binder::Status createAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
- binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
- binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags);
- binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status clearAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
- binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status destroyAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
- binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
+ binder::Status fixupAppData(const std::optional<std::string>& uuid, int32_t flags);
- binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
+ binder::Status snapshotAppData(const std::optional<std::string>& volumeUuid,
const std::string& packageName, const int32_t user, const int32_t snapshotId,
int32_t storageFlags, int64_t* _aidl_return);
- binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
+ binder::Status restoreAppDataSnapshot(const std::optional<std::string>& volumeUuid,
const std::string& packageName, const int32_t appId, const std::string& seInfo,
const int32_t user, const int32_t snapshotId, int32_t storageFlags);
- binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
+ binder::Status destroyAppDataSnapshot(const std::optional<std::string> &volumeUuid,
const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
const int32_t snapshotId, int32_t storageFlags);
- binder::Status destroyCeSnapshotsNotSpecified(const std::unique_ptr<std::string> &volumeUuid,
- const int32_t userId, const std::vector<int32_t>& retainSnapshotIds);
+ binder::Status destroyCeSnapshotsNotSpecified(const std::optional<std::string> &volumeUuid,
+ const int32_t user, const std::vector<int32_t>& retainSnapshotIds);
- binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+ binder::Status getAppSize(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
int32_t appId, const std::vector<int64_t>& ceDataInodes,
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
- binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+ binder::Status getUserSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return);
- binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+ binder::Status getExternalSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return);
- binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+ binder::Status getAppCrates(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames,
int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>*
_aidl_return);
binder::Status getUserCrates(
- const std::unique_ptr<std::string>& uuid, int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ const std::optional<std::string>& uuid, int32_t userId,
+ std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>*
_aidl_return);
- binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
+ binder::Status setAppQuota(const std::optional<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota);
- binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
- const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+ binder::Status moveCompleteApp(const std::optional<std::string>& fromUuid,
+ const std::optional<std::string>& toUuid, const std::string& packageName,
int32_t appId, const std::string& seInfo,
int32_t targetSdkVersion, const std::string& fromCodePath);
binder::Status dexopt(const std::string& apkPath, int32_t uid,
- const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
- const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
- const std::unique_ptr<std::string>& classLoaderContext,
- const std::unique_ptr<std::string>& seInfo, bool downgrade,
- int32_t targetSdkVersion, const std::unique_ptr<std::string>& profileName,
- const std::unique_ptr<std::string>& dexMetadataPath,
- const std::unique_ptr<std::string>& compilationReason);
+ const std::optional<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::optional<std::string>& uuid,
+ const std::optional<std::string>& classLoaderContext,
+ const std::optional<std::string>& seInfo, bool downgrade,
+ int32_t targetSdkVersion, const std::optional<std::string>& profileName,
+ const std::optional<std::string>& dexMetadataPath,
+ const std::optional<std::string>& compilationReason);
binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
const std::string& outDexFile, int uid, bool* _aidl_return);
@@ -137,9 +137,9 @@
const std::string& profileName);
binder::Status rmPackageDir(const std::string& packageDir);
- binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
+ binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes,
int64_t cacheReservedBytes, int32_t flags);
- binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
+ binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
@@ -147,27 +147,27 @@
binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
const std::string& outputPath);
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
- const std::unique_ptr<std::string>& outputPath);
+ const std::optional<std::string>& outputPath);
binder::Status installApkVerity(const std::string& filePath,
android::base::unique_fd verityInput, int32_t contentSize);
binder::Status assertFsverityRootHashMatches(const std::string& filePath,
const std::vector<uint8_t>& expectedHash);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
- const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
+ const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
binder::Status hashSecondaryDexFile(const std::string& dexPath,
- const std::string& packageName, int32_t uid, const std::unique_ptr<std::string>& volumeUuid,
+ const std::string& packageName, int32_t uid, const std::optional<std::string>& volumeUuid,
int32_t storageFlag, std::vector<uint8_t>* _aidl_return);
binder::Status invalidateMounts();
- binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
+ binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid,
bool* _aidl_return);
- binder::Status tryMountDataMirror(const std::unique_ptr<std::string>& volumeUuid);
- binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
+ binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid);
+ binder::Status onPrivateVolumeRemoved(const std::optional<std::string>& volumeUuid);
binder::Status prepareAppProfile(const std::string& packageName,
int32_t userId, int32_t appId, const std::string& profileName,
- const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata,
+ const std::string& codePath, const std::optional<std::string>& dexMetadata,
bool* _aidl_return);
binder::Status migrateLegacyObbData();
@@ -184,7 +184,7 @@
/* Map from UID to cache quota size */
std::unordered_map<uid_t, int64_t> mCacheQuotas;
- std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid);
+ std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid);
};
} // namespace installd
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ffa8724..82be007 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -2325,7 +2325,7 @@
// out_secondary_dex_exists will be set to false.
bool reconcile_secondary_dex_file(const std::string& dex_path,
const std::string& pkgname, int uid, const std::vector<std::string>& isas,
- const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+ const std::optional<std::string>& volume_uuid, int storage_flag,
/*out*/bool* out_secondary_dex_exists) {
*out_secondary_dex_exists = false; // start by assuming the file does not exist.
if (isas.size() == 0) {
@@ -2346,7 +2346,7 @@
/* child -- drop privileges before continuing */
drop_capabilities(uid);
- const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
@@ -2447,11 +2447,11 @@
// the app.
// For any other errors (e.g. if any of the parameters are invalid) returns false.
bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid,
- const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+ const std::optional<std::string>& volume_uuid, int storage_flag,
std::vector<uint8_t>* out_secondary_dex_hash) {
out_secondary_dex_hash->clear();
- const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: "
@@ -2978,7 +2978,7 @@
appid_t app_id,
const std::string& profile_name,
const std::string& code_path,
- const std::unique_ptr<std::string>& dex_metadata) {
+ const std::optional<std::string>& dex_metadata) {
// Prepare the current profile.
std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name,
/*is_secondary_dex*/ false);
@@ -2989,7 +2989,7 @@
}
// Check if we need to install the profile from the dex metadata.
- if (dex_metadata == nullptr) {
+ if (!dex_metadata) {
return true;
}
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index cc44873..d35953c 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -21,6 +21,8 @@
#include <sys/types.h>
+#include <optional>
+
#include <cutils/multiuser.h>
namespace android {
@@ -100,17 +102,17 @@
appid_t app_id,
const std::string& profile_name,
const std::string& code_path,
- const std::unique_ptr<std::string>& dex_metadata);
+ const std::optional<std::string>& dex_metadata);
bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
bool reconcile_secondary_dex_file(const std::string& dex_path,
const std::string& pkgname, int uid, const std::vector<std::string>& isas,
- const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+ const std::optional<std::string>& volumeUuid, int storage_flag,
/*out*/bool* out_secondary_dex_exists);
bool hash_secondary_dex_file(const std::string& dex_path,
- const std::string& pkgname, int uid, const std::unique_ptr<std::string>& volume_uuid,
+ const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid,
int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash);
int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 5a5cb53..863cdfe 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -114,15 +114,14 @@
class CacheTest : public testing::Test {
protected:
InstalldNativeService* service;
- std::unique_ptr<std::string> testUuid;
+ std::optional<std::string> testUuid;
virtual void SetUp() {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(nullptr);
service = new InstalldNativeService();
- testUuid = std::make_unique<std::string>();
- *testUuid = std::string(kTestUuid);
+ testUuid = kTestUuid;
system("mkdir -p /data/local/tmp/user/0");
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 16e4055..96f5e44 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -193,7 +193,7 @@
const uid_t kTestAppGid = multiuser_get_shared_gid(kTestUserId, kTestAppId);
InstalldNativeService* service_;
- std::unique_ptr<std::string> volume_uuid_;
+ std::optional<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
std::string empty_dm_file_;
@@ -221,7 +221,7 @@
ASSERT_TRUE(init_selinux());
service_ = new InstalldNativeService();
- volume_uuid_ = nullptr;
+ volume_uuid_ = std::nullopt;
package_name_ = "com.installd.test.dexopt";
se_info_ = "default";
app_apk_dir_ = android_app_dir + package_name_;
@@ -294,7 +294,7 @@
}
// Create a secondary dex file on CE storage
- const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str();
+ const char* volume_uuid_cstr = volume_uuid_ ? volume_uuid_->c_str() : nullptr;
app_private_dir_ce_ = create_data_user_ce_package_path(
volume_uuid_cstr, kTestUserId, package_name_.c_str());
secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar";
@@ -353,36 +353,32 @@
if (class_loader_context == nullptr) {
class_loader_context = "&";
}
- std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
int32_t dexopt_needed = 0; // does not matter;
- std::unique_ptr<std::string> out_path = nullptr; // does not matter
+ std::optional<std::string> out_path; // does not matter
int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
std::string compiler_filter = "speed-profile";
- std::unique_ptr<std::string> class_loader_context_ptr(
- new std::string(class_loader_context));
- std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
bool downgrade = false;
int32_t target_sdk_version = 0; // default
- std::unique_ptr<std::string> profile_name_ptr = nullptr;
- std::unique_ptr<std::string> dm_path_ptr = nullptr;
- std::unique_ptr<std::string> compilation_reason_ptr = nullptr;
+ std::optional<std::string> profile_name;
+ std::optional<std::string> dm_path;
+ std::optional<std::string> compilation_reason;
binder::Status result = service_->dexopt(path,
uid,
- package_name_ptr,
+ package_name_,
kRuntimeIsa,
dexopt_needed,
out_path,
dex_flags,
compiler_filter,
volume_uuid_,
- class_loader_context_ptr,
- se_info_ptr,
+ class_loader_context,
+ se_info_,
downgrade,
target_sdk_version,
- profile_name_ptr,
- dm_path_ptr,
- compilation_reason_ptr);
+ profile_name,
+ dm_path,
+ compilation_reason);
ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
int expected_access = should_dex_be_compiled ? 0 : -1;
std::string odex = GetSecondaryDexArtifact(path, "odex");
@@ -481,41 +477,35 @@
bool downgrade,
bool should_binder_call_succeed,
/*out */ binder::Status* binder_result) {
- std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
- std::unique_ptr<std::string> out_path(
- oat_dir == nullptr ? nullptr : new std::string(oat_dir));
- std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
- std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+ std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
+ std::string class_loader_context = "&";
int32_t target_sdk_version = 0; // default
- std::unique_ptr<std::string> profile_name_ptr(new std::string("primary.prof"));
- std::unique_ptr<std::string> dm_path_ptr = nullptr;
- if (dm_path != nullptr) {
- dm_path_ptr.reset(new std::string(dm_path));
- }
- std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason"));
+ std::string profile_name = "primary.prof";
+ std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt;
+ std::string compilation_reason = "test-reason";
bool prof_result;
ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
- package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
- dm_path_ptr, &prof_result));
+ package_name_, kTestUserId, kTestAppId, profile_name, apk_path_,
+ dm_path_opt, &prof_result));
ASSERT_TRUE(prof_result);
binder::Status result = service_->dexopt(apk_path_,
uid,
- package_name_ptr,
+ package_name_,
kRuntimeIsa,
dexopt_needed,
out_path,
dex_flags,
compiler_filter,
volume_uuid_,
- class_loader_context_ptr,
- se_info_ptr,
+ class_loader_context,
+ se_info_,
downgrade,
target_sdk_version,
- profile_name_ptr,
- dm_path_ptr,
- compilation_reason_ptr);
+ profile_name,
+ dm_path_opt,
+ compilation_reason);
ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
if (!should_binder_call_succeed) {
@@ -992,7 +982,7 @@
bool result;
ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
- /*dex_metadata*/ nullptr, &result));
+ /*dex_metadata*/ {}, &result));
ASSERT_EQ(expected_result, result);
if (!expected_result) {
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 0fb62ae..1e7559d 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -99,15 +99,14 @@
class ServiceTest : public testing::Test {
protected:
InstalldNativeService* service;
- std::unique_ptr<std::string> testUuid;
+ std::optional<std::string> testUuid;
virtual void SetUp() {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(nullptr);
service = new InstalldNativeService();
- testUuid = std::make_unique<std::string>();
- *testUuid = std::string(kTestUuid);
+ testUuid = kTestUuid;
system("mkdir -p /data/local/tmp/user/0");
init_globals_from_data_and_root();
@@ -322,7 +321,7 @@
// Request a snapshot of the CE content but not the DE content.
int64_t ce_snapshot_inode;
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode));
struct stat buf;
memset(&buf, 0, sizeof(buf));
@@ -344,7 +343,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of the DE content but not the CE content.
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode));
// Only DE content snapshot was requested.
ASSERT_EQ(ce_snapshot_inode, 0);
@@ -365,7 +364,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of both the CE as well as the DE content.
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
ASSERT_TRUE(android::base::ReadFileToString(
@@ -407,10 +406,10 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request snapshot for the package com.foo.
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Now request snapshot with the same id for the package com.bar
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Check that both snapshots have correct data in them.
@@ -439,9 +438,9 @@
ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true));
int64_t ce_snapshot_inode;
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode));
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 73, FLAG_STORAGE_DE, nullptr));
// No CE content snapshot was performed.
ASSERT_EQ(ce_snapshot_inode, 0);
@@ -476,7 +475,7 @@
"TEST_CONTENT_2_DE", fake_package_de_path + "/file2",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Previous snapshot (with data for file1) must be cleared.
@@ -497,7 +496,7 @@
ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
- EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
+ EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_optional<std::string>("FOO"),
"com.foo", 0, 17, FLAG_STORAGE_DE, nullptr));
}
@@ -524,7 +523,7 @@
ASSERT_TRUE(android::base::WriteStringToFile(
"TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
// The snapshot call must clear cache.
struct stat sb;
@@ -558,7 +557,7 @@
"TEST_CONTENT_DE", fake_package_de_path + "/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
std::string ce_content, de_content;
@@ -584,7 +583,7 @@
int64_t ce_snapshot_inode;
// Request a snapshot of both the CE as well as the DE content.
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
// Because CE data snapshot was requested, ce_snapshot_inode can't be null.
ASSERT_NE(0, ce_snapshot_inode);
@@ -594,7 +593,7 @@
ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
- ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
// Check snapshot is deleted.
ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
@@ -615,7 +614,7 @@
"DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
// Check snapshot is deleted.
@@ -624,7 +623,7 @@
ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
// Check that deleting already deleted snapshot is no-op.
- ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
}
@@ -637,7 +636,7 @@
ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
- ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
+ ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_optional<std::string>("BAR"),
"com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk());
}
@@ -669,7 +668,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
ASSERT_TRUE(service->destroyCeSnapshotsNotSpecified(
- std::make_unique<std::string>("TEST"), 0, { 1543, 77 }).isOk());
+ std::make_optional<std::string>("TEST"), 0, { 1543, 77 }).isOk());
// Check only snapshots not specified are deleted.
struct stat sb;
@@ -690,7 +689,7 @@
ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
- EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
+ EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_optional<std::string>("BAR"),
"com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
}
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 5afae4b..987adaf 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -75,7 +75,7 @@
defaults: ["lshal_defaults"],
gtest: true,
static_libs: [
- "android.hardware.tests.baz@1.0",
+ "android.hardware.tests.inheritance@1.0",
"libgmock",
],
shared_libs: [
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
index af22ac9..72958bd 100644
--- a/cmds/lshal/DebugCommand.cpp
+++ b/cmds/lshal/DebugCommand.cpp
@@ -39,7 +39,7 @@
// Optargs cannnot be used because the flag should not be considered set
// if it should really be contained in mOptions.
if (std::string(arg.argv[optind]) == "-E") {
- mExcludesParentInstances = true;
+ mParentDebugInfoLevel = ParentDebugInfoLevel::NOTHING;
optind++;
}
@@ -67,7 +67,7 @@
return mLshal.emitDebugInfo(
pair.first, pair.second.empty() ? "default" : pair.second, mOptions,
- mExcludesParentInstances,
+ mParentDebugInfoLevel,
mLshal.out().buf(),
mLshal.err());
}
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
index cd57e31..317cc28 100644
--- a/cmds/lshal/DebugCommand.h
+++ b/cmds/lshal/DebugCommand.h
@@ -21,6 +21,7 @@
#include <android-base/macros.h>
#include "Command.h"
+#include "ParentDebugInfoLevel.h"
#include "utils.h"
namespace android {
@@ -42,9 +43,8 @@
std::string mInterfaceName;
std::vector<std::string> mOptions;
- // Outputs the actual descriptor of a hal instead of the debug output
- // if the arguments provided are a superclass of the actual hal impl.
- bool mExcludesParentInstances;
+ // See comment on ParentDebugInfoLevel.
+ ParentDebugInfoLevel mParentDebugInfoLevel = ParentDebugInfoLevel::FULL;
DISALLOW_COPY_AND_ASSIGN(DebugCommand);
};
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index fb11cee..a805a48 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -555,7 +555,7 @@
std::stringstream ss;
auto pair = splitFirst(iName, '/');
mLshal.emitDebugInfo(pair.first, pair.second, {},
- false /* excludesParentInstances */, ss,
+ ParentDebugInfoLevel::FQNAME_ONLY, ss,
NullableOStream<std::ostream>(nullptr));
return ss.str();
};
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 132b31e..2c3efe5 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -101,7 +101,7 @@
const std::string &interfaceName,
const std::string &instanceName,
const std::vector<std::string> &options,
- bool excludesParentInstances,
+ ParentDebugInfoLevel parentDebugInfoLevel,
std::ostream &out,
NullableOStream<std::ostream> err) const {
using android::hidl::base::V1_0::IBase;
@@ -126,7 +126,7 @@
return NO_INTERFACE;
}
- if (excludesParentInstances) {
+ if (parentDebugInfoLevel != ParentDebugInfoLevel::FULL) {
const std::string descriptor = getDescriptor(base.get());
if (descriptor.empty()) {
std::string msg = interfaceName + "/" + instanceName + " getDescriptor failed";
@@ -134,6 +134,9 @@
LOG(ERROR) << msg;
}
if (descriptor != interfaceName) {
+ if (parentDebugInfoLevel == ParentDebugInfoLevel::FQNAME_ONLY) {
+ out << "[See " << descriptor << "/" << instanceName << "]";
+ }
return OK;
}
}
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 830bd87..50279d4 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -25,6 +25,7 @@
#include "Command.h"
#include "NullableOStream.h"
+#include "ParentDebugInfoLevel.h"
#include "utils.h"
namespace android {
@@ -49,7 +50,7 @@
const std::string &interfaceName,
const std::string &instanceName,
const std::vector<std::string> &options,
- bool excludesParentInstances,
+ ParentDebugInfoLevel parentDebugInfoLevel,
std::ostream &out,
NullableOStream<std::ostream> err) const;
diff --git a/cmds/lshal/ParentDebugInfoLevel.h b/cmds/lshal/ParentDebugInfoLevel.h
new file mode 100644
index 0000000..12ac9c8
--- /dev/null
+++ b/cmds/lshal/ParentDebugInfoLevel.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace android {
+namespace lshal {
+
+// Describe verbosity when dumping debug information on a HAL service by
+// referring to a parent HAL interface FQName (for example, when dumping debug information
+// on foo@1.0::IFoo but the HAL implementation is foo@1.1::IFoo).
+enum class ParentDebugInfoLevel {
+ // Write nothing.
+ NOTHING,
+ // Write a short description that includes the FQName of the real implementation.
+ FQNAME_ONLY,
+ // Write full debug info.
+ FULL,
+};
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 3d550ba..afe5d63 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -24,7 +24,7 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
-#include <android/hardware/tests/baz/1.0/IQuux.h>
+#include <android/hardware/tests/inheritance/1.0/IChild.h>
#include <hidl/HidlTransportSupport.h>
#include <vintf/parse_xml.h>
@@ -44,6 +44,7 @@
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
using android::vintf::Arch;
using android::vintf::CompatibilityMatrix;
using android::vintf::gCompatibilityMatrixConverter;
@@ -59,10 +60,14 @@
namespace android {
namespace hardware {
namespace tests {
-namespace baz {
+namespace inheritance {
namespace V1_0 {
namespace implementation {
-struct Quux : android::hardware::tests::baz::V1_0::IQuux {
+struct Child : android::hardware::tests::inheritance::V1_0::IChild {
+ ::android::hardware::Return<void> doChild() override { return Void(); }
+ ::android::hardware::Return<void> doParent() override { return Void(); }
+ ::android::hardware::Return<void> doGrandparent() override { return Void(); }
+
::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override {
const native_handle_t *handle = hh.getNativeHandle();
if (handle->numFds < 1) {
@@ -76,7 +81,7 @@
}
ssize_t written = write(fd, content.c_str(), content.size());
if (written != (ssize_t)content.size()) {
- LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < "
+ LOG(WARNING) << "SERVER(Child) debug writes " << written << " bytes < "
<< content.size() << " bytes, errno = " << errno;
}
return Void();
@@ -85,7 +90,7 @@
} // namespace implementation
} // namespace V1_0
-} // namespace baz
+} // namespace inheritance
} // namespace tests
} // namespace hardware
@@ -124,18 +129,24 @@
class DebugTest : public ::testing::Test {
public:
void SetUp() override {
- using ::android::hardware::tests::baz::V1_0::IQuux;
- using ::android::hardware::tests::baz::V1_0::implementation::Quux;
+ using ::android::hardware::tests::inheritance::V1_0::IChild;
+ using ::android::hardware::tests::inheritance::V1_0::IParent;
+ using ::android::hardware::tests::inheritance::V1_0::IGrandparent;
+ using ::android::hardware::tests::inheritance::V1_0::implementation::Child;
err.str("");
out.str("");
serviceManager = new testing::NiceMock<MockServiceManager>();
- ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
- [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> {
- if (iface == IQuux::descriptor && inst == "default")
- return new Quux();
- return nullptr;
- }));
+ ON_CALL(*serviceManager, get(_, _))
+ .WillByDefault(
+ Invoke([](const auto& iface,
+ const auto& inst) -> ::android::hardware::Return<sp<IBase>> {
+ if (inst != "default") return nullptr;
+ if (iface == IChild::descriptor || iface == IParent::descriptor ||
+ iface == IGrandparent::descriptor)
+ return new Child();
+ return nullptr;
+ }));
lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager);
}
@@ -159,17 +170,17 @@
TEST_F(DebugTest, Debug) {
EXPECT_EQ(0u, callMain(lshal, {
- "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
+ "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild/default", "foo", "bar"
}));
- EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
+ EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\nfoo\nbar"));
EXPECT_THAT(err.str(), IsEmpty());
}
TEST_F(DebugTest, Debug2) {
EXPECT_EQ(0u, callMain(lshal, {
- "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
+ "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild", "baz", "quux"
}));
- EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
+ EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\nbaz\nquux"));
EXPECT_THAT(err.str(), IsEmpty());
}
@@ -180,6 +191,22 @@
EXPECT_THAT(err.str(), HasSubstr("does not exist"));
}
+TEST_F(DebugTest, DebugParent) {
+ EXPECT_EQ(0u, callMain(lshal, {
+ "lshal", "debug", "android.hardware.tests.inheritance@1.0::IParent", "calling parent"
+ }));
+ EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\ncalling parent"));
+ EXPECT_THAT(err.str(), IsEmpty());
+}
+
+TEST_F(DebugTest, DebugParentExclude) {
+ EXPECT_EQ(0u, callMain(lshal, {
+ "lshal", "debug", "-E", "android.hardware.tests.inheritance@1.0::IParent", "excluding"
+ }));
+ EXPECT_THAT(out.str(), IsEmpty());
+ EXPECT_THAT(err.str(), IsEmpty());
+}
+
class MockLshal : public Lshal {
public:
MockLshal() {}
@@ -766,6 +793,91 @@
EXPECT_EQ("", err.str());
}
+// Fake service returned by mocked IServiceManager::get for DumpDebug.
+// The interfaceChain and getHashChain functions returns
+// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.
+class InheritingService : public IBase {
+public:
+ explicit InheritingService(pid_t id) : mId(id) {}
+ android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
+ cb(getInterfaceName(mId));
+ return hardware::Void();
+ }
+ android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+ std::vector<hidl_string> ret;
+ for (auto i = mId; i > 0; --i) {
+ ret.push_back(getInterfaceName(i));
+ }
+ ret.push_back(IBase::descriptor);
+ cb(ret);
+ return hardware::Void();
+ }
+ android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+ std::vector<hidl_hash> ret;
+ for (auto i = mId; i > 0; --i) {
+ ret.push_back(getHashFromId(i));
+ }
+ ret.push_back(getHashFromId(0xff));
+ cb(ret);
+ return hardware::Void();
+ }
+ android::hardware::Return<void> debug(const hidl_handle& hh,
+ const hidl_vec<hidl_string>&) override {
+ const native_handle_t* handle = hh.getNativeHandle();
+ if (handle->numFds < 1) {
+ return Void();
+ }
+ int fd = handle->data[0];
+ std::string content = "debug info for ";
+ content += getInterfaceName(mId);
+ ssize_t written = write(fd, content.c_str(), content.size());
+ if (written != (ssize_t)content.size()) {
+ LOG(WARNING) << "SERVER(" << descriptor << ") debug writes " << written << " bytes < "
+ << content.size() << " bytes, errno = " << errno;
+ }
+ return Void();
+ }
+
+private:
+ pid_t mId;
+};
+
+TEST_F(ListTest, DumpDebug) {
+ size_t inheritanceLevel = 3;
+ sp<IBase> service = new InheritingService(inheritanceLevel);
+
+ EXPECT_CALL(*serviceManager, list(_)).WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
+ std::vector<hidl_string> ret;
+ for (auto i = 1; i <= inheritanceLevel; ++i) {
+ ret.push_back(getInterfaceName(i) + "/default");
+ }
+ cb(ret);
+ return hardware::Void();
+ }));
+ EXPECT_CALL(*serviceManager, get(_, _))
+ .WillRepeatedly(
+ Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
+ int id = getIdFromInstanceName(instance);
+ if (id > inheritanceLevel) return nullptr;
+ return sp<IBase>(service);
+ }));
+
+ const std::string expected = "[fake description 0]\n"
+ "Interface\n"
+ "a.h.foo1@1.0::IFoo/default\n"
+ "[See a.h.foo3@3.0::IFoo/default]\n"
+ "a.h.foo2@2.0::IFoo/default\n"
+ "[See a.h.foo3@3.0::IFoo/default]\n"
+ "a.h.foo3@3.0::IFoo/default\n"
+ "debug info for a.h.foo3@3.0::IFoo\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=b", "-id"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
class ListVintfTest : public ListTest {
public:
virtual void SetUp() override {
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 152ac28..6d5070f 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -3,16 +3,11 @@
user system
group system readproc
critical
- onrestart restart healthd
- onrestart restart zygote
+ onrestart restart apexd
onrestart restart audioserver
- onrestart restart media
- onrestart restart surfaceflinger
- onrestart restart inputflinger
- onrestart restart drm
- onrestart restart cameraserver
- onrestart restart keystore
onrestart restart gatekeeperd
- onrestart restart thermalservice
+ onrestart class_restart main
+ onrestart class_restart hal
+ onrestart class_restart early_hal
writepid /dev/cpuset/system-background/tasks
shutdown critical
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
index 3fa4d7d..756f6c3 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -3,4 +3,7 @@
user system
group system readproc
writepid /dev/cpuset/system-background/tasks
+ onrestart class_restart main
+ onrestart class_restart hal
+ onrestart class_restart early_hal
shutdown critical
diff --git a/data/etc/android.hardware.camera.concurrent.xml b/data/etc/android.hardware.camera.concurrent.xml
new file mode 100644
index 0000000..2cbb263
--- /dev/null
+++ b/data/etc/android.hardware.camera.concurrent.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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 set of features required for a camera2 device that supports concurrent operation
+ of front and back cameras -->
+<permissions>
+ <feature name="android.hardware.camera.front" />
+ <feature name="android.hardware.camera.concurrent" />
+</permissions>
diff --git a/data/etc/android.hardware.sensor.hinge_angle.xml b/data/etc/android.hardware.sensor.hinge_angle.xml
new file mode 100644
index 0000000..d744305
--- /dev/null
+++ b/data/etc/android.hardware.sensor.hinge_angle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<!-- Feature for devices with a hinge angle sensor. -->
+<permissions>
+ <feature name="android.hardware.sensor.hinge_angle" />
+</permissions>
\ No newline at end of file
diff --git a/data/etc/android.software.controls.xml b/data/etc/android.software.controls.xml
new file mode 100644
index 0000000..2cba34b
--- /dev/null
+++ b/data/etc/android.software.controls.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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 device controls support on the device,
+ as specified in the CDD. ONLY devices that meet the CDD's requirements may
+ declare this feature. -->
+<permissions>
+ <feature name="android.software.controls" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 619d017..dc6963f 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -59,6 +59,9 @@
<!-- Feature to specify if the device support managed users. -->
<feature name="android.software.managed_users" notLowRam="true"/>
+ <!-- Feature to specify if the device supports controls. -->
+ <feature name="android.software.controls" />
+
<!-- Devices with all optimizations required to support VR Mode and
pass all CDD requirements for this feature may include
android.hardware.vr.high_performance -->
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 52524ca..e878f86 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -59,6 +59,9 @@
<!-- Feature to specify if the device support managed users. -->
<feature name="android.software.managed_users" />
+ <!-- Feature to specify if the device supports controls. -->
+ <feature name="android.software.controls" />
+
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with a rear-facing camera must include one of these as appropriate:
android.hardware.camera.xml or
diff --git a/docs/Doxyfile b/docs/Doxyfile
index efa639d..a1bd960 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1621,7 +1621,23 @@
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED = __attribute__(x)=
+PREDEFINED = \
+ "__ANDROID_API__=10000" \
+ "__BEGIN_DECLS=" \
+ "__END_DECLS=" \
+ "__INTRODUCED_IN(x)=" \
+ "__INTRODUCED_IN_32(x)=" \
+ "__INTRODUCED_IN_64(x)=" \
+ "__RENAME(x)=" \
+ "__RENAME_LDBL(x,y,z)=" \
+ "__printflike(x,y)=" \
+ "__attribute__(x)=" \
+ "__wur=" \
+ "__mallocfunc=" \
+ "__attribute_pure__=" \
+ "__attribute__(x)=" \
+ __ANDROID__ \
+ __BIONIC__ \
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
diff --git a/headers/Android.bp b/headers/Android.bp
index 82bc8a1..8f41c2b 100644
--- a/headers/Android.bp
+++ b/headers/Android.bp
@@ -17,4 +17,12 @@
"libutils_headers",
"libstagefright_foundation_headers",
],
+ min_sdk_version: "29",
+
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index dc37bbd..e65b224 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -321,6 +321,46 @@
OMX_VIDEO_DolbyVisionLevelmax = 0x7FFFFFFF
} OMX_VIDEO_DOLBYVISIONLEVELTYPE;
+/** AV1 Profile enum type */
+typedef enum OMX_VIDEO_AV1PROFILETYPE {
+ OMX_VIDEO_AV1ProfileMain8 = 0x00000001,
+ OMX_VIDEO_AV1ProfileMain10 = 0x00000002,
+ OMX_VIDEO_AV1ProfileMain10HDR10 = 0x00001000,
+ OMX_VIDEO_AV1ProfileMain10HDR10Plus = 0x00002000,
+ OMX_VIDEO_AV1ProfileUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_AV1ProfileMax = 0x7FFFFFFF
+} OMX_VIDEO_AV1PROFILETYPE;
+
+/** AV1 Level enum type */
+typedef enum OMX_VIDEO_AV1LEVELTYPE {
+ OMX_VIDEO_AV1Level2 = 0x1,
+ OMX_VIDEO_AV1Level21 = 0x2,
+ OMX_VIDEO_AV1Level22 = 0x4,
+ OMX_VIDEO_AV1Level23 = 0x8,
+ OMX_VIDEO_AV1Level3 = 0x10,
+ OMX_VIDEO_AV1Level31 = 0x20,
+ OMX_VIDEO_AV1Level32 = 0x40,
+ OMX_VIDEO_AV1Level33 = 0x80,
+ OMX_VIDEO_AV1Level4 = 0x100,
+ OMX_VIDEO_AV1Level41 = 0x200,
+ OMX_VIDEO_AV1Level42 = 0x400,
+ OMX_VIDEO_AV1Level43 = 0x800,
+ OMX_VIDEO_AV1Level5 = 0x1000,
+ OMX_VIDEO_AV1Level51 = 0x2000,
+ OMX_VIDEO_AV1Level52 = 0x4000,
+ OMX_VIDEO_AV1Level53 = 0x8000,
+ OMX_VIDEO_AV1Level6 = 0x10000,
+ OMX_VIDEO_AV1Level61 = 0x20000,
+ OMX_VIDEO_AV1Level62 = 0x40000,
+ OMX_VIDEO_AV1Level63 = 0x80000,
+ OMX_VIDEO_AV1Level7 = 0x100000,
+ OMX_VIDEO_AV1Level71 = 0x200000,
+ OMX_VIDEO_AV1Level72 = 0x400000,
+ OMX_VIDEO_AV1Level73 = 0x800000,
+ OMX_VIDEO_AV1LevelUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_AV1LevelMax = 0x7FFFFFFF
+} OMX_VIDEO_AV1LEVELTYPE;
+
/**
* Structure for configuring video compression intra refresh period
*
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 2631b14..f195399 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -125,6 +125,8 @@
* Note that {@link ADataSpace} only exposes a few values. This may return
* {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no
* corresponding ADataSpace.
+ *
+ * Available since API level 30.
*/
int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30);
@@ -189,6 +191,8 @@
/**
* User-defined function for writing the output of compression.
*
+ * Available since API level 30.
+ *
* @param userContext Pointer to user-defined data passed to
* {@link AndroidBitmap_compress}.
* @param data Compressed data of |size| bytes to write.
@@ -202,6 +206,8 @@
/**
* Compress |pixels| as described by |info|.
*
+ * Available since API level 30.
+ *
* @param info Description of the pixels to compress.
* @param dataspace {@link ADataSpace} describing the color space of the
* pixels.
@@ -234,6 +240,8 @@
*
* Client must not modify it while a Bitmap is wrapping it.
*
+ * Available since API level 30.
+ *
* @param bitmap Handle to an android.graphics.Bitmap.
* @param outBuffer On success, is set to a pointer to the
* {@link AHardwareBuffer} associated with bitmap. This acquires
diff --git a/libs/nativedisplay/include/android/choreographer.h b/include/android/choreographer.h
similarity index 80%
rename from libs/nativedisplay/include/android/choreographer.h
rename to include/android/choreographer.h
index 5fd3de9..e9f559c 100644
--- a/libs/nativedisplay/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -75,14 +75,16 @@
* Deprecated: Use AChoreographer_postFrameCallback64 instead.
*/
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
+ AChoreographer_frameCallback callback, void* data)
+ __INTRODUCED_IN(24) __DEPRECATED_IN(29);
/**
* Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
*/
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data,
- long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis) __INTRODUCED_IN(24)
+ __DEPRECATED_IN(29);
#endif /* __ANDROID_API__ >= 24 */
@@ -95,7 +97,8 @@
* Available since API level 29.
*/
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
+ AChoreographer_frameCallback64 callback, void* data)
+ __INTRODUCED_IN(29);
/**
* Post a callback to be run on the frame following the specified delay. The
@@ -105,7 +108,8 @@
* Available since API level 29.
*/
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
+ AChoreographer_frameCallback64 callback, void* data,
+ uint32_t delayMillis) __INTRODUCED_IN(29);
#endif /* __ANDROID_API__ >= 29 */
@@ -125,9 +129,19 @@
*
* This api is thread-safe. Any thread is allowed to register a new refresh
* rate callback for the choreographer instance.
+ *
+ * Note that in API level 30, this api is not guaranteed to be atomic with
+ * DisplayManager. That is, calling Display#getRefreshRate very soon after
+ * a refresh rate callback is invoked may return a stale refresh rate. If any
+ * Display properties would be required by this callback, then it is recommended
+ * to listen directly to DisplayManager.DisplayListener#onDisplayChanged events
+ * instead.
+ *
+ * Available since API level 30.
*/
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback, void* data);
+ AChoreographer_refreshRateCallback, void* data)
+ __INTRODUCED_IN(30);
/**
* Unregisters a callback to be run when the display refresh rate changes, along
@@ -140,9 +154,12 @@
* callback and associated data pointer are unregistered, then there is a
* guarantee that when the unregistration completes that that callback will not
* be run with the data pointer passed.
+ *
+ * Available since API level 30.
*/
void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback, void* data);
+ AChoreographer_refreshRateCallback, void* data)
+ __INTRODUCED_IN(30);
#endif /* __ANDROID_API__ >= 30 */
__END_DECLS
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 05f4340..ccf3e59 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,13 +58,13 @@
ACONFIGURATION_ORIENTATION_ANY = 0x0000,
/**
* Orientation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+ * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
* resource qualifier.
*/
ACONFIGURATION_ORIENTATION_PORT = 0x0001,
/**
* Orientation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+ * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
* resource qualifier.
*/
ACONFIGURATION_ORIENTATION_LAND = 0x0002,
@@ -75,7 +75,7 @@
ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000,
/**
* Touchscreen: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+ * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
* resource qualifier.
*/
ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001,
@@ -83,7 +83,7 @@
ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002,
/**
* Touchscreen: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+ * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
* resource qualifier.
*/
ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003,
@@ -92,43 +92,43 @@
ACONFIGURATION_DENSITY_DEFAULT = 0,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_LOW = 120,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_MEDIUM = 160,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_TV = 213,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_HIGH = 240,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_XHIGH = 320,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_XXHIGH = 480,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_XXXHIGH = 640,
@@ -141,19 +141,19 @@
ACONFIGURATION_KEYBOARD_ANY = 0x0000,
/**
* Keyboard: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
* resource qualifier.
*/
ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001,
/**
* Keyboard: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
* resource qualifier.
*/
ACONFIGURATION_KEYBOARD_QWERTY = 0x0002,
/**
* Keyboard: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
* resource qualifier.
*/
ACONFIGURATION_KEYBOARD_12KEY = 0x0003,
@@ -162,25 +162,25 @@
ACONFIGURATION_NAVIGATION_ANY = 0x0000,
/**
* Navigation: value corresponding to the
- * <a href="@@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+ * <a href="@/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_NONAV = 0x0001,
/**
* Navigation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_DPAD = 0x0002,
/**
* Navigation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003,
/**
* Navigation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_WHEEL = 0x0004,
@@ -189,19 +189,19 @@
ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
/**
* Keyboard availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
* resource qualifier.
*/
ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
/**
* Keyboard availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
* resource qualifier.
*/
ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
/**
* Keyboard availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
* resource qualifier.
*/
ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
@@ -210,13 +210,13 @@
ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
/**
* Navigation availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
* resource qualifier.
*/
ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
/**
* Navigation availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
* resource qualifier.
*/
ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
@@ -226,28 +226,28 @@
/**
* Screen size: value indicating the screen is at least
* approximately 320x426 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
/**
* Screen size: value indicating the screen is at least
* approximately 320x470 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
/**
* Screen size: value indicating the screen is at least
* approximately 480x640 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
/**
* Screen size: value indicating the screen is at least
* approximately 720x960 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
@@ -256,13 +256,13 @@
ACONFIGURATION_SCREENLONG_ANY = 0x00,
/**
* Screen layout: value that corresponds to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENLONG_NO = 0x1,
/**
* Screen layout: value that corresponds to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENLONG_YES = 0x2,
@@ -275,13 +275,13 @@
ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
/**
* Wide color gamut: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+ * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
* nowidecg</a> resource qualifier specified.
*/
ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
/**
* Wide color gamut: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+ * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
* widecg</a> resource qualifier specified.
*/
ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
@@ -290,13 +290,13 @@
ACONFIGURATION_HDR_ANY = 0x00,
/**
* HDR: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+ * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
* lowdr</a> resource qualifier specified.
*/
ACONFIGURATION_HDR_NO = 0x1,
/**
* HDR: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+ * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
* highdr</a> resource qualifier specified.
*/
ACONFIGURATION_HDR_YES = 0x2,
@@ -305,38 +305,38 @@
ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">no
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">no
* UI mode type</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
@@ -344,12 +344,12 @@
ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
/**
* UI night mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
/**
* UI night mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2,
@@ -366,78 +366,78 @@
ACONFIGURATION_LAYOUTDIR_ANY = 0x00,
/**
* Layout direction: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
*/
ACONFIGURATION_LAYOUTDIR_LTR = 0x01,
/**
* Layout direction: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
*/
ACONFIGURATION_LAYOUTDIR_RTL = 0x02,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+ * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
* configuration.
*/
ACONFIGURATION_MCC = 0x0001,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+ * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
* configuration.
*/
ACONFIGURATION_MNC = 0x0002,
/**
* Bit mask for
- * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+ * <a href="/guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
* configuration.
*/
ACONFIGURATION_LOCALE = 0x0004,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
+ * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
* configuration.
*/
ACONFIGURATION_TOUCHSCREEN = 0x0008,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
* configuration.
*/
ACONFIGURATION_KEYBOARD = 0x0010,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
* configuration.
*/
ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
* configuration.
*/
ACONFIGURATION_NAVIGATION = 0x0040,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
+ * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
* configuration.
*/
ACONFIGURATION_ORIENTATION = 0x0080,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
* configuration.
*/
ACONFIGURATION_DENSITY = 0x0100,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
* configuration.
*/
ACONFIGURATION_SCREEN_SIZE = 0x0200,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
+ * <a href="/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
* configuration.
*/
ACONFIGURATION_VERSION = 0x0400,
@@ -447,27 +447,27 @@
ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
* configuration.
*/
ACONFIGURATION_UI_MODE = 0x1000,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
+ * <a href="/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
* configuration.
*/
ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
+ * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
* configuration.
*/
ACONFIGURATION_LAYOUTDIR = 0x4000,
ACONFIGURATION_SCREEN_ROUND = 0x8000,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
- * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+ * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+ * and <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
*/
ACONFIGURATION_COLOR_MODE = 0x10000,
/**
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
index 293e5ac..ae208a6 100644
--- a/include/android/hardware_buffer_jni.h
+++ b/include/android/hardware_buffer_jni.h
@@ -39,9 +39,9 @@
* Return the AHardwareBuffer wrapped by a Java HardwareBuffer object.
*
* This method does not acquire any additional reference to the AHardwareBuffer
- * that is returned. To keep the AHardwareBuffer live after the Java
- * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire()
- * to acquire an additional reference.
+ * that is returned. To keep the AHardwareBuffer alive after the Java
+ * HardwareBuffer object is closed, explicitly or by the garbage collector, be
+ * sure to use AHardwareBuffer_acquire() to acquire an additional reference.
*
* Available since API level 26.
*/
@@ -50,7 +50,18 @@
/**
* Return a new Java HardwareBuffer object that wraps the passed native
- * AHardwareBuffer object.
+ * AHardwareBuffer object. The Java HardwareBuffer will acquire a reference to
+ * the internal buffer and manage its lifetime. For example:
+ *
+ * <pre><code>
+ * AHardwareBuffer* buffer;
+ * AHardwareBuffer_allocate(..., &buffer); // `buffer` has reference count 1
+ * jobject java_result = AHardwareBuffer_toHardwareBuffer(buffer); // `buffer` has reference count 2.
+ * AHardwareBuffer_release(buffer); // `buffer` has reference count 1
+ * return result; // The underlying buffer is kept alive by `java_result` and
+ * // will be set to 0 when it is closed on the Java side with
+ * // HardwareBuffer::close().
+ * </code></pre>
*
* Available since API level 26.
*/
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 3a87da0..d7e6e41 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -133,6 +133,8 @@
/**
* Create a new {@link AImageDecoder} from an {@link AAsset}.
*
+ * Available since API level 30.
+ *
* @param asset {@link AAsset} containing encoded image data. Client is still
* responsible for calling {@link AAsset_close} on it, which may be
* done after deleting the returned {@link AImageDecoder}.
@@ -162,6 +164,8 @@
/**
* Create a new {@link AImageDecoder} from a file descriptor.
*
+ * Available since API level 30.
+ *
* @param fd Seekable, readable, open file descriptor for encoded data.
* Client is still responsible for closing it, which may be done
* after deleting the returned {@link AImageDecoder}.
@@ -190,6 +194,8 @@
/**
* Create a new AImageDecoder from a buffer.
*
+ * Available since API level 30.
+ *
* @param buffer Pointer to encoded data. Must be valid for the entire time
* the {@link AImageDecoder} is used.
* @param length Byte length of buffer.
@@ -217,12 +223,16 @@
/**
* Delete the AImageDecoder.
+ *
+ * Available since API level 30.
*/
void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
/**
* Choose the desired output format.
*
+ * Available since API level 30.
+ *
* @param format {@link AndroidBitmapFormat} to use for the output.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure. On failure, the
@@ -247,6 +257,8 @@
* Pass true to this method to leave them unpremultiplied. This has no effect on an
* opaque image.
*
+ * Available since API level 30.
+ *
* @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure.
@@ -267,6 +279,8 @@
* Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
* an {@link ADataSpace}.
*
+ * Available since API level 30.
+ *
* @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
* specifies how to interpret the colors. By default,
* AImageDecoder will decode into the ADataSpace specified by
@@ -292,6 +306,8 @@
* specified by width and height, and the output image will be the size of the
* crop rect.
*
+ * Available since API level 30.
+ *
* @param width Width of the output (prior to cropping).
* This will affect future calls to
* {@link AImageDecoder_getMinimumStride}, which will now return
@@ -319,6 +335,8 @@
* others. This computes the most efficient target size to use to reach a
* particular sampleSize.
*
+ * Available since API level 30.
+ *
* @param sampleSize A subsampling rate of the original image. Must be greater
* than or equal to 1. A sampleSize of 2 means to skip every
* other pixel/line, resulting in a width and height that are
@@ -344,6 +362,8 @@
* the specified {@link ARect}. Clients will only need to allocate enough memory
* for the cropped ARect.
*
+ * Available since API level 30.
+ *
* @param crop Rectangle describing a crop of the decode. It must be contained inside of
* the (possibly scaled, by {@link AImageDecoder_setTargetSize})
* image dimensions. This will affect future calls to
@@ -376,6 +396,8 @@
*
* This is owned by the {@link AImageDecoder} and will be destroyed when the
* AImageDecoder is destroyed via {@link AImageDecoder_delete}.
+ *
+ * Available since API level 30.
*/
const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
const AImageDecoder*) __INTRODUCED_IN(30);
@@ -385,6 +407,8 @@
* pixel width of the output, unless {@link AImageDecoder_setTargetSize} is
* used to choose a different size or {@link AImageDecoder_setCrop} is used to
* set a crop rect.
+ *
+ * Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -393,12 +417,16 @@
* pixel height of the output, unless {@link AImageDecoder_setTargetSize} is
* used to choose a different size or {@link AImageDecoder_setCrop} is used to
* set a crop rect.
+ *
+ * Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
* Report the mimeType of the encoded image.
*
+ * Available since API level 30.
+ *
* @return a string literal describing the mime type.
*/
const char* AImageDecoderHeaderInfo_getMimeType(
@@ -409,6 +437,8 @@
* by default. {@link AImageDecoder} will try to choose one that is sensible
* for the image and the system. Note that this does not indicate the
* encoded format of the image.
+ *
+ * Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -419,6 +449,8 @@
* {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
* this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because
* {@link AImageDecoder_decodeImage} will premultiply pixels by default.
+ *
+ * Available since API level 30.
*/
int AImageDecoderHeaderInfo_getAlphaFlags(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -429,6 +461,8 @@
* By default, {@link AImageDecoder_decodeImage} will not do any color
* conversion.
*
+ * Available since API level 30.
+ *
* @return The {@link ADataSpace} representing the way the colors
* are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a
* corresponding ADataSpace). This specifies how to interpret the colors
@@ -452,12 +486,16 @@
*
* If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
* cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
+ *
+ * Available since API level 30.
*/
size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
/**
* Decode the image into pixels, using the settings of the {@link AImageDecoder}.
*
+ * Available since API level 30.
+ *
* @param decoder Opaque object representing the decoder.
* @param pixels On success, will be filled with the result
* of the decode. Must be large enough to hold |size| bytes.
diff --git a/include/android/input.h b/include/android/input.h
index dbfd61e..7c39234 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -55,6 +55,7 @@
#include <sys/types.h>
#include <android/keycodes.h>
#include <android/looper.h>
+#include <jni.h>
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
@@ -931,6 +932,15 @@
/** Get the input event source. */
int32_t AInputEvent_getSource(const AInputEvent* event);
+/**
+ * Releases interface objects created by {@link AKeyEvent_fromJava()}
+ * and {@link AMotionEvent_fromJava()}.
+ * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
+ * The underlying Java object remains valid and does not change its state.
+ */
+
+void AInputEvent_release(const AInputEvent* event);
+
/*** Accessors for key events only. ***/
/** Get the key event action. */
@@ -977,6 +987,15 @@
*/
int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
+/**
+ * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent.
+ * The result may be used with generic and KeyEvent-specific AInputEvent_* functions.
+ * The object returned by this function must be disposed using {@link AInputEvent_release()}.
+ * User must guarantee that lifetime for object referenced by keyEvent is prolongated
+ * up to release of returned AInputEvent*.
+ */
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+
/*** Accessors for motion events only. ***/
/** Get the combined motion event action code and pointer index. */
@@ -1292,6 +1311,14 @@
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index, size_t history_index);
+/**
+ * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent.
+ * The result may be used with generic and MotionEvent-specific AInputEvent_* functions.
+ * The object returned by this function must be disposed using {@link AInputEvent_release()}.
+ * User must guarantee that object referenced by motionEvent won't be recycled and
+ * its lifetime is prolongated up to release of returned AInputEvent*.
+ */
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
struct AInputQueue;
/**
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 59b1deb..c6d1c94 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -126,8 +126,8 @@
ANDROID_RESOLV_NO_RETRY = 1 << 0,
/**
- * Do not cache the result of the lookup. The lookup may return a result that is already
- * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified.
+ * Don't lookup this request in the cache, and don't cache the result of the lookup.
+ * This flag implies {@link #ANDROID_RESOLV_NO_CACHE_LOOKUP}.
*/
ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1,
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 3247fa1..83582d6 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -60,8 +60,6 @@
extern "C" {
#endif
-#if __ANDROID_API__ >= 30
-
enum AThermalStatus {
/** Error in thermal status. */
ATHERMAL_STATUS_ERROR = -1,
@@ -111,36 +109,45 @@
*/
typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+#if __ANDROID_API__ >= 30
+
/**
* Acquire an instance of the thermal manager. This must be freed using
* {@link AThermal_releaseManager}.
*
+ * Available since API level 30.
+ *
* @return manager instance on success, nullptr on failure.
- */
-AThermalManager* AThermal_acquireManager();
+ */
+AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30);
/**
* Release the thermal manager pointer acquired via
* {@link AThermal_acquireManager}.
*
- * @param manager The manager to be released.
+ * Available since API level 30.
*
+ * @param manager The manager to be released.
*/
-void AThermal_releaseManager(AThermalManager *manager);
+void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30);
/**
* Gets the current thermal status.
*
+ * Available since API level 30.
+ *
* @param manager The manager instance to use to query the thermal status.
* Acquired via {@link AThermal_acquireManager}.
*
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
-*/
-AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager);
+ */
+AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30);
/**
* Register the thermal status listener for thermal status change.
*
+ * Available since API level 30.
+ *
* @param manager The manager instance to use to register.
* Acquired via {@link AThermal_acquireManager}.
* @param callback The callback function to be called when thermal status updated.
@@ -152,11 +159,13 @@
* EPIPE if communication with the system service has failed.
*/
int AThermal_registerThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data);
+ AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
/**
* Unregister the thermal status listener previously resgistered.
*
+ * Available since API level 30.
+ *
* @param manager The manager instance to use to unregister.
* Acquired via {@link AThermal_acquireManager}.
* @param callback The callback function to be called when thermal status updated.
@@ -168,8 +177,7 @@
* EPIPE if communication with the system service has failed.
*/
int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data);
-
+ AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
#endif // __ANDROID_API__ >= 30
diff --git a/include/android/trace.h b/include/android/trace.h
index d59690a..dbad6f6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -115,7 +115,7 @@
#endif /* __ANDROID_API__ >= 29 */
#ifdef __cplusplus
-};
+}
#endif
#endif // ANDROID_NATIVE_TRACE_H
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 6100626..334fe34 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,12 +17,12 @@
#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
#define _LIBINPUT_DISPLAY_VIEWPORT_H
-#include <cinttypes>
-#include <optional>
-
#include <android-base/stringprintf.h>
#include <input/Input.h>
+#include <cinttypes>
+#include <optional>
+
using android::base::StringPrintf;
namespace android {
@@ -39,22 +39,21 @@
* Keep in sync with values in InputManagerService.java.
*/
enum class ViewportType : int32_t {
- VIEWPORT_INTERNAL = 1,
- VIEWPORT_EXTERNAL = 2,
- VIEWPORT_VIRTUAL = 3,
+ INTERNAL = 1,
+ EXTERNAL = 2,
+ VIRTUAL = 3,
};
static const char* viewportTypeToString(ViewportType type) {
- switch(type) {
- case ViewportType::VIEWPORT_INTERNAL:
+ switch (type) {
+ case ViewportType::INTERNAL:
return "INTERNAL";
- case ViewportType::VIEWPORT_EXTERNAL:
+ case ViewportType::EXTERNAL:
return "EXTERNAL";
- case ViewportType::VIEWPORT_VIRTUAL:
+ case ViewportType::VIRTUAL:
return "VIRTUAL";
- default:
- return "UNKNOWN";
}
+ return "UNKNOWN";
}
/*
@@ -74,36 +73,40 @@
int32_t physicalBottom;
int32_t deviceWidth;
int32_t deviceHeight;
+ bool isActive;
std::string uniqueId;
// The actual (hardware) port that the associated display is connected to.
// Not all viewports will have this specified.
std::optional<uint8_t> physicalPort;
ViewportType type;
- DisplayViewport() :
- displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
- logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
- physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
- deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
- type(ViewportType::VIEWPORT_INTERNAL) {
- }
+ DisplayViewport()
+ : displayId(ADISPLAY_ID_NONE),
+ orientation(DISPLAY_ORIENTATION_0),
+ logicalLeft(0),
+ logicalTop(0),
+ logicalRight(0),
+ logicalBottom(0),
+ physicalLeft(0),
+ physicalTop(0),
+ physicalRight(0),
+ physicalBottom(0),
+ deviceWidth(0),
+ deviceHeight(0),
+ isActive(false),
+ uniqueId(),
+ physicalPort(std::nullopt),
+ type(ViewportType::INTERNAL) {}
bool operator==(const DisplayViewport& other) const {
- return displayId == other.displayId
- && orientation == other.orientation
- && logicalLeft == other.logicalLeft
- && logicalTop == other.logicalTop
- && logicalRight == other.logicalRight
- && logicalBottom == other.logicalBottom
- && physicalLeft == other.physicalLeft
- && physicalTop == other.physicalTop
- && physicalRight == other.physicalRight
- && physicalBottom == other.physicalBottom
- && deviceWidth == other.deviceWidth
- && deviceHeight == other.deviceHeight
- && uniqueId == other.uniqueId
- && physicalPort == other.physicalPort
- && type == other.type;
+ return displayId == other.displayId && orientation == other.orientation &&
+ logicalLeft == other.logicalLeft && logicalTop == other.logicalTop &&
+ logicalRight == other.logicalRight && logicalBottom == other.logicalBottom &&
+ physicalLeft == other.physicalLeft && physicalTop == other.physicalTop &&
+ physicalRight == other.physicalRight && physicalBottom == other.physicalBottom &&
+ deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight &&
+ isActive == other.isActive && uniqueId == other.uniqueId &&
+ physicalPort == other.physicalPort && type == other.type;
}
bool operator!=(const DisplayViewport& other) const {
@@ -127,25 +130,24 @@
physicalBottom = height;
deviceWidth = width;
deviceHeight = height;
+ isActive = false;
uniqueId.clear();
physicalPort = std::nullopt;
- type = ViewportType::VIEWPORT_INTERNAL;
+ type = ViewportType::INTERNAL;
}
std::string toString() const {
return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
- "logicalFrame=[%d, %d, %d, %d], "
- "physicalFrame=[%d, %d, %d, %d], "
- "deviceSize=[%d, %d]",
- viewportTypeToString(type), displayId,
- uniqueId.c_str(),
- physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
- orientation,
- logicalLeft, logicalTop,
- logicalRight, logicalBottom,
- physicalLeft, physicalTop,
- physicalRight, physicalBottom,
- deviceWidth, deviceHeight);
+ "logicalFrame=[%d, %d, %d, %d], "
+ "physicalFrame=[%d, %d, %d, %d], "
+ "deviceSize=[%d, %d], "
+ "isActive=[%d]",
+ viewportTypeToString(type), displayId, uniqueId.c_str(),
+ physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
+ : "<none>",
+ orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
+ physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
+ deviceHeight, isActive);
}
};
diff --git a/include/input/Flags.h b/include/input/Flags.h
new file mode 100644
index 0000000..4ad9056
--- /dev/null
+++ b/include/input/Flags.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2020 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 <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <type_traits>
+
+#include "utils/BitSet.h"
+
+#ifndef __UI_INPUT_FLAGS_H
+#define __UI_INPUT_FLAGS_H
+
+namespace android {
+
+namespace details {
+template <typename F, F V>
+constexpr std::optional<std::string_view> enum_value_name() {
+ // Should look something like (but all on one line):
+ // std::optional<std::string_view>
+ // android::details::enum_value_name()
+ // [F = android::test::TestFlags, V = android::test::TestFlags::ONE]
+ std::string_view view = __PRETTY_FUNCTION__;
+ size_t templateStart = view.rfind("[");
+ size_t templateEnd = view.rfind("]");
+ if (templateStart == std::string::npos || templateEnd == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Extract the template parameters without the enclosing braces.
+ // Example (cont'd): F = android::test::TestFlags, V = android::test::TestFlags::ONE
+ view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
+ size_t valStart = view.rfind("V = ");
+ if (valStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Example (cont'd): V = android::test::TestFlags::ONE
+ view = view.substr(valStart);
+ size_t nameStart = view.rfind("::");
+ if (nameStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Chop off the initial "::"
+ nameStart += 2;
+ return view.substr(nameStart);
+}
+
+template <typename F>
+inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
+
+template <typename F, typename T, T... I>
+constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
+ constexpr size_t count = seq.size();
+
+ std::array<F, count> values{};
+ for (size_t i = 0, v = 0; v < count; ++i) {
+ values[v++] = static_cast<F>(T{1} << i);
+ }
+
+ return values;
+}
+
+template <typename F>
+inline constexpr auto flag_values = generate_flag_values<F>(
+ std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
+
+template <typename F, std::size_t... I>
+constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
+ return std::array<std::optional<std::string_view>, sizeof...(I)>{
+ {enum_value_name<F, flag_values<F>[I]>()...}};
+}
+
+template <typename F>
+inline constexpr auto flag_names =
+ generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
+
+// A trait for determining whether a type is specifically an enum class or not.
+template <typename T, bool = std::is_enum_v<T>>
+struct is_enum_class : std::false_type {};
+
+// By definition, an enum class is an enum that is not implicitly convertible to its underlying
+// type.
+template <typename T>
+struct is_enum_class<T, true>
+ : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
+
+template <typename T>
+inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
+} // namespace details
+
+template <auto V>
+constexpr auto flag_name() {
+ using F = decltype(V);
+ return details::enum_value_name<F, V>();
+}
+
+template <typename F>
+constexpr std::optional<std::string_view> flag_name(F flag) {
+ using U = std::underlying_type_t<F>;
+ auto idx = __builtin_ctzl(static_cast<U>(flag));
+ return details::flag_names<F>[idx];
+}
+
+/* A class for handling flags defined by an enum or enum class in a type-safe way. */
+template <typename F>
+class Flags {
+ // F must be an enum or its underlying type is undefined. Theoretically we could specialize this
+ // further to avoid this restriction but in general we want to encourage the use of enums
+ // anyways.
+ static_assert(std::is_enum_v<F>, "Flags type must be an enum");
+ using U = typename std::underlying_type_t<F>;
+
+public:
+ constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
+ constexpr Flags() : mFlags(0) {}
+ constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
+
+ // Provide a non-explicit construct for non-enum classes since they easily convert to their
+ // underlying types (e.g. when used with bitwise operators). For enum classes, however, we
+ // should force them to be explicitly constructed from their underlying types to make full use
+ // of the type checker.
+ template <typename T = U>
+ constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
+ : mFlags(t) {}
+ template <typename T = U>
+ explicit constexpr Flags(T t,
+ typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+ : mFlags(t) {}
+
+ class Iterator {
+ // The type can't be larger than 64-bits otherwise it won't fit in BitSet64.
+ static_assert(sizeof(U) <= sizeof(uint64_t));
+
+ public:
+ Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; }
+ Iterator() : mRemainingFlags(0), mCurrFlag(static_cast<F>(0)) {}
+
+ // Pre-fix ++
+ Iterator& operator++() {
+ if (mRemainingFlags.isEmpty()) {
+ mCurrFlag = static_cast<F>(0);
+ } else {
+ uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left
+ const U flag = 1 << (64 - bit - 1);
+ mCurrFlag = static_cast<F>(flag);
+ }
+ return *this;
+ }
+
+ // Post-fix ++
+ Iterator operator++(int) {
+ Iterator iter = *this;
+ ++*this;
+ return iter;
+ }
+
+ bool operator==(Iterator other) const {
+ return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags;
+ }
+
+ bool operator!=(Iterator other) const { return !(*this == other); }
+
+ F operator*() { return mCurrFlag; }
+
+ // iterator traits
+
+ // In the future we could make this a bidirectional const iterator instead of a forward
+ // iterator but it doesn't seem worth the added complexity at this point. This could not,
+ // however, be made a non-const iterator as assigning one flag to another is a non-sensical
+ // operation.
+ using iterator_category = std::input_iterator_tag;
+ using value_type = F;
+ // Per the C++ spec, because input iterators are not assignable the iterator's reference
+ // type does not actually need to be a reference. In fact, making it a reference would imply
+ // that modifying it would change the underlying Flags object, which is obviously wrong for
+ // the same reason this can't be a non-const iterator.
+ using reference = F;
+ using difference_type = void;
+ using pointer = void;
+
+ private:
+ BitSet64 mRemainingFlags;
+ F mCurrFlag;
+ };
+
+ /*
+ * Tests whether the given flag is set.
+ */
+ bool test(F flag) const {
+ U f = static_cast<U>(flag);
+ return (f & mFlags) == f;
+ }
+
+ /* Tests whether any of the given flags are set */
+ bool any(Flags<F> f) { return (mFlags & f.mFlags) != 0; }
+
+ /* Tests whether all of the given flags are set */
+ bool all(Flags<F> f) { return (mFlags & f.mFlags) == f.mFlags; }
+
+ Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
+ Flags<F>& operator|=(Flags<F> rhs) {
+ mFlags = mFlags | rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator&(Flags<F> rhs) const { return static_cast<F>(mFlags & rhs.mFlags); }
+ Flags<F>& operator&=(Flags<F> rhs) {
+ mFlags = mFlags & rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator^(Flags<F> rhs) const { return static_cast<F>(mFlags ^ rhs.mFlags); }
+ Flags<F>& operator^=(Flags<F> rhs) {
+ mFlags = mFlags ^ rhs.mFlags;
+ return *this;
+ }
+
+ Flags<F> operator~() { return static_cast<F>(~mFlags); }
+
+ bool operator==(Flags<F> rhs) const { return mFlags == rhs.mFlags; }
+ bool operator!=(Flags<F> rhs) const { return !operator==(rhs); }
+
+ Flags<F>& operator=(const Flags<F>& rhs) {
+ mFlags = rhs.mFlags;
+ return *this;
+ }
+
+ Iterator begin() const { return Iterator(*this); }
+
+ Iterator end() const { return Iterator(); }
+
+ /*
+ * Returns the stored set of flags.
+ *
+ * Note that this returns the underlying type rather than the base enum class. This is because
+ * the value is no longer necessarily a strict member of the enum since the returned value could
+ * be multiple enum variants OR'd together.
+ */
+ U get() const { return mFlags; }
+
+ std::string string() const {
+ std::string result;
+ bool first = true;
+ U unstringified = 0;
+ for (const F f : *this) {
+ std::optional<std::string_view> flagString = flag_name(f);
+ if (flagString) {
+ appendFlag(result, flagString.value(), first);
+ } else {
+ unstringified |= static_cast<U>(f);
+ }
+ }
+
+ if (unstringified != 0) {
+ appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+ }
+
+ if (first) {
+ result += "0x0";
+ }
+
+ return result;
+ }
+
+private:
+ U mFlags;
+
+ static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
+ if (first) {
+ first = false;
+ } else {
+ str += " | ";
+ }
+ str += flag;
+ }
+};
+
+// This namespace provides operator overloads for enum classes to make it easier to work with them
+// as flags. In order to use these, add them via a `using namespace` declaration.
+namespace flag_operators {
+
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+inline Flags<F> operator~(F f) {
+ using U = typename std::underlying_type_t<F>;
+ return static_cast<F>(~static_cast<U>(f));
+}
+template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+Flags<F> operator|(F lhs, F rhs) {
+ using U = typename std::underlying_type_t<F>;
+ return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+}
+
+} // namespace flag_operators
+} // namespace android
+
+#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
deleted file mode 100644
index d23e3b7..0000000
--- a/include/input/IInputFlinger.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBINPUT_IINPUT_FLINGER_H
-#define _LIBINPUT_IINPUT_FLINGER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-
-#include <input/InputWindow.h>
-#include <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-/*
- * This class defines the Binder IPC interface for accessing various
- * InputFlinger features.
- */
-class IInputFlinger : public IInterface {
-public:
- DECLARE_META_INTERFACE(InputFlinger)
-
- virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
- virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
- virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
-};
-
-
-/**
- * Binder implementation.
- */
-class BnInputFlinger : public BnInterface<IInputFlinger> {
-public:
- enum {
- SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- REGISTER_INPUT_CHANNEL_TRANSACTION,
- UNREGISTER_INPUT_CHANNEL_TRANSACTION
- };
-
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif // _LIBINPUT_IINPUT_FLINGER_H
diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h
deleted file mode 100644
index 15d31b2..0000000
--- a/include/input/ISetInputWindowsListener.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class ISetInputWindowsListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(SetInputWindowsListener)
- virtual void onSetInputWindowsFinished() = 0;
-};
-
-class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> {
-public:
- enum SetInputWindowsTag : uint32_t {
- ON_SET_INPUT_WINDOWS_FINISHED
- };
-
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-}; // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h
index 9e47318..d40ba43 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -26,6 +26,7 @@
#include <android/input.h>
#include <math.h>
#include <stdint.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -86,6 +87,13 @@
constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+/**
+ * This flag indicates that the point up event has been canceled.
+ * Typically this is used for palm event when the user has accidental touches.
+ * TODO: Adjust flag to public api
+ */
+constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20;
+
enum {
/* Used when a motion event is not associated with any display.
* Typically used for non-pointer events. */
@@ -280,9 +288,9 @@
public:
// Used to divide integer space to ensure no conflict among these sources./
enum class Source : int32_t {
- INPUT_READER = 0x0 << SOURCE_SHIFT,
- INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT,
- OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events
+ INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT),
+ INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT),
+ OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events
};
IdGenerator(Source source);
@@ -294,7 +302,7 @@
private:
const Source mSource;
- static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT;
+ static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT);
};
/**
@@ -341,6 +349,8 @@
void scale(float globalScale, float windowXScale, float windowYScale);
void applyOffset(float xOffset, float yOffset);
+ void transform(const ui::Transform& transform);
+
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
}
@@ -462,6 +472,8 @@
nsecs_t eventTime);
void initialize(const KeyEvent& from);
+ static const char* actionToString(int32_t action);
+
protected:
int32_t mAction;
int32_t mFlags;
@@ -515,13 +527,11 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXScale() const { return mXScale; }
+ inline float getXOffset() const { return mTransform.tx(); }
- inline float getYScale() const { return mYScale; }
+ inline float getYOffset() const { return mTransform.ty(); }
- inline float getXOffset() const { return mXOffset; }
-
- inline float getYOffset() const { return mYOffset; }
+ inline ui::Transform getTransform() const { return mTransform; }
inline float getXPrecision() const { return mXPrecision; }
@@ -683,8 +693,8 @@
void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale, float xOffset,
- float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float rawXCursorPosition,
float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
@@ -701,7 +711,7 @@
// Apply 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
- void transform(const float matrix[9]);
+ void transform(const std::array<float, 9>& matrix);
#ifdef __ANDROID__
status_t readFromParcel(Parcel* parcel);
@@ -725,6 +735,8 @@
static const char* getLabel(int32_t axis);
static int32_t getAxisFromLabel(const char* label);
+ static const char* actionToString(int32_t action);
+
protected:
int32_t mAction;
int32_t mActionButton;
@@ -733,10 +745,7 @@
int32_t mMetaState;
int32_t mButtonState;
MotionClassification mClassification;
- float mXScale;
- float mYScale;
- float mXOffset;
- float mYOffset;
+ ui::Transform mTransform;
float mXPrecision;
float mYPrecision;
float mRawXCursorPosition;
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 7f04611..b6b9353 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -21,6 +21,7 @@
#include <binder/IBinder.h>
#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
#include <input/Input.h>
#include <utils/RefBase.h>
@@ -31,16 +32,18 @@
/*
* Describes the properties of an application that can receive input.
*/
-struct InputApplicationInfo {
+struct InputApplicationInfo : public Parcelable {
sp<IBinder> token;
std::string name;
- nsecs_t dispatchingTimeout;
+ std::chrono::nanoseconds dispatchingTimeout;
- status_t write(Parcel& output) const;
- static InputApplicationInfo read(const Parcel& from);
+ InputApplicationInfo() = default;
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+
+ status_t writeToParcel(android::Parcel* parcel) const override;
};
-
/*
* Handle for an application that can receive input.
*
@@ -57,8 +60,9 @@
return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
+ inline std::chrono::nanoseconds getDispatchingTimeout(
+ std::chrono::nanoseconds defaultValue) const {
+ return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
}
inline sp<IBinder> getApplicationToken() const {
@@ -75,6 +79,7 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
+
protected:
InputApplicationHandle();
virtual ~InputApplicationHandle();
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 20a17e3..c7685b7 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -144,10 +144,10 @@
};
/* Types of input device configuration files. */
-enum InputDeviceConfigurationFileType {
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
+enum class InputDeviceConfigurationFileType : int32_t {
+ CONFIGURATION = 0, /* .idc file */
+ KEY_LAYOUT = 1, /* .kl file */
+ KEY_CHARACTER_MAP = 2, /* .kcm file */
};
/*
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 7ca9031..8e00969 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -34,12 +34,14 @@
#include <android-base/chrono_utils.h>
#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
#include <input/Input.h>
+#include <sys/stat.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <utils/Vector.h>
#include <android-base/unique_fd.h>
@@ -69,10 +71,7 @@
struct Header {
Type type; // 4 bytes
- // We don't need this field in order to align the body below but we
- // leave it here because InputMessage::size() and other functions
- // compute the size of this structure as sizeof(Header) + sizeof(Body).
- uint32_t padding;
+ uint32_t seq;
} header;
// Body *must* be 8 byte aligned.
@@ -81,8 +80,8 @@
static_assert(sizeof(std::array<uint8_t, 32>) == 32);
union Body {
struct Key {
- uint32_t seq;
int32_t eventId;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -101,8 +100,8 @@
} key;
struct Motion {
- uint32_t seq;
int32_t eventId;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -117,10 +116,12 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
- float xScale;
- float yScale;
- float xOffset;
- float yOffset;
+ float dsdx;
+ float dtdx;
+ float dtdy;
+ float dsdy;
+ float tx;
+ float ty;
float xPrecision;
float yPrecision;
float xCursorPosition;
@@ -151,16 +152,14 @@
} motion;
struct Finished {
- uint32_t seq;
+ uint32_t empty1;
uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
inline size_t size() const { return sizeof(Finished); }
} finished;
struct Focus {
- uint32_t seq;
int32_t eventId;
- uint32_t empty1;
// The following two fields take up 4 bytes total
uint16_t hasFocus; // actually a bool
uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
@@ -172,6 +171,19 @@
bool isValid(size_t actualSize) const;
size_t size() const;
void getSanitizedCopy(InputMessage* msg) const;
+
+ static const char* typeToString(Type type) {
+ switch (type) {
+ case Type::KEY:
+ return "KEY";
+ case Type::MOTION:
+ return "MOTION";
+ case Type::FINISHED:
+ return "FINISHED";
+ case Type::FOCUS:
+ return "FOCUS";
+ }
+ }
};
/*
@@ -182,14 +194,15 @@
*
* The input channel is closed when all references to it are released.
*/
-class InputChannel : public RefBase {
-protected:
- virtual ~InputChannel();
-
+class InputChannel : public Parcelable {
public:
- static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd,
- sp<IBinder> token);
-
+ static std::unique_ptr<InputChannel> create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token);
+ InputChannel() = default;
+ InputChannel(const InputChannel& other)
+ : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
+ virtual ~InputChannel();
/**
* Create a pair of input channels.
* The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -198,10 +211,12 @@
* Return OK on success.
*/
static status_t openInputChannelPair(const std::string& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+ std::unique_ptr<InputChannel>& outServerChannel,
+ std::unique_ptr<InputChannel>& outClientChannel);
inline std::string getName() const { return mName; }
- inline int getFd() const { return mFd.get(); }
+ inline const android::base::unique_fd& getFd() const { return mFd; }
+ inline sp<IBinder> getToken() const { return mToken; }
/* Send a message to the other endpoint.
*
@@ -229,10 +244,10 @@
status_t receiveMessage(InputMessage* msg);
/* Return a new object that has a duplicate of this channel's fd. */
- sp<InputChannel> dup() const;
+ std::unique_ptr<InputChannel> dup() const;
- status_t write(Parcel& out) const;
- static sp<InputChannel> read(const Parcel& from);
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
/**
* The connection token is used to identify the input connection, i.e.
@@ -248,8 +263,20 @@
*/
sp<IBinder> getConnectionToken() const;
+ bool operator==(const InputChannel& inputChannel) const {
+ struct stat lhs, rhs;
+ if (fstat(mFd.get(), &lhs) != 0) {
+ return false;
+ }
+ if (fstat(inputChannel.getFd(), &rhs) != 0) {
+ return false;
+ }
+ // If file descriptors are pointing to same inode they are duplicated fds.
+ return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken &&
+ lhs.st_ino == rhs.st_ino;
+ }
+
private:
- InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token);
std::string mName;
android::base::unique_fd mFd;
@@ -262,13 +289,13 @@
class InputPublisher {
public:
/* Creates a publisher associated with an input channel. */
- explicit InputPublisher(const sp<InputChannel>& channel);
+ explicit InputPublisher(const std::shared_ptr<InputChannel>& channel);
/* Destroys the publisher and releases its input channel. */
~InputPublisher();
/* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
/* Publishes a key event to the input channel.
*
@@ -295,11 +322,10 @@
int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
int32_t actionButton, int32_t flags, int32_t edgeFlags,
int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale,
- float xOffset, float yOffset, float xPrecision, float yPrecision,
- float xCursorPosition, float yCursorPosition, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
/* Publishes a focus event to the input channel.
@@ -325,8 +351,7 @@
status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
private:
-
- sp<InputChannel> mChannel;
+ std::shared_ptr<InputChannel> mChannel;
};
/*
@@ -335,13 +360,13 @@
class InputConsumer {
public:
/* Creates a consumer associated with an input channel. */
- explicit InputConsumer(const sp<InputChannel>& channel);
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
/* Destroys the consumer and releases its input channel. */
~InputConsumer();
/* Gets the underlying input channel. */
- inline sp<InputChannel> getChannel() { return mChannel; }
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
/* Consumes an input event from the input channel and copies its contents into
* an InputEvent object created using the specified factory.
@@ -411,12 +436,13 @@
*/
int32_t getPendingBatchSource() const;
+ std::string dump() const;
+
private:
// True if touch resampling is enabled.
const bool mResampleTouch;
- // The input channel.
- sp<InputChannel> mChannel;
+ std::shared_ptr<InputChannel> mChannel;
// The current input message.
InputMessage mMsg;
@@ -427,9 +453,9 @@
// Batched motion events per device and source.
struct Batch {
- Vector<InputMessage> samples;
+ std::vector<InputMessage> samples;
};
- Vector<Batch> mBatches;
+ std::vector<Batch> mBatches;
// Touch state per device and source, only for sources of class pointer.
struct History {
@@ -516,7 +542,7 @@
return false;
}
};
- Vector<TouchState> mTouchStates;
+ std::vector<TouchState> mTouchStates;
// Chain of batched sequence numbers. When multiple input messages are combined into
// a batch, we append a record here that associates the last sequence number in the
@@ -526,7 +552,7 @@
uint32_t seq; // sequence number of batched input message
uint32_t chain; // sequence number of previous batched input message
};
- Vector<SeqChain> mSeqChains;
+ std::vector<SeqChain> mSeqChains;
status_t consumeBatch(InputEventFactoryInterface* factory,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index a695a8f..8a752c1 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,103 +17,114 @@
#ifndef _UI_INPUT_WINDOW_H
#define _UI_INPUT_WINDOW_H
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <input/Flags.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Transform.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include "InputApplication.h"
namespace android {
-class Parcel;
/*
* Describes the properties of a window that can receive input.
*/
-struct InputWindowInfo {
+struct InputWindowInfo : public Parcelable {
InputWindowInfo() = default;
- InputWindowInfo(const Parcel& from);
// Window flags from WindowManager.LayoutParams
- enum {
- FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
- FLAG_DIM_BEHIND = 0x00000002,
- FLAG_BLUR_BEHIND = 0x00000004,
- FLAG_NOT_FOCUSABLE = 0x00000008,
- FLAG_NOT_TOUCHABLE = 0x00000010,
- FLAG_NOT_TOUCH_MODAL = 0x00000020,
- FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
- FLAG_KEEP_SCREEN_ON = 0x00000080,
- FLAG_LAYOUT_IN_SCREEN = 0x00000100,
- FLAG_LAYOUT_NO_LIMITS = 0x00000200,
- FLAG_FULLSCREEN = 0x00000400,
- FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
- FLAG_DITHER = 0x00001000,
- FLAG_SECURE = 0x00002000,
- FLAG_SCALED = 0x00004000,
- FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
- FLAG_LAYOUT_INSET_DECOR = 0x00010000,
- FLAG_ALT_FOCUSABLE_IM = 0x00020000,
- FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
- FLAG_SHOW_WHEN_LOCKED = 0x00080000,
- FLAG_SHOW_WALLPAPER = 0x00100000,
- FLAG_TURN_SCREEN_ON = 0x00200000,
- FLAG_DISMISS_KEYGUARD = 0x00400000,
- FLAG_SPLIT_TOUCH = 0x00800000,
- FLAG_SLIPPERY = 0x20000000,
- };
+ enum class Flag : uint32_t {
+ ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ DIM_BEHIND = 0x00000002,
+ BLUR_BEHIND = 0x00000004,
+ NOT_FOCUSABLE = 0x00000008,
+ NOT_TOUCHABLE = 0x00000010,
+ NOT_TOUCH_MODAL = 0x00000020,
+ TOUCHABLE_WHEN_WAKING = 0x00000040,
+ KEEP_SCREEN_ON = 0x00000080,
+ LAYOUT_IN_SCREEN = 0x00000100,
+ LAYOUT_NO_LIMITS = 0x00000200,
+ FULLSCREEN = 0x00000400,
+ FORCE_NOT_FULLSCREEN = 0x00000800,
+ DITHER = 0x00001000,
+ SECURE = 0x00002000,
+ SCALED = 0x00004000,
+ IGNORE_CHEEK_PRESSES = 0x00008000,
+ LAYOUT_INSET_DECOR = 0x00010000,
+ ALT_FOCUSABLE_IM = 0x00020000,
+ WATCH_OUTSIDE_TOUCH = 0x00040000,
+ SHOW_WHEN_LOCKED = 0x00080000,
+ SHOW_WALLPAPER = 0x00100000,
+ TURN_SCREEN_ON = 0x00200000,
+ DISMISS_KEYGUARD = 0x00400000,
+ SPLIT_TOUCH = 0x00800000,
+ HARDWARE_ACCELERATED = 0x01000000,
+ LAYOUT_IN_OVERSCAN = 0x02000000,
+ TRANSLUCENT_STATUS = 0x04000000,
+ TRANSLUCENT_NAVIGATION = 0x08000000,
+ LOCAL_FOCUS_MODE = 0x10000000,
+ SLIPPERY = 0x20000000,
+ LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
+ DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
+ }; // Window types from WindowManager.LayoutParams
- // Window types from WindowManager.LayoutParams
- enum {
+ enum class Type : int32_t {
+ UNKNOWN = 0,
FIRST_APPLICATION_WINDOW = 1,
- TYPE_BASE_APPLICATION = 1,
- TYPE_APPLICATION = 2,
- TYPE_APPLICATION_STARTING = 3,
+ BASE_APPLICATION = 1,
+ APPLICATION = 2,
+ APPLICATION_STARTING = 3,
LAST_APPLICATION_WINDOW = 99,
- FIRST_SUB_WINDOW = 1000,
- TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
- TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
- TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
- TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
- TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
- LAST_SUB_WINDOW = 1999,
- FIRST_SYSTEM_WINDOW = 2000,
- TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
- TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
- TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
- TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
- TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
- TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
- TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
- TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
- TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
- TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
- TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
- TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
- TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
- TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
- TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
- TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15,
- TYPE_DRAG = FIRST_SYSTEM_WINDOW+16,
- TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17,
- TYPE_POINTER = FIRST_SYSTEM_WINDOW+18,
- 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,
- TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34,
- LAST_SYSTEM_WINDOW = 2999,
+ FIRST_SUB_WINDOW = 1000,
+ APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
+ APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
+ APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
+ APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
+ LAST_SUB_WINDOW = 1999,
+ FIRST_SYSTEM_WINDOW = 2000,
+ STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
+ PHONE = FIRST_SYSTEM_WINDOW + 2,
+ SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
+ KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
+ TOAST = FIRST_SYSTEM_WINDOW + 5,
+ SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
+ PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
+ SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
+ KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
+ SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
+ INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
+ INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
+ WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
+ STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
+ SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
+ DRAG = FIRST_SYSTEM_WINDOW + 16,
+ STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
+ POINTER = FIRST_SYSTEM_WINDOW + 18,
+ NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
+ VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
+ BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
+ INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
+ NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
+ MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
+ ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
+ DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
+ ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
+ NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+ LAST_SYSTEM_WINDOW = 2999,
};
- enum {
- INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
- INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
- INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
+ enum class Feature {
+ DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
+ NO_INPUT_CHANNEL = 0x00000002,
+ DISABLE_USER_ACTIVITY = 0x00000004,
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -125,9 +136,9 @@
// This uniquely identifies the input window.
int32_t id = -1;
std::string name;
- int32_t layoutParamsFlags = 0;
- int32_t layoutParamsType = 0;
- nsecs_t dispatchingTimeout = -1;
+ Flags<Flag> flags;
+ Type type = Type::UNKNOWN;
+ std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5);
/* These values are filled in by SurfaceFlinger. */
int32_t frameLeft = -1;
@@ -147,9 +158,8 @@
// in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
float globalScaleFactor = 1.0f;
- // Scaling factors applied to individual windows.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
+ // Transform applied to individual windows.
+ ui::Transform transform;
/*
* This is filled in by the WM relative to the frame and then translated
@@ -161,9 +171,15 @@
bool hasFocus = false;
bool hasWallpaper = false;
bool paused = false;
+ /* This flag is set when the window is of a trusted type that is allowed to silently
+ * overlay other windows for the purpose of implementing the secure views feature.
+ * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+ * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+ */
+ bool trustedOverlay = false;
int32_t ownerPid = -1;
int32_t ownerUid = -1;
- int32_t inputFeatures = 0;
+ Flags<Feature> inputFeatures;
int32_t displayId = ADISPLAY_ID_NONE;
int32_t portalToDisplayId = ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
@@ -173,23 +189,19 @@
void addTouchableRegion(const Rect& region);
bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
- bool frameContainsPoint(int32_t x, int32_t y) const;
- /* Returns true if the window is of a trusted type that is allowed to silently
- * overlay other windows for the purpose of implementing the secure views feature.
- * Trusted overlays, such as IME windows, can partly obscure other windows without causing
- * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
- */
- bool isTrustedOverlay() const;
+ bool frameContainsPoint(int32_t x, int32_t y) const;
bool supportsSplitTouch() const;
bool overlaps(const InputWindowInfo* other) const;
- status_t write(Parcel& output) const;
- static InputWindowInfo read(const Parcel& from);
-};
+ bool operator==(const InputWindowInfo& inputChannel) const;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+};
/*
* Handle for a window that can receive input.
@@ -199,37 +211,36 @@
*/
class InputWindowHandle : public RefBase {
public:
+ explicit InputWindowHandle();
+ InputWindowHandle(const InputWindowHandle& other);
+ InputWindowHandle(const InputWindowInfo& other);
- inline const InputWindowInfo* getInfo() const {
- return &mInfo;
- }
+ inline const InputWindowInfo* getInfo() const { return &mInfo; }
sp<IBinder> getToken() const;
int32_t getId() const { return mInfo.id; }
- sp<IBinder> getApplicationToken() {
- return mInfo.applicationInfo.token;
- }
+ sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; }
- inline std::string getName() const {
- return !mInfo.name.empty() ? mInfo.name : "<invalid>";
- }
+ inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; }
- inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
+ inline std::chrono::nanoseconds getDispatchingTimeout(
+ std::chrono::nanoseconds defaultValue) const {
+ return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
}
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
+ * As this class is created as RefBase object, no pure virtual function is allowed.
*
* This method should only be called from within the input dispatcher's
* critical section.
*
* Returns true on success, or false if the handle is no longer valid.
*/
- virtual bool updateInfo() = 0;
+ virtual bool updateInfo() { return false; }
/**
* Updates from another input window handle.
@@ -242,13 +253,15 @@
*/
void releaseChannel();
+ // Not override since this class is not derrived from Parcelable.
+ status_t readFromParcel(const android::Parcel* parcel);
+ status_t writeToParcel(android::Parcel* parcel) const;
+
protected:
- explicit InputWindowHandle();
virtual ~InputWindowHandle();
InputWindowInfo mInfo;
};
-
} // namespace android
#endif // _UI_INPUT_WINDOW_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 727865a..ee010a3 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -18,8 +18,8 @@
#define _LIBINPUT_VELOCITY_TRACKER_H
#include <input/Input.h>
-#include <utils/Timers.h>
#include <utils/BitSet.h>
+#include <utils/Timers.h>
namespace android {
@@ -30,6 +30,22 @@
*/
class VelocityTracker {
public:
+ enum class Strategy : int32_t {
+ DEFAULT = -1,
+ MIN = 0,
+ IMPULSE = 0,
+ LSQ1 = 1,
+ LSQ2 = 2,
+ LSQ3 = 3,
+ WLSQ2_DELTA = 4,
+ WLSQ2_CENTRAL = 5,
+ WLSQ2_RECENT = 6,
+ INT1 = 7,
+ INT2 = 8,
+ LEGACY = 9,
+ MAX = LEGACY,
+ };
+
struct Position {
float x, y;
};
@@ -62,8 +78,8 @@
};
// Creates a velocity tracker using the specified strategy.
- // If strategy is NULL, uses the default strategy for the platform.
- VelocityTracker(const char* strategy = nullptr);
+ // If strategy is not provided, uses the default strategy for the platform.
+ VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
~VelocityTracker();
@@ -102,16 +118,21 @@
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
private:
- static const char* DEFAULT_STRATEGY;
+ // The default velocity tracker strategy.
+ // Although other strategies are available for testing and comparison purposes,
+ // this is the strategy that applications will actually use. Be very careful
+ // when adjusting the default strategy because it can dramatically affect
+ // (often in a bad way) the user experience.
+ static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
nsecs_t mLastEventTime;
BitSet32 mCurrentPointerIdBits;
int32_t mActivePointerId;
- VelocityTrackerStrategy* mStrategy;
+ std::unique_ptr<VelocityTrackerStrategy> mStrategy;
- bool configureStrategy(const char* strategy);
+ bool configureStrategy(const Strategy strategy);
- static VelocityTrackerStrategy* createStrategy(const char* strategy);
+ static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
};
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
deleted file mode 100644
index 853f0c9..0000000
--- a/include/powermanager/IPowerManager.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2011 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_IPOWERMANAGER_H
-#define ANDROID_IPOWERMANAGER_H
-
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <hardware/power.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IPowerManager : public IInterface
-{
-public:
- // These transaction IDs must be kept in sync with the method order from
- // IPowerManager.aidl.
- enum {
- ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION,
- ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1,
- RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2,
- UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3,
- POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4,
- UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5,
- IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6,
- USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7,
- WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8,
- GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9,
- NAP = IBinder::FIRST_CALL_TRANSACTION + 10,
- IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11,
- IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12,
- GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13,
- SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14,
- REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17,
- REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18,
- SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19,
- CRASH = IBinder::FIRST_CALL_TRANSACTION + 20,
- };
-
- DECLARE_META_INTERFACE(PowerManager)
-
- // The parcels created by these methods must be kept in sync with the
- // corresponding methods from IPowerManager.aidl.
- // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay = false) = 0;
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay = false) = 0;
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0;
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay = false) = 0;
- virtual status_t powerHint(int hintId, int data) = 0;
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0;
- virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t crash(const String16& message) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPOWERMANAGER_H
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..dd34c0a
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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_POWERHALCONTROLLER_H
+#define ANDROID_POWERHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+// Connects to underlying Power HAL handles.
+class HalConnector {
+public:
+ HalConnector() = default;
+ virtual ~HalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect();
+ virtual void reset();
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Controller for Power HAL handle.
+// This relies on HalConnector to connect to the underlying Power HAL
+// service and reconnects to it after each failed api call. This also ensures
+// connecting to the service is thread-safe.
+class PowerHalController : public HalWrapper {
+public:
+ PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {}
+ explicit PowerHalController(std::unique_ptr<HalConnector> connector)
+ : mHalConnector(std::move(connector)) {}
+ virtual ~PowerHalController() = default;
+
+ void init();
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+private:
+ std::mutex mConnectedHalMutex;
+ std::unique_ptr<HalConnector> mHalConnector;
+
+ // Shared pointers to keep global pointer and allow local copies to be used in
+ // different threads
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
+ const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
+
+ std::shared_ptr<HalWrapper> initHal();
+ HalResult processHalResult(HalResult result, const char* functionName);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALCONTROLLER_H
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
new file mode 100644
index 0000000..ed6f6f3
--- /dev/null
+++ b/include/powermanager/PowerHalLoader.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 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_POWERHALLOADER_H
+#define ANDROID_POWERHALLOADER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+namespace android {
+
+namespace power {
+
+// Loads available Power HAL services.
+class PowerHalLoader {
+public:
+ static void unloadAll();
+ static sp<hardware::power::IPower> loadAidl();
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
+ static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
+
+private:
+ static std::mutex gHalMutex;
+ static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
+ EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+
+ PowerHalLoader() = delete;
+ ~PowerHalLoader() = delete;
+};
+
+}; // namespace power
+
+} // namespace android
+
+#endif // ANDROID_POWERHALLOADER_H
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
new file mode 100644
index 0000000..c3e7601
--- /dev/null
+++ b/include/powermanager/PowerHalWrapper.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 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_POWERHALWRAPPER_H
+#define ANDROID_POWERHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+namespace android {
+
+namespace power {
+
+// State of Power HAL support for individual apis.
+enum class HalSupport {
+ UNKNOWN = 0,
+ ON = 1,
+ OFF = 2,
+};
+
+// State of the Power HAL api call result.
+enum class HalResult {
+ SUCCESSFUL = 0,
+ FAILED = 1,
+ UNSUPPORTED = 2,
+};
+
+// Wrapper for Power HAL handlers.
+class HalWrapper {
+public:
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0;
+};
+
+// Empty Power HAL wrapper that ignores all api calls.
+class EmptyHalWrapper : public HalWrapper {
+public:
+ EmptyHalWrapper() = default;
+ ~EmptyHalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+};
+
+// Wrapper for the HIDL Power HAL v1.0.
+class HidlHalWrapperV1_0 : public HalWrapper {
+public:
+ explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
+ : mHandleV1_0(std::move(Hal)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+protected:
+ virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+
+private:
+ sp<hardware::power::V1_0::IPower> mHandleV1_0;
+ HalResult setInteractive(bool enabled);
+ HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled);
+};
+
+// Wrapper for the HIDL Power HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+public:
+ HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
+ sp<hardware::power::V1_1::IPower> handleV1_1)
+ : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+protected:
+ virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ uint32_t data) override;
+
+private:
+ sp<hardware::power::V1_1::IPower> mHandleV1_1;
+};
+
+// Wrapper for the AIDL Power HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+private:
+ // Control access to the boost and mode supported arrays.
+ std::mutex mBoostMutex;
+ std::mutex mModeMutex;
+ sp<hardware::power::IPower> mHandle;
+ // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
+ // Need to increase the array size if more boost supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
+ // Android framework only sends mode upto DISPLAY_INACTIVE.
+ // Need to increase the array if more mode supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+ mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
+};
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALWRAPPER_H
diff --git a/include/ui/Rotation.h b/include/ui/Rotation.h
new file mode 120000
index 0000000..095d2ce
--- /dev/null
+++ b/include/ui/Rotation.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rotation.h
\ No newline at end of file
diff --git a/include/ui/Transform.h b/include/ui/Transform.h
new file mode 120000
index 0000000..323f1fd
--- /dev/null
+++ b/include/ui/Transform.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Transform.h
\ No newline at end of file
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 5a0d3f6..dae6eeb 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -83,28 +83,28 @@
InitFrameworkHandlers();
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd_ == -1) {
- PLOG(FATAL) << "failed to create epoll fd";
+ PLOG(FATAL) << "adbd_auth: failed to create epoll fd";
}
event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
if (event_fd_ == -1) {
- PLOG(FATAL) << "failed to create eventfd";
+ PLOG(FATAL) << "adbd_auth: failed to create eventfd";
}
sock_fd_.reset(android_get_control_socket("adbd"));
if (sock_fd_ == -1) {
- PLOG(ERROR) << "failed to get adbd authentication socket";
+ PLOG(ERROR) << "adbd_auth: failed to get adbd authentication socket";
} else {
if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
- PLOG(FATAL) << "failed to make adbd authentication socket cloexec";
+ PLOG(FATAL) << "adbd_auth: failed to make adbd authentication socket cloexec";
}
if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) {
- PLOG(FATAL) << "failed to make adbd authentication socket nonblocking";
+ PLOG(FATAL) << "adbd_auth: failed to make adbd authentication socket nonblocking";
}
if (listen(sock_fd_.get(), 4) != 0) {
- PLOG(FATAL) << "failed to listen on adbd authentication socket";
+ PLOG(FATAL) << "adbd_auth: failed to listen on adbd authentication socket";
}
}
}
@@ -146,7 +146,7 @@
struct epoll_event event;
event.events = EPOLLIN;
if (!output_queue_.empty()) {
- LOG(INFO) << "marking framework writable";
+ LOG(INFO) << "adbd_auth: marking framework writable";
event.events |= EPOLLOUT;
}
event.data.u64 = kEpollConstFramework;
@@ -155,7 +155,7 @@
}
void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) {
- LOG(INFO) << "received new framework fd " << new_fd.get()
+ LOG(INFO) << "adbd_auth: received new framework fd " << new_fd.get()
<< " (current = " << framework_fd_.get() << ")";
// If we already had a framework fd, clean up after ourselves.
@@ -170,7 +170,7 @@
struct epoll_event event;
event.events = EPOLLIN;
if (!output_queue_.empty()) {
- LOG(INFO) << "marking framework writable";
+ LOG(INFO) << "adbd_auth: marking framework writable";
event.events |= EPOLLOUT;
}
event.data.u64 = kEpollConstFramework;
@@ -180,10 +180,10 @@
}
void HandlePacket(std::string_view packet) EXCLUDES(mutex_) {
- LOG(INFO) << "received packet: " << packet;
+ LOG(INFO) << "adbd_auth: received packet: " << packet;
if (packet.size() < 2) {
- LOG(ERROR) << "received packet of invalid length";
+ LOG(ERROR) << "adbd_auth: received packet of invalid length";
std::lock_guard<std::mutex> lock(mutex_);
ReplaceFrameworkFd(unique_fd());
}
@@ -197,7 +197,7 @@
}
}
if (!handled_packet) {
- LOG(ERROR) << "unhandled packet: " << packet;
+ LOG(ERROR) << "adbd_auth: unhandled packet: " << packet;
std::lock_guard<std::mutex> lock(mutex_);
ReplaceFrameworkFd(unique_fd());
}
@@ -206,12 +206,18 @@
void AllowUsbDevice(std::string_view buf) EXCLUDES(mutex_) {
std::lock_guard<std::mutex> lock(mutex_);
CHECK(buf.empty());
- CHECK(dispatched_prompt_.has_value());
- auto& [id, key, arg] = *dispatched_prompt_;
- keys_.emplace(id, std::move(key));
- callbacks_.key_authorized(arg, id);
- dispatched_prompt_ = std::nullopt;
+ if (dispatched_prompt_.has_value()) {
+ // It's possible for the framework to send us a response without our having sent a
+ // request to it: e.g. if adbd restarts while we have a pending request.
+ auto& [id, key, arg] = *dispatched_prompt_;
+ keys_.emplace(id, std::move(key));
+
+ callbacks_.key_authorized(arg, id);
+ dispatched_prompt_ = std::nullopt;
+ } else {
+ LOG(WARNING) << "adbd_auth: received authorization for unknown prompt, ignoring";
+ }
// We need to dispatch pending prompts here upon success as well,
// since we might have multiple queued prompts.
@@ -273,14 +279,14 @@
iovs[2].iov_base = p->public_key.data();
iovs[2].iov_len = p->public_key.size();
} else {
- LOG(FATAL) << "unhandled packet type?";
+ LOG(FATAL) << "adbd_auth: unhandled packet type?";
}
output_queue_.pop_front();
ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt);
if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
- PLOG(ERROR) << "failed to write to framework fd";
+ PLOG(ERROR) << "adbd_auth: failed to write to framework fd";
ReplaceFrameworkFd(unique_fd());
return false;
}
@@ -290,7 +296,7 @@
void Run() {
if (sock_fd_ == -1) {
- LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts";
+ LOG(ERROR) << "adbd_auth: socket unavailable, disabling user prompts";
} else {
struct epoll_event event;
event.events = EPOLLIN;
@@ -309,9 +315,9 @@
struct epoll_event events[3];
int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1));
if (rc == -1) {
- PLOG(FATAL) << "epoll_wait failed";
+ PLOG(FATAL) << "adbd_auth: epoll_wait failed";
} else if (rc == 0) {
- LOG(FATAL) << "epoll_wait returned 0";
+ LOG(FATAL) << "adbd_auth: epoll_wait returned 0";
}
bool restart = false;
@@ -326,7 +332,7 @@
unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr,
SOCK_CLOEXEC | SOCK_NONBLOCK));
if (new_framework_fd == -1) {
- PLOG(FATAL) << "failed to accept framework fd";
+ PLOG(FATAL) << "adbd_auth: failed to accept framework fd";
}
LOG(INFO) << "adbd_auth: received a new framework connection";
@@ -344,7 +350,8 @@
uint64_t dummy;
int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
if (rc != 8) {
- PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")";
+ PLOG(FATAL)
+ << "adbd_auth: failed to read from eventfd (rc = " << rc << ")";
}
std::lock_guard<std::mutex> lock(mutex_);
@@ -357,9 +364,9 @@
if (event.events & EPOLLIN) {
int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf)));
if (rc == -1) {
- LOG(FATAL) << "failed to read from framework fd";
+ LOG(FATAL) << "adbd_auth: failed to read from framework fd";
} else if (rc == 0) {
- LOG(INFO) << "hit EOF on framework fd";
+ LOG(INFO) << "adbd_auth: hit EOF on framework fd";
std::lock_guard<std::mutex> lock(mutex_);
ReplaceFrameworkFd(unique_fd());
} else {
@@ -386,10 +393,10 @@
void IteratePublicKeys(bool (*callback)(void*, const char*, size_t), void* opaque) {
for (const auto& path : key_paths) {
if (access(path, R_OK) == 0) {
- LOG(INFO) << "Loading keys from " << path;
+ LOG(INFO) << "adbd_auth: loading keys from " << path;
std::string content;
if (!android::base::ReadFileToString(path, &content)) {
- PLOG(ERROR) << "Couldn't read " << path;
+ PLOG(ERROR) << "adbd_auth: couldn't read " << path;
continue;
}
for (const auto& line : android::base::Split(content, "\n")) {
@@ -405,6 +412,7 @@
uint64_t id = NextId();
std::lock_guard<std::mutex> lock(mutex_);
+ LOG(INFO) << "adbd_auth: sending prompt with id " << id;
pending_prompts_.emplace_back(id, public_key, arg);
DispatchPendingPrompt();
return id;
@@ -423,7 +431,7 @@
std::lock_guard<std::mutex> lock(mutex_);
auto it = keys_.find(id);
if (it == keys_.end()) {
- LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping";
+ LOG(DEBUG) << "adbd_auth: couldn't find public key to notify disconnection, skipping";
return;
}
output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)});
@@ -446,7 +454,8 @@
std::lock_guard<std::mutex> lock(mutex_);
auto it = keys_.find(id);
if (it == keys_.end()) {
- LOG(DEBUG) << "couldn't find public key to notify disconnection of tls device, skipping";
+ LOG(DEBUG) << "adbd_auth: couldn't find public key to notify disconnection of tls "
+ "device, skipping";
return;
}
output_queue_.emplace_back(AdbdPacketTlsDeviceDisconnected{
@@ -461,9 +470,9 @@
uint64_t value = 1;
ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
if (rc == -1) {
- PLOG(FATAL) << "write to eventfd failed";
+ PLOG(FATAL) << "adbd_auth: write to eventfd failed";
} else if (rc != sizeof(value)) {
- LOG(FATAL) << "write to eventfd returned short (" << rc << ")";
+ LOG(FATAL) << "adbd_auth: write to eventfd returned short (" << rc << ")";
}
}
@@ -516,8 +525,9 @@
if (callbacks->version == 1) {
return new AdbdAuthContext(reinterpret_cast<AdbdAuthCallbacksV1*>(callbacks));
} else {
- LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version;
- return nullptr;
+ LOG(ERROR) << "adbd_auth: received unknown AdbdAuthCallbacks version "
+ << callbacks->version;
+ return nullptr;
}
}
@@ -545,7 +555,12 @@
void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len,
void* opaque) {
- ctx->PromptUser(std::string_view(public_key, len), opaque);
+ adbd_auth_prompt_user_with_id(ctx, public_key, len, opaque);
+}
+
+uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx, const char* public_key, size_t len,
+ void* opaque) {
+ return ctx->PromptUser(std::string_view(public_key, len), opaque);
}
uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx,
diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h
index 6ee3166..8f834df 100644
--- a/libs/adbd_auth/include/adbd_auth.h
+++ b/libs/adbd_auth/include/adbd_auth.h
@@ -122,9 +122,23 @@
* @param len the length of the public_key argument
* @param arg an opaque userdata argument
*/
-void adbd_auth_prompt_user(AdbdAuthContext* ctx,
- const char* public_key,
- size_t len, void* opaque) __INTRODUCED_IN(30);
+void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* opaque)
+ __INTRODUCED_IN(30);
+
+/**
+ * Prompt the user to authorize a public key.
+ *
+ * When this happens, a callback will be run on the auth thread with the result.
+ *
+ * @param ctx the AdbdAuthContext
+ * @param public_key the RSA public key to prompt user with
+ * @param len the length of the public_key argument
+ * @param arg an opaque userdata argument
+ * @return a unique id which will be returned via callback
+ */
+__attribute__((weak)) uint64_t adbd_auth_prompt_user_with_id(AdbdAuthContext* ctx,
+ const char* public_key, size_t len,
+ void* opaque) __INTRODUCED_IN(30);
/**
* Let system_server know that a TLS device has connected.
diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt
index 5857ecb..7584ca3 100644
--- a/libs/adbd_auth/libadbd_auth.map.txt
+++ b/libs/adbd_auth/libadbd_auth.map.txt
@@ -7,6 +7,7 @@
adbd_auth_notify_auth; # apex introduced=30
adbd_auth_notify_disconnect; # apex introduced=30
adbd_auth_prompt_user; # apex introduced=30
+ adbd_auth_prompt_user_with_id; # apex introduced=30
adbd_auth_tls_device_connected; # apex introduced=30
adbd_auth_tls_device_disconnected; # apex introduced=30
adbd_auth_get_max_version; # apex introduced=30
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 2518b14..258a4e3 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -29,10 +29,13 @@
name: "libarect",
host_supported: true,
vendor_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
windows: {
enabled: true,
},
},
+ min_sdk_version: "29",
}
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..d005058 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
return PROCESS_STATE_UNKNOWN;
}
+bool ActivityManager::setSchedPolicyCgroup(const int32_t tid, const int32_t group)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->setSchedPolicyCgroup(tid, group);
+ }
+ return false;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7298282..b24a577 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -17,6 +17,8 @@
export_include_dirs: ["include"],
vendor_available: true,
host_supported: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
header_libs: [
"libbase_headers",
@@ -28,6 +30,7 @@
"libcutils_headers",
"libutils_headers",
],
+ min_sdk_version: "29",
}
// These interfaces are android-specific implementation unrelated to binder
@@ -61,6 +64,8 @@
},
double_loadable: true,
host_supported: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
// TODO(b/31559095): get headers from bionic on host
include_dirs: [
@@ -152,6 +157,7 @@
sanitize: {
misc_undefined: ["integer"],
},
+ min_sdk_version: "29",
}
// AIDL interface between libbinder and framework.jar
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 2174ce2..1c6b491 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -91,12 +91,12 @@
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
- return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(),
+ return noteOp(op, uid, callingPackage, {},
String16("Legacy AppOpsManager.noteOp call"));
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag, const String16& message) {
+ const std::optional<String16>& attributionTag, const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->noteOperation(op, uid, callingPackage, attributionTag,
@@ -108,12 +108,12 @@
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault) {
- return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(),
+ return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, {},
String16("Legacy AppOpsManager.startOpNoThrow call"));
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
+ bool startIfModeDefault, const std::optional<String16>& attributionTag,
const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
@@ -125,11 +125,11 @@
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
- finishOp(op, uid, callingPackage, std::unique_ptr<String16>());
+ finishOp(op, uid, callingPackage, {});
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag) {
+ const std::optional<String16>& attributionTag) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index e0fb543..6ca3b16 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -24,6 +24,7 @@
#include <binder/IShellCallback.h>
#include <binder/Parcel.h>
+#include <linux/sched.h>
#include <stdio.h>
namespace android {
@@ -133,6 +134,8 @@
// unlocked objects
bool mRequestingSid = false;
sp<IBinder> mExtension;
+ int mPolicy = SCHED_NORMAL;
+ int mPriority = 0;
// for below objects
Mutex mLock;
@@ -279,6 +282,47 @@
return e->mExtension;
}
+void BBinder::setMinSchedulerPolicy(int policy, int priority) {
+ switch (policy) {
+ case SCHED_NORMAL:
+ LOG_ALWAYS_FATAL_IF(priority < -20 || priority > 19, "Invalid priority for SCHED_NORMAL: %d", priority);
+ break;
+ case SCHED_RR:
+ case SCHED_FIFO:
+ LOG_ALWAYS_FATAL_IF(priority < 1 || priority > 99, "Invalid priority for sched %d: %d", policy, priority);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unrecognized scheduling policy: %d", policy);
+ }
+
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (e == nullptr) {
+ // Avoid allocations if called with default.
+ if (policy == SCHED_NORMAL && priority == 0) {
+ return;
+ }
+
+ e = getOrCreateExtras();
+ if (!e) return; // out of memory
+ }
+
+ e->mPolicy = policy;
+ e->mPriority = priority;
+}
+
+int BBinder::getMinSchedulerPolicy() {
+ Extras* e = mExtras.load(std::memory_order_acquire);
+ if (e == nullptr) return SCHED_NORMAL;
+ return e->mPolicy;
+}
+
+int BBinder::getMinSchedulerPriority() {
+ Extras* e = mExtras.load(std::memory_order_acquire);
+ if (e == nullptr) return 0;
+ return e->mPriority;
+}
+
pid_t BBinder::getDebugPid() {
return getpid();
}
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..a302112 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,17 @@
}
return reply.readInt32();
}
+
+ virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(tid);
+ data.writeInt32(group);
+ remote()->transact(SET_SCHED_POLICY_CGROUP_TRANSACTION, data, &reply);
+ if (reply.readExceptionCode() != 0) return false;
+ return reply.readBool();
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 0714723..cd78866 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -22,6 +22,8 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
+#include <optional>
+
namespace android {
// ----------------------------------------------------------------------
@@ -47,7 +49,7 @@
}
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -64,7 +66,7 @@
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag,
+ const String16& packageName, const std::optional<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -83,7 +85,7 @@
}
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag) {
+ const String16& packageName, const std::optional<String16>& attributionTag) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
@@ -182,7 +184,7 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> attributionTag;
+ std::optional<String16> attributionTag;
data.readString16(&attributionTag);
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
String16 message = data.readString16();
@@ -198,7 +200,7 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> attributionTag;
+ std::optional<String16> attributionTag;
data.readString16(&attributionTag);
bool startIfModeDefault = data.readInt32() == 1;
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
@@ -215,7 +217,7 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> attributionTag;
+ std::optional<String16> attributionTag;
data.readString16(&attributionTag);
finishOperation(token, code, uid, packageName, attributionTag);
reply->writeNoException();
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index c2bb811..d8b44f9 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -82,10 +82,10 @@
explicit BpMemoryHeap(const sp<IBinder>& impl);
virtual ~BpMemoryHeap();
- virtual int getHeapID() const;
- virtual void* getBase() const;
- virtual size_t getSize() const;
- virtual uint32_t getFlags() const;
+ int getHeapID() const override;
+ void* getBase() const override;
+ size_t getSize() const override;
+ uint32_t getFlags() const override;
off_t getOffset() const override;
private:
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9888b59..9aa82d9 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -18,6 +18,9 @@
#include <binder/IServiceManager.h>
+#include <inttypes.h>
+#include <unistd.h>
+
#include <android/os/BnServiceCallback.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
@@ -36,8 +39,6 @@
#include "Static.h"
-#include <unistd.h>
-
namespace android {
using AidlServiceManager = android::os::IServiceManager;
@@ -85,8 +86,8 @@
sp<AidlServiceManager> mTheRealServiceManager;
};
-static std::once_flag gSmOnce;
-static sp<IServiceManager> gDefaultServiceManager;
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;
sp<IServiceManager> defaultServiceManager()
{
@@ -95,6 +96,7 @@
while (sm == nullptr) {
sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
if (sm == nullptr) {
+ ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
sleep(1);
}
}
@@ -205,6 +207,10 @@
: mTheRealServiceManager(impl)
{}
+// This implementation could be simplified and made more efficient by delegating
+// to waitForService. However, this changes the threading structure in some
+// cases and could potentially break prebuilts. Once we have higher logistical
+// complexity, this could be attempted.
sp<IBinder> ServiceManagerShim::getService(const String16& name) const
{
static bool gSystemBootCompleted = false;
@@ -214,7 +220,8 @@
const bool isVendorService =
strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
- const long timeout = uptimeMillis() + 5000;
+ const long timeout = 5000;
+ int64_t startTime = uptimeMillis();
// Vendor code can't access system properties
if (!gSystemBootCompleted && !isVendorService) {
#ifdef __ANDROID__
@@ -228,15 +235,21 @@
// retry interval in millisecond; note that vendor services stay at 100ms
const long sleepTime = gSystemBootCompleted ? 1000 : 100;
+ ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
+ ProcessState::self()->getDriverName().c_str());
+
int n = 0;
- while (uptimeMillis() < timeout) {
+ while (uptimeMillis() - startTime < timeout) {
n++;
- ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
- ProcessState::self()->getDriverName().c_str());
usleep(1000*sleepTime);
sp<IBinder> svc = checkService(name);
- if (svc != nullptr) return svc;
+ if (svc != nullptr) {
+ ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms",
+ String8(name).string(), ProcessState::self()->getDriverName().c_str(),
+ uptimeMillis() - startTime);
+ return svc;
+ }
}
ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
return nullptr;
@@ -321,6 +334,11 @@
while(true) {
{
+ // It would be really nice if we could read binder commands on this
+ // thread instead of needing a threadpool to be started, but for
+ // instance, if we call getAndExecuteCommand, it might be the case
+ // that another thread serves the callback, and we never get a
+ // command, so we hang indefinitely.
std::unique_lock<std::mutex> lock(waiter->mMutex);
using std::literals::chrono_literals::operator""s;
waiter->mCv.wait_for(lock, 1s, [&] {
@@ -329,6 +347,8 @@
if (waiter->mBinder != nullptr) return waiter->mBinder;
}
+ ALOGW("Waited one second for %s (is service started? are binder threads started and available?)", name.c_str());
+
// Handle race condition for lazy services. Here is what can happen:
// - the service dies (not processed by init yet).
// - sm processes death notification.
@@ -342,8 +362,6 @@
return nullptr;
}
if (out != nullptr) return out;
-
- ALOGW("Waited one second for %s", name.c_str());
}
}
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 6f49aa1..325e204 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -45,25 +45,31 @@
Status onClients(const sp<IBinder>& service, bool clients) override;
private:
+ struct Service {
+ sp<IBinder> service;
+ bool allowIsolated;
+ int dumpFlags;
+
+ // whether, based on onClients calls, we know we have a client for this
+ // service or not
+ bool clients = false;
+ };
+
+ /**
+ * Looks up a service guaranteed to be registered (service from onClients).
+ */
+ std::map<std::string, Service>::iterator assertRegisteredService(const sp<IBinder>& service);
+
/**
* Unregisters all services that we can. If we can't unregister all, re-register other
* services.
*/
void tryShutdown();
- /**
- * Counter of the number of services that currently have at least one client.
- */
+ // count of services with clients
size_t mNumConnectedServices;
- struct Service {
- sp<IBinder> service;
- bool allowIsolated;
- int dumpFlags;
- };
- /**
- * Map of registered names and services
- */
+ // map of registered names and services
std::map<std::string, Service> mRegisteredServices;
bool mForcePersist;
@@ -89,12 +95,28 @@
}
// Only add this when a service is added for the first time, as it is not removed
- mRegisteredServices[name] = {service, allowIsolated, dumpFlags};
+ mRegisteredServices[name] = {
+ .service = service,
+ .allowIsolated = allowIsolated,
+ .dumpFlags = dumpFlags
+ };
}
return true;
}
+std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) {
+ LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service");
+ for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) {
+ auto const& [name, registered] = *it;
+ (void) name;
+ if (registered.service != service) continue;
+ return it;
+ }
+ LOG_ALWAYS_FATAL("Got callback on service which we did not register: %s", String8(service->getInterfaceDescriptor()).c_str());
+ __builtin_unreachable();
+}
+
void ClientCounterCallback::forcePersist(bool persist) {
mForcePersist = persist;
if(!mForcePersist) {
@@ -108,15 +130,25 @@
* invocations could occur on different threads however.
*/
Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
- if (clients) {
- mNumConnectedServices++;
- } else {
- mNumConnectedServices--;
+ auto & [name, registered] = *assertRegisteredService(service);
+ if (registered.clients == clients) {
+ LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
+ "notified has clients: %d", name.c_str(), registered.clients, clients);
+ }
+ registered.clients = clients;
+
+ // update cache count of clients
+ {
+ size_t numWithClients = 0;
+ for (const auto& [name, registered] : mRegisteredServices) {
+ (void) name;
+ if (registered.clients) numWithClients++;
+ }
+ mNumConnectedServices = numWithClients;
}
ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
- mNumConnectedServices, mRegisteredServices.size(),
- String8(service->getInterfaceDescriptor()).string(), clients);
+ mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
tryShutdown();
return Status::ok();
@@ -192,4 +224,4 @@
}
} // namespace hardware
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index ebf91f9..b46b3e8 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -387,7 +387,7 @@
while (cur) {
if (cur->start == start) {
LOG_FATAL_IF(cur->free,
- "block at offset 0x%08lX of size 0x%08lX already freed",
+ "block at offset 0x%08lX of size 0x%08X already freed",
cur->start*kMemoryAlign, cur->size*kMemoryAlign);
// merge freed blocks together
@@ -411,7 +411,7 @@
}
#endif
LOG_FATAL_IF(!freed->free,
- "freed block at offset 0x%08lX of size 0x%08lX is not free!",
+ "freed block at offset 0x%08lX of size 0x%08X is not free!",
freed->start * kMemoryAlign, freed->size * kMemoryAlign);
return freed;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9642a87..8fd59ba 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
@@ -188,16 +189,18 @@
return OK;
}
+static constexpr inline int schedPolicyMask(int policy, int priority) {
+ return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT);
+}
+
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
flat_binder_object obj;
+ obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
- if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
- /* minimum priority for all nodes is nice 0 */
- obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
- } else {
- /* minimum priority for all nodes is MAX_NICE(19) */
- obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ int schedBits = 0;
+ if (!IPCThreadState::self()->backgroundSchedulingDisabled()) {
+ schedBits = schedPolicyMask(SCHED_NORMAL, 19);
}
if (binder != nullptr) {
@@ -213,6 +216,13 @@
obj.handle = handle;
obj.cookie = 0;
} else {
+ int policy = local->getMinSchedulerPolicy();
+ int priority = local->getMinSchedulerPriority();
+
+ if (policy != 0 || priority != 0) {
+ // override value, since it is set explicitly
+ schedBits = schedPolicyMask(policy, priority);
+ }
if (local->isRequestingSid()) {
obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
}
@@ -226,6 +236,8 @@
obj.cookie = 0;
}
+ obj.flags |= schedBits;
+
return finishFlattenBinder(binder, obj);
}
@@ -751,6 +763,13 @@
return NO_ERROR;
}
+status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) {
+ if (!str) {
+ return writeInt32(-1);
+ }
+ return writeUtf8AsUtf16(*str);
+}
+
status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) {
if (!str) {
return writeInt32(-1);
@@ -775,6 +794,12 @@
return writeByteVectorInternal(val.data(), val.size());
}
+status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val)
+{
+ if (!val) return writeInt32(-1);
+ return writeByteVectorInternal(val->data(), val->size());
+}
+
status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
{
if (!val) return writeInt32(-1);
@@ -785,6 +810,12 @@
return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
}
+status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val)
+{
+ if (!val) return writeInt32(-1);
+ return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
+}
+
status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val)
{
if (!val) return writeInt32(-1);
@@ -796,6 +827,11 @@
return writeTypedVector(val, &Parcel::writeInt32);
}
+status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeInt32);
+}
+
status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeInt32);
@@ -806,6 +842,11 @@
return writeTypedVector(val, &Parcel::writeInt64);
}
+status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeInt64);
+}
+
status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeInt64);
@@ -816,6 +857,11 @@
return writeTypedVector(val, &Parcel::writeUint64);
}
+status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeUint64);
+}
+
status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeUint64);
@@ -826,6 +872,11 @@
return writeTypedVector(val, &Parcel::writeFloat);
}
+status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeFloat);
+}
+
status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeFloat);
@@ -836,6 +887,11 @@
return writeTypedVector(val, &Parcel::writeDouble);
}
+status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeDouble);
+}
+
status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeDouble);
@@ -846,6 +902,11 @@
return writeTypedVector(val, &Parcel::writeBool);
}
+status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeBool);
+}
+
status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeBool);
@@ -856,6 +917,11 @@
return writeTypedVector(val, &Parcel::writeChar);
}
+status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeChar);
+}
+
status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeChar);
@@ -867,12 +933,23 @@
}
status_t Parcel::writeString16Vector(
+ const std::optional<std::vector<std::optional<String16>>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeString16);
+}
+
+status_t Parcel::writeString16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeString16);
}
status_t Parcel::writeUtf8VectorAsUtf16Vector(
+ const std::optional<std::vector<std::optional<std::string>>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
+status_t Parcel::writeUtf8VectorAsUtf16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) {
return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
}
@@ -1007,6 +1084,15 @@
return err;
}
+status_t Parcel::writeString16(const std::optional<String16>& str)
+{
+ if (!str) {
+ return writeInt32(-1);
+ }
+
+ return writeString16(*str);
+}
+
status_t Parcel::writeString16(const std::unique_ptr<String16>& str)
{
if (!str) {
@@ -1049,11 +1135,20 @@
return writeTypedVector(val, &Parcel::writeStrongBinder);
}
+status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
+}
+
status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
}
+status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
+}
+
status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const {
return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
}
@@ -1152,6 +1247,10 @@
return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor);
}
+status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
+}
+
status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) {
return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
}
@@ -1485,6 +1584,17 @@
return readByteVectorInternal(val, size);
}
+status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const {
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ if (!*val) {
+ // reserveOutVector does not create the out vector if size is < 0.
+ // This occurs when writing a null byte vector.
+ return OK;
+ }
+ return readByteVectorInternal(&**val, size);
+}
+
status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
size_t size;
if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1496,6 +1606,17 @@
return readByteVectorInternal(val->get(), size);
}
+status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const {
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ if (!*val) {
+ // reserveOutVector does not create the out vector if size is < 0.
+ // This occurs when writing a null byte vector.
+ return OK;
+ }
+ return readByteVectorInternal(&**val, size);
+}
+
status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const {
size_t size;
if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1507,6 +1628,10 @@
return readByteVectorInternal(val->get(), size);
}
+status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readInt32);
+}
+
status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readInt32);
}
@@ -1515,6 +1640,10 @@
return readTypedVector(val, &Parcel::readInt32);
}
+status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readInt64);
+}
+
status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readInt64);
}
@@ -1523,6 +1652,10 @@
return readTypedVector(val, &Parcel::readInt64);
}
+status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUint64);
+}
+
status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readUint64);
}
@@ -1531,6 +1664,10 @@
return readTypedVector(val, &Parcel::readUint64);
}
+status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readFloat);
+}
+
status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const {
return readNullableTypedVector(val, &Parcel::readFloat);
}
@@ -1539,6 +1676,10 @@
return readTypedVector(val, &Parcel::readFloat);
}
+status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readDouble);
+}
+
status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const {
return readNullableTypedVector(val, &Parcel::readDouble);
}
@@ -1547,6 +1688,28 @@
return readTypedVector(val, &Parcel::readDouble);
}
+status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->emplace();
+
+ status = readBoolVector(&**val);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
const int32_t start = dataPosition();
int32_t size;
@@ -1599,6 +1762,10 @@
return OK;
}
+status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readChar);
+}
+
status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readChar);
}
@@ -1608,6 +1775,11 @@
}
status_t Parcel::readString16Vector(
+ std::optional<std::vector<std::optional<String16>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readString16);
+}
+
+status_t Parcel::readString16Vector(
std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const {
return readNullableTypedVector(val, &Parcel::readString16);
}
@@ -1617,6 +1789,11 @@
}
status_t Parcel::readUtf8VectorFromUtf16Vector(
+ std::optional<std::vector<std::optional<std::string>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
+
+status_t Parcel::readUtf8VectorFromUtf16Vector(
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const {
return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
}
@@ -1808,6 +1985,21 @@
return NO_ERROR;
}
+status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const {
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ str->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ str->emplace();
+ return readUtf8FromUtf16(&**str);
+}
+
status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const {
const int32_t start = dataPosition();
int32_t size;
@@ -1886,6 +2078,29 @@
return String16();
}
+status_t Parcel::readString16(std::optional<String16>* pArg) const
+{
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ pArg->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ pArg->emplace();
+
+ status = readString16(&**pArg);
+
+ if (status != OK) {
+ pArg->reset();
+ }
+
+ return status;
+}
+
status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const
{
const int32_t start = dataPosition();
@@ -2091,6 +2306,10 @@
return OK;
}
+status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
+}
+
status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
}
@@ -2436,7 +2655,7 @@
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
- : continueWrite(newSize);
+ : continueWrite(std::max(newSize, (size_t) 128));
}
status_t Parcel::restartWrite(size_t desired)
@@ -2460,7 +2679,7 @@
releaseObjects();
- if (data) {
+ if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4b773e8..acc1e67 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -32,6 +32,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <mutex>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -73,38 +74,49 @@
sp<ProcessState> ProcessState::self()
{
- Mutex::Autolock _l(gProcessMutex);
- if (gProcess != nullptr) {
- return gProcess;
- }
- gProcess = new ProcessState(kDefaultDriver);
- return gProcess;
+ return init(kDefaultDriver, false /*requireDefault*/);
}
sp<ProcessState> ProcessState::initWithDriver(const char* driver)
{
- Mutex::Autolock _l(gProcessMutex);
- if (gProcess != nullptr) {
- // Allow for initWithDriver to be called repeatedly with the same
- // driver.
- if (!strcmp(gProcess->getDriverName().c_str(), driver)) {
- return gProcess;
- }
- LOG_ALWAYS_FATAL("ProcessState was already initialized.");
- }
-
- if (access(driver, R_OK) == -1) {
- ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
- driver = "/dev/binder";
- }
-
- gProcess = new ProcessState(driver);
- return gProcess;
+ return init(driver, true /*requireDefault*/);
}
sp<ProcessState> ProcessState::selfOrNull()
{
- Mutex::Autolock _l(gProcessMutex);
+ return init(nullptr, false /*requireDefault*/);
+}
+
+sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
+{
+ [[clang::no_destroy]] static sp<ProcessState> gProcess;
+ [[clang::no_destroy]] static std::mutex gProcessMutex;
+
+ if (driver == nullptr) {
+ std::lock_guard<std::mutex> l(gProcessMutex);
+ return gProcess;
+ }
+
+ [[clang::no_destroy]] static std::once_flag gProcessOnce;
+ std::call_once(gProcessOnce, [&](){
+ if (access(driver, R_OK) == -1) {
+ ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);
+ driver = "/dev/binder";
+ }
+
+ std::lock_guard<std::mutex> l(gProcessMutex);
+ gProcess = new ProcessState(driver);
+ });
+
+ if (requireDefault) {
+ // Detect if we are trying to initialize with a different driver, and
+ // consider that an error. ProcessState will only be initialized once above.
+ LOG_ALWAYS_FATAL_IF(gProcess->getDriverName() != driver,
+ "ProcessState was already initialized with %s,"
+ " can't initialize with %s.",
+ gProcess->getDriverName().c_str(), driver);
+ }
+
return gProcess;
}
@@ -328,6 +340,8 @@
}
status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
+ LOG_ALWAYS_FATAL_IF(mThreadPoolStarted && maxThreads < mMaxThreads,
+ "Binder threadpool cannot be shrunk after starting");
status_t result = NO_ERROR;
if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) {
mMaxThreads = maxThreads;
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index 779ed41..db0f1c7 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -68,9 +68,4 @@
TextOutput& aout(*new FdTextOutput(STDOUT_FILENO));
TextOutput& aerr(*new FdTextOutput(STDERR_FILENO));
-// ------------ ProcessState.cpp
-
-Mutex& gProcessMutex = *new Mutex;
-sp<ProcessState> gProcess;
-
} // namespace android
diff --git a/libs/binder/Static.h b/libs/binder/Static.h
index f8e0ee5..83524e8 100644
--- a/libs/binder/Static.h
+++ b/libs/binder/Static.h
@@ -27,8 +27,4 @@
// For TextStream.cpp
extern Vector<int32_t> gTextBuffers;
-// For ProcessState.cpp
-extern Mutex& gProcessMutex;
-extern sp<ProcessState> gProcess;
-
} // namespace android
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 674f065..64ab7a9 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -193,13 +193,15 @@
}
status_t status = parcel->writeInt32(mException);
- if (status != OK) { return status; }
+ if (status != OK) return status;
if (mException == EX_NONE) {
// We have no more information to write.
return status;
}
status = parcel->writeString16(String16(mMessage));
+ if (status != OK) return status;
status = parcel->writeInt32(0); // Empty remote stack trace header
+ if (status != OK) return status;
if (mException == EX_SERVICE_SPECIFIC) {
status = parcel->writeInt32(mErrorCode);
} else if (mException == EX_PARCELABLE) {
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 9aa7651..c232283 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -7,6 +7,9 @@
"name": "binderVendorDoubleLoadTest"
},
{
+ "name": "binderAllocationLimits"
+ },
+ {
"name": "binderDriverInterfaceTest"
},
{
@@ -29,6 +32,17 @@
},
{
"name": "libbinderthreadstateutils_test"
+ },
+ {
+ "name": "CtsOsTestCases",
+ "options": [
+ {
+ "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
+ },
+ {
+ "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
+ }
+ ]
}
]
}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
index 52c730c..8c0495c 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/fuzzer/binder.cpp
@@ -135,6 +135,7 @@
PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+ PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16),
[] (const ::android::Parcel& p, uint8_t /*data*/) {
FUZZ_LOG() << "about to read c-str";
const char* str = p.readCString();
@@ -143,6 +144,7 @@
PARCEL_READ_OPT_STATUS(android::String8, readString8),
PARCEL_READ_OPT_STATUS(android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+ PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
[] (const ::android::Parcel& p, uint8_t /*data*/) {
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
@@ -156,17 +158,22 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
// only reading one parcelable type for now
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
// PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+ PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable),
// only reading one binder type for now
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
@@ -174,30 +181,42 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
// PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
// PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
// PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
// PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
// PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
// PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
// PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
// PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
// PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
// PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
[] (const android::Parcel& p, uint8_t /*len*/) {
@@ -234,6 +253,7 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
// PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
[] (const android::Parcel& p, uint8_t len) {
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..7043b17 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -77,7 +77,7 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
-
+ bool setSchedPolicyCgroup(const int32_t tid, const int32_t group);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 6afcd77..6d04f13 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -21,6 +21,8 @@
#include <utils/threads.h>
+#include <optional>
+
#ifdef __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif
@@ -143,18 +145,18 @@
// const String16&) instead
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag, const String16& message);
+ const std::optional<String16>& attributionTag, const String16& message);
// @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
// const String16&) instead
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
+ bool startIfModeDefault, const std::optional<String16>& attributionTag,
const String16& message);
// @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag);
+ const std::optional<String16>& attributionTag);
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 74e52db..f3fea16 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -72,6 +72,22 @@
// This must be called before the object is sent to another process. Not thread safe.
void setExtension(const sp<IBinder>& extension);
+ // This must be called before the object is sent to another process. Not thread safe.
+ //
+ // This function will abort if improper parameters are set. This is like
+ // sched_setscheduler. However, it sets the minimum scheduling policy
+ // only for the duration that this specific binder object is handling the
+ // call in a threadpool. By default, this API is set to SCHED_NORMAL/0. In
+ // this case, the scheduling priority will not actually be modified from
+ // binder defaults. See also IPCThreadState::disableBackgroundScheduling.
+ //
+ // Appropriate values are:
+ // SCHED_NORMAL: -20 <= priority <= 19
+ // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99
+ void setMinSchedulerPolicy(int policy, int priority);
+ int getMinSchedulerPolicy();
+ int getMinSchedulerPriority();
+
pid_t getDebugPid();
protected:
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 8e871b8..378a911 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -137,7 +137,6 @@
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
ObjectManager mObjects;
- Parcel* mConstantData;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..fe58a41 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ SET_SCHED_POLICY_CGROUP_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 1ffb8de..a4a20c8 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -21,6 +21,8 @@
#include <binder/IAppOpsCallback.h>
#include <binder/IInterface.h>
+#include <optional>
+
#ifdef __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif
@@ -36,13 +38,13 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag,
+ const String16& packageName, const std::optional<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag) = 0;
+ const String16& packageName, const std::optional<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 64604b7..eea0e89 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -173,6 +173,10 @@
* The @a cookie is optional -- if non-NULL, it should be a
* memory address that you own (that is, you know it is unique).
*
+ * @note When all references to the binder being linked to are dropped, the
+ * recipient is automatically unlinked. So, you must hold onto a binder in
+ * order to receive death notifications about it.
+ *
* @note You will only receive death notifications for remote binders,
* as local binders by definition can't die without you dying as well.
* Trying to use this function on a local binder will result in an
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 3a401ad..33ee680 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -20,6 +20,8 @@
#include <binder/Binder.h>
+#include <assert.h>
+
namespace android {
// ----------------------------------------------------------------------
@@ -155,7 +157,11 @@
std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \
bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\
{ \
- if (!I##INTERFACE::default_impl && impl) { \
+ /* Only one user of this interface can use this function */ \
+ /* at a time. This is a heuristic to detect if two different */ \
+ /* users in the same process use this function. */ \
+ assert(!I##INTERFACE::default_impl); \
+ if (impl) { \
I##INTERFACE::default_impl = std::move(impl); \
return true; \
} \
@@ -241,8 +247,6 @@
"android.hardware.ISoundTriggerHwService",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
- "android.input.IInputFlinger",
- "android.input.ISetInputWindowsListener",
"android.media.IAudioFlinger",
"android.media.IAudioFlingerClient",
"android.media.IAudioPolicyService",
@@ -276,7 +280,6 @@
"android.os.IComplexTypeInterface",
"android.os.IPermissionController",
"android.os.IPingResponder",
- "android.os.IPowerManager",
"android.os.IProcessInfoService",
"android.os.ISchedulingPolicyService",
"android.os.IStringConstants",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 4818889..8d51cdc 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -39,12 +39,28 @@
status_t clearLastError();
+ /**
+ * Returns the PID of the process which has made the current binder
+ * call. If not in a binder call, this will return getpid. If the
+ * call is oneway, this will return 0.
+ */
pid_t getCallingPid() const;
- // nullptr if unavailable
- //
- // this can't be restored once it's cleared, and it does not return the
- // context of the current process when not in a binder call.
+
+ /**
+ * Returns the SELinux security identifier of the process which has
+ * made the current binder call. If not in a binder call this will
+ * return nullptr. If this isn't requested with
+ * Binder::setRequestingSid, it will also return nullptr.
+ *
+ * This can't be restored once it's cleared, and it does not return the
+ * context of the current process when not in a binder call.
+ */
const char* getCallingSid() const;
+
+ /**
+ * Returns the UID of the process which has made the current binder
+ * call. If not in a binder call, this will return 0.
+ */
uid_t getCallingUid() const;
void setStrictModePolicy(int32_t policy);
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 3fccddc..edada3d 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -57,14 +57,14 @@
virtual ~MemoryHeapBase();
/* implement IMemoryHeap interface */
- virtual int getHeapID() const;
+ int getHeapID() const override;
/* virtual address of the heap. returns MAP_FAILED in case of error */
- virtual void* getBase() const;
+ void* getBase() const override;
- virtual size_t getSize() const;
- virtual uint32_t getFlags() const;
- off_t getOffset() const override;
+ size_t getSize() const override;
+ uint32_t getFlags() const override;
+ off_t getOffset() const override;
const char* getDevice() const;
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
index b605bd3..a98583d 100644
--- a/libs/binder/include/binder/Nullable.h
+++ b/libs/binder/include/binder/Nullable.h
@@ -15,7 +15,7 @@
*/
#pragma once
-#include <memory>
+#include <optional>
#include <utility>
namespace android {
@@ -32,11 +32,11 @@
// c = std::move(a);
template <typename T>
-using nullable = std::unique_ptr<T>;
+using nullable = std::optional<T>;
template <typename T, typename... Args>
inline nullable<T> make_nullable(Args&&... args) {
- return std::make_unique<T>(std::forward<Args>(args)...);
+ return std::make_optional<T>(std::forward<Args>(args)...);
}
} // namespace aidl
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index c1f64fb..b6cfb8e 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -121,6 +121,7 @@
status_t writeString8(const String8& str);
status_t writeString8(const char* str, size_t len);
status_t writeString16(const String16& str);
+ status_t writeString16(const std::optional<String16>& str);
status_t writeString16(const std::unique_ptr<String16>& str);
status_t writeString16(const char16_t* str, size_t len);
status_t writeStrongBinder(const sp<IBinder>& val);
@@ -132,33 +133,48 @@
// Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
status_t writeUtf8AsUtf16(const std::string& str);
+ status_t writeUtf8AsUtf16(const std::optional<std::string>& str);
status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+ status_t writeByteVector(const std::optional<std::vector<int8_t>>& val);
status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
status_t writeByteVector(const std::vector<int8_t>& val);
+ status_t writeByteVector(const std::optional<std::vector<uint8_t>>& val);
status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
status_t writeByteVector(const std::vector<uint8_t>& val);
+ status_t writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
status_t writeInt32Vector(const std::vector<int32_t>& val);
+ status_t writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
status_t writeInt64Vector(const std::vector<int64_t>& val);
+ status_t writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val);
status_t writeUint64Vector(const std::vector<uint64_t>& val);
+ status_t writeFloatVector(const std::optional<std::vector<float>>& val);
status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
status_t writeFloatVector(const std::vector<float>& val);
+ status_t writeDoubleVector(const std::optional<std::vector<double>>& val);
status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
status_t writeDoubleVector(const std::vector<double>& val);
+ status_t writeBoolVector(const std::optional<std::vector<bool>>& val);
status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
status_t writeBoolVector(const std::vector<bool>& val);
+ status_t writeCharVector(const std::optional<std::vector<char16_t>>& val);
status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
status_t writeCharVector(const std::vector<char16_t>& val);
status_t writeString16Vector(
+ const std::optional<std::vector<std::optional<String16>>>& val);
+ status_t writeString16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
status_t writeString16Vector(const std::vector<String16>& val);
status_t writeUtf8VectorAsUtf16Vector(
+ const std::optional<std::vector<std::optional<std::string>>>& val);
+ status_t writeUtf8VectorAsUtf16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
+ status_t writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
@@ -167,14 +183,20 @@
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::vector<T>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t writeEnumVector(const std::optional<std::vector<T>>& val);
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
// Write an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::vector<T>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t writeEnumVector(const std::optional<std::vector<T>>& val);
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
template<typename T>
+ status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val);
+ template<typename T>
status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
template<typename T>
status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
@@ -182,6 +204,8 @@
status_t writeParcelableVector(const std::vector<T>& val);
template<typename T>
+ status_t writeNullableParcelable(const std::optional<T>& parcelable);
+ template<typename T>
status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable);
status_t writeParcelable(const Parcelable& parcelable);
@@ -195,6 +219,8 @@
template<typename T>
status_t writeVectorSize(const std::vector<T>& val);
template<typename T>
+ status_t writeVectorSize(const std::optional<std::vector<T>>& val);
+ template<typename T>
status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
// Place a native_handle into the parcel (the native_handle's file-
@@ -230,6 +256,8 @@
// Place a vector of file desciptors into the parcel. Each descriptor is
// dup'd as in writeDupFileDescriptor
status_t writeUniqueFileDescriptorVector(
+ const std::optional<std::vector<base::unique_fd>>& val);
+ status_t writeUniqueFileDescriptorVector(
const std::unique_ptr<std::vector<base::unique_fd>>& val);
status_t writeUniqueFileDescriptorVector(
const std::vector<base::unique_fd>& val);
@@ -279,6 +307,7 @@
// Read a UTF16 encoded string, convert to UTF8
status_t readUtf8FromUtf16(std::string* str) const;
+ status_t readUtf8FromUtf16(std::optional<std::string>* str) const;
status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
const char* readCString() const;
@@ -287,27 +316,34 @@
const char* readString8Inplace(size_t* outLen) const;
String16 readString16() const;
status_t readString16(String16* pArg) const;
+ status_t readString16(std::optional<String16>* pArg) const;
status_t readString16(std::unique_ptr<String16>* pArg) const;
const char16_t* readString16Inplace(size_t* outLen) const;
sp<IBinder> readStrongBinder() const;
status_t readStrongBinder(sp<IBinder>* val) const;
status_t readNullableStrongBinder(sp<IBinder>* val) const;
-
// Read an Enum vector with underlying type int8_t.
// Does not use padding; each byte is contiguous.
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::vector<T>* val) const;
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t readEnumVector(std::optional<std::vector<T>>* val) const;
// Read an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::vector<T>* val) const;
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t readEnumVector(std::optional<std::vector<T>>* val) const;
template<typename T>
status_t readParcelableVector(
+ std::optional<std::vector<std::optional<T>>>* val) const;
+ template<typename T>
+ status_t readParcelableVector(
std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
template<typename T>
status_t readParcelableVector(std::vector<T>* val) const;
@@ -315,6 +351,8 @@
status_t readParcelable(Parcelable* parcelable) const;
template<typename T>
+ status_t readParcelable(std::optional<T>* parcelable) const;
+ template<typename T>
status_t readParcelable(std::unique_ptr<T>* parcelable) const;
template<typename T>
@@ -323,31 +361,45 @@
template<typename T>
status_t readNullableStrongBinder(sp<T>* val) const;
+ status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+ status_t readByteVector(std::optional<std::vector<int8_t>>* val) const;
status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
status_t readByteVector(std::vector<int8_t>* val) const;
+ status_t readByteVector(std::optional<std::vector<uint8_t>>* val) const;
status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
status_t readByteVector(std::vector<uint8_t>* val) const;
+ status_t readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
status_t readInt32Vector(std::vector<int32_t>* val) const;
+ status_t readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
status_t readInt64Vector(std::vector<int64_t>* val) const;
+ status_t readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const;
status_t readUint64Vector(std::vector<uint64_t>* val) const;
+ status_t readFloatVector(std::optional<std::vector<float>>* val) const;
status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
status_t readFloatVector(std::vector<float>* val) const;
+ status_t readDoubleVector(std::optional<std::vector<double>>* val) const;
status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
status_t readDoubleVector(std::vector<double>* val) const;
+ status_t readBoolVector(std::optional<std::vector<bool>>* val) const;
status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
status_t readBoolVector(std::vector<bool>* val) const;
+ status_t readCharVector(std::optional<std::vector<char16_t>>* val) const;
status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
status_t readCharVector(std::vector<char16_t>* val) const;
status_t readString16Vector(
+ std::optional<std::vector<std::optional<String16>>>* val) const;
+ status_t readString16Vector(
std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
status_t readString16Vector(std::vector<String16>* val) const;
status_t readUtf8VectorFromUtf16Vector(
+ std::optional<std::vector<std::optional<std::string>>>* val) const;
+ status_t readUtf8VectorFromUtf16Vector(
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
@@ -360,10 +412,15 @@
template<typename T>
status_t resizeOutVector(std::vector<T>* val) const;
template<typename T>
+ status_t resizeOutVector(std::optional<std::vector<T>>* val) const;
+ template<typename T>
status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
template<typename T>
status_t reserveOutVector(std::vector<T>* val, size_t* size) const;
template<typename T>
+ status_t reserveOutVector(std::optional<std::vector<T>>* val,
+ size_t* size) const;
+ template<typename T>
status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val,
size_t* size) const;
@@ -399,6 +456,8 @@
// Retrieve a vector of smart file descriptors from the parcel.
status_t readUniqueFileDescriptorVector(
+ std::optional<std::vector<base::unique_fd>>* val) const;
+ status_t readUniqueFileDescriptorVector(
std::unique_ptr<std::vector<base::unique_fd>>* val) const;
status_t readUniqueFileDescriptorVector(
std::vector<base::unique_fd>* val) const;
@@ -492,6 +551,9 @@
status_t unsafeReadTypedVector(std::vector<T>* val,
status_t(Parcel::*read_func)(U*) const) const;
template<typename T>
+ status_t readNullableTypedVector(std::optional<std::vector<T>>* val,
+ status_t(Parcel::*read_func)(T*) const) const;
+ template<typename T>
status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
status_t(Parcel::*read_func)(T*) const) const;
template<typename T>
@@ -501,9 +563,15 @@
status_t unsafeWriteTypedVector(const std::vector<T>& val,
status_t(Parcel::*write_func)(U));
template<typename T>
+ status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(const T&));
+ template<typename T>
status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(const T&));
template<typename T>
+ status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(T));
+ template<typename T>
status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(T));
template<typename T>
@@ -691,6 +759,15 @@
}
template<typename T>
+status_t Parcel::writeVectorSize(const std::optional<std::vector<T>>& val) {
+ if (!val) {
+ return writeInt32(-1);
+ }
+
+ return writeVectorSize(*val);
+}
+
+template<typename T>
status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) {
if (!val) {
return writeInt32(-1);
@@ -715,6 +792,22 @@
}
template<typename T>
+status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
+ int32_t size;
+ status_t err = readInt32(&size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ val->reset();
+ if (size >= 0) {
+ val->emplace(size_t(size));
+ }
+
+ return OK;
+}
+
+template<typename T>
status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
int32_t size;
status_t err = readInt32(&size);
@@ -747,6 +840,25 @@
}
template<typename T>
+status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
+ int32_t read_size;
+ status_t err = readInt32(&read_size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (read_size >= 0) {
+ *size = static_cast<size_t>(read_size);
+ val->emplace();
+ (*val)->reserve(*size);
+ } else {
+ val->reset();
+ }
+
+ return OK;
+}
+
+template<typename T>
status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
size_t* size) const {
int32_t read_size;
@@ -841,6 +953,30 @@
}
template<typename T>
+status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val,
+ status_t(Parcel::*read_func)(T*) const) const {
+ const size_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->emplace();
+
+ status = unsafeReadTypedVector(&**val, read_func);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
status_t(Parcel::*read_func)(T*) const) const {
const size_t start = dataPosition();
@@ -901,6 +1037,16 @@
}
template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(const T&)) {
+ if (!val) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(const T&)) {
if (val.get() == nullptr) {
@@ -911,6 +1057,16 @@
}
template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(T)) {
+ if (!val) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(T)) {
if (val.get() == nullptr) {
@@ -926,6 +1082,30 @@
}
template<typename T>
+status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
+ const size_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->emplace();
+
+ using NullableT = std::optional<T>;
+ status = unsafeReadTypedVector<NullableT, NullableT>(&**val, &Parcel::readParcelable);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
const size_t start = dataPosition();
int32_t size;
@@ -939,7 +1119,8 @@
setDataPosition(start);
val->reset(new std::vector<std::unique_ptr<T>>());
- status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
+ using NullableT = std::unique_ptr<T>;
+ status = unsafeReadTypedVector<NullableT, NullableT>(val->get(), &Parcel::readParcelable);
if (status != OK) {
val->reset();
@@ -949,6 +1130,29 @@
}
template<typename T>
+status_t Parcel::readParcelable(std::optional<T>* parcelable) const {
+ const size_t start = dataPosition();
+ int32_t present;
+ status_t status = readInt32(&present);
+ parcelable->reset();
+
+ if (status != OK || !present) {
+ return status;
+ }
+
+ setDataPosition(start);
+ parcelable->emplace();
+
+ status = readParcelable(&**parcelable);
+
+ if (status != OK) {
+ parcelable->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
const size_t start = dataPosition();
int32_t present;
@@ -972,6 +1176,11 @@
}
template<typename T>
+status_t Parcel::writeNullableParcelable(const std::optional<T>& parcelable) {
+ return writeRawNullableParcelable(parcelable ? &*parcelable : nullptr);
+}
+
+template<typename T>
status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
return writeRawNullableParcelable(parcelable.get());
}
@@ -982,6 +1191,16 @@
}
template<typename T>
+status_t Parcel::writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) {
+ if (!val) {
+ return this->writeInt32(-1);
+ }
+
+ using NullableT = std::optional<T>;
+ return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
+}
+
+template<typename T>
status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
if (val.get() == nullptr) {
return this->writeInt32(-1);
@@ -996,7 +1215,8 @@
return this->writeInt32(-1);
}
- return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+ using NullableT = std::unique_ptr<T>;
+ return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
}
template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
@@ -1013,6 +1233,11 @@
return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
+ if (!val) return writeInt32(-1);
+ return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
if (!val) return writeInt32(-1);
return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
@@ -1022,6 +1247,10 @@
return writeTypedVector(val, &Parcel::writeEnum);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeEnum);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
return writeNullableTypedVector(val, &Parcel::writeEnum);
}
@@ -1053,6 +1282,17 @@
return readByteVectorInternal(val, size);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ if (!*val) {
+ // reserveOutVector does not create the out vector if size is < 0.
+ // This occurs when writing a null Enum vector.
+ return OK;
+ }
+ return readByteVectorInternal(&**val, size);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
size_t size;
if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1068,6 +1308,10 @@
return readTypedVector(val, &Parcel::readEnum);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readEnum);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
return readNullableTypedVector(val, &Parcel::readEnum);
}
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 4635ad8..71e1d3c 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -31,7 +31,8 @@
public:
ParcelFileDescriptor();
explicit ParcelFileDescriptor(android::base::unique_fd fd);
- ParcelFileDescriptor(ParcelFileDescriptor&& other) : mFd(std::move(other.mFd)) { }
+ ParcelFileDescriptor(ParcelFileDescriptor&& other) noexcept : mFd(std::move(other.mFd)) { }
+ ParcelFileDescriptor& operator=(ParcelFileDescriptor&& other) noexcept = default;
~ParcelFileDescriptor() override;
int get() const { return mFd.get(); }
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index a9166e2..83c2f19 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -52,6 +52,21 @@
//
// Returns android::OK on success and an appropriate error otherwise.
virtual status_t readFromParcel(const Parcel* parcel) = 0;
+
+ // WARNING: for use by auto-generated code only (AIDL). Should not be used
+ // manually, or there is a risk of breaking CTS, GTS, VTS, or CTS-on-GSI
+ // tests.
+ enum class Stability {
+ STABILITY_LOCAL,
+ STABILITY_VINTF, // corresponds to @VintfStability
+ };
+
+ // 'Stable' means this parcelable is guaranteed to be stable for multiple
+ // years.
+ // It must be guaranteed by setting stability field in aidl_interface.
+ // WARNING: getStability() is only expected to be overridden by auto-generated
+ // code. Returns true if this parcelable is stable.
+ virtual Stability getStability() const { return Stability::STABILITY_LOCAL; }
}; // class Parcelable
#if defined(__clang__)
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index e57ff1c..9f5346a 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -42,6 +42,8 @@
* any call to ProcessState::self(). The default is /dev/vndbinder
* for processes built with the VNDK and /dev/binder for those
* which are not.
+ *
+ * If this is called with nullptr, the behavior is the same as selfOrNull.
*/
static sp<ProcessState> initWithDriver(const char *driver);
@@ -90,6 +92,8 @@
void setCallRestriction(CallRestriction restriction);
private:
+ static sp<ProcessState> init(const char *defaultDriver, bool requireDefault);
+
friend class IPCThreadState;
explicit ProcessState(const char* driver);
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index f66406f..c7e1e14 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -50,12 +50,18 @@
// ---------------------------------------------------------------------------
+// DO NOT USE: prefer libutils/libbase logs, which don't require static data to
+// be allocated.
// Text output stream for printing to the log (via utils/Log.h).
extern TextOutput& alog;
+// DO NOT USE: prefer libutils/libbase logs, which don't require static data to
+// be allocated.
// Text output stream for printing to stdout.
extern TextOutput& aout;
+// DO NOT USE: prefer libutils/libbase logs, which don't require static data to
+// be allocated.
// Text output stream for printing to stderr.
extern TextOutput& aerr;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index b37db43..4fd0657 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -36,6 +36,7 @@
host_supported: true,
export_include_dirs: [
+ "include_cpp",
"include_ndk",
"include_platform",
],
@@ -101,6 +102,17 @@
license: "NOTICE",
}
+// TODO(b/160624671): package with the aidl compiler
+ndk_headers {
+ name: "libbinder_ndk_helper_headers",
+ from: "include_cpp/android",
+ to: "android",
+ srcs: [
+ "include_cpp/android/*.h",
+ ],
+ license: "NOTICE",
+}
+
ndk_library {
name: "libbinder_ndk",
symbol_file: "libbinder_ndk.map.txt",
@@ -111,6 +123,7 @@
name: "libbinder_ndk",
symbol_file: "libbinder_ndk.map.txt",
export_include_dirs: [
+ "include_cpp",
"include_ndk",
"include_platform",
],
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 649faa1..d287290 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -15,6 +15,7 @@
*/
#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
#include "ibinder_internal.h"
#include <android/binder_stability.h>
@@ -99,8 +100,14 @@
String8 descriptor(getBinder()->getInterfaceDescriptor());
if (descriptor != newDescriptor) {
- LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
- << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+ if (getBinder()->isBinderAlive()) {
+ LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
+ << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+ } else {
+ // b/155793159
+ LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor.c_str()
+ << "' to dead binder.";
+ }
return false;
}
@@ -676,3 +683,29 @@
rawBinder->setExtension(ext->getBinder());
return STATUS_OK;
}
+
+// platform methods follow
+
+void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) {
+ ABBinder* localBinder = binder->asABBinder();
+ if (localBinder == nullptr) {
+ LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder";
+ }
+
+ localBinder->setRequestingSid(requestingSid);
+}
+
+const char* AIBinder_getCallingSid() {
+ return ::android::IPCThreadState::self()->getCallingSid();
+}
+
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder) {
+ if (binder == nullptr) return nullptr;
+ return binder->getBinder();
+}
+
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder) {
+ sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder_incStrong(ndkBinder.get());
+ return ndkBinder.get();
+}
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
similarity index 100%
rename from libs/binder/ndk/include_ndk/android/binder_auto_utils.h
rename to libs/binder/ndk/include_cpp/android/binder_auto_utils.h
diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_cpp/android/binder_enums.h
similarity index 100%
rename from libs/binder/ndk/include_ndk/android/binder_enums.h
rename to libs/binder/ndk/include_cpp/android/binder_enums.h
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
similarity index 100%
rename from libs/binder/ndk/include_ndk/android/binder_interface_utils.h
rename to libs/binder/ndk/include_cpp/android/binder_interface_utils.h
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
similarity index 98%
rename from libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
rename to libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index df5df13..09949ea 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -831,34 +831,34 @@
}
/**
- * Writes a vector of int8_t to the next location in a non-null parcel.
+ * Writes a vector of uint8_t to the next location in a non-null parcel.
*/
-inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int8_t>& vec) {
- return AParcel_writeByteArray(parcel, vec.data(), vec.size());
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint8_t>& vec) {
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), vec.size());
}
/**
- * Writes an optional vector of int8_t to the next location in a non-null parcel.
+ * Writes an optional vector of uint8_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel,
- const std::optional<std::vector<int8_t>>& vec) {
+ const std::optional<std::vector<uint8_t>>& vec) {
if (!vec) return AParcel_writeByteArray(parcel, nullptr, -1);
return AParcel_writeVector(parcel, *vec);
}
/**
- * Reads a vector of int8_t from the next location in a non-null parcel.
+ * Reads a vector of uint8_t from the next location in a non-null parcel.
*/
-inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int8_t>* vec) {
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint8_t>* vec) {
void* vectorData = static_cast<void*>(vec);
return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>);
}
/**
- * Reads an optional vector of int8_t from the next location in a non-null parcel.
+ * Reads an optional vector of uint8_t from the next location in a non-null parcel.
*/
inline binder_status_t AParcel_readVector(const AParcel* parcel,
- std::optional<std::vector<int8_t>>* vec) {
+ std::optional<std::vector<uint8_t>>* vec) {
void* vectorData = static_cast<void*>(vec);
return AParcel_readByteArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<int8_t>);
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4560f22..33763d5 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -26,6 +26,7 @@
#pragma once
+#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -407,6 +408,8 @@
* This returns true if the class association succeeds. If it fails, no change is made to the
* binder object.
*
+ * Warning: this may fail if the binder is dead.
+ *
* Available since API level 29.
*
* \param binder the object to attach the class to.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 86b75b8..a031e29 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,6 +26,7 @@
#pragma once
+#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index ab9a144..3a55f94 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -26,6 +26,7 @@
#pragma once
#include <errno.h>
+#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
new file mode 100644
index 0000000..d4feaba
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+#include <binder/IBinder.h>
+#endif
+
+__BEGIN_DECLS
+
+/**
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
+ *
+ * \param binder local server binder to request security contexts on
+ */
+void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) __INTRODUCED_IN(31);
+
+/**
+ * Returns the selinux context of the callee.
+ *
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
+ *
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
+ */
+__attribute__((warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
+
+__END_DECLS
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+/**
+ * Get libbinder version of binder from AIBinder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder binder with ownership retained by the client
+ * \return platform binder object
+ */
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+
+/**
+ * Get libbinder_ndk version of binder from platform binder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder platform binder which may be from anywhere (doesn't have to be
+ * created with libbinder_ndK)
+ * \return binder with one reference count of ownership given to the client. See
+ * AIBinder_decStrong
+ */
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+
+#endif
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index ac46cb8..114a781 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -20,6 +20,10 @@
__BEGIN_DECLS
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error this is only for platform code
+#endif
+
/**
* Gets whether or not FDs are allowed by this AParcel
*
@@ -29,4 +33,4 @@
*/
bool AParcel_getAllowFds(const AParcel*);
-__END_DECLS
\ No newline at end of file
+__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index a9eba47..9b5fa26 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -95,8 +95,6 @@
AServiceManager_addService; # apex llndk
AServiceManager_checkService; # apex llndk
AServiceManager_getService; # apex llndk
- local:
- *;
};
LIBBINDER_NDK30 { # introduced=30
@@ -111,11 +109,21 @@
AIBinder_markVendorStability; # llndk
AIBinder_markVintfStability; # apex llndk
AIBinder_Class_setHandleShellCommand; # apex llndk
- local:
- *;
+};
+
+LIBBINDER_NDK31 { # introduced=31
+ global:
+ AIBinder_getCallingSid; # apex
+ AIBinder_setRequestingSid; # apex
};
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
+ extern "C++" {
+ AIBinder_fromPlatformBinder*;
+ AIBinder_toPlatformBinder*;
+ };
+ local:
+ *;
};
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/tests/Android.bp
similarity index 92%
rename from libs/binder/ndk/test/Android.bp
rename to libs/binder/ndk/tests/Android.bp
index 5f5265c..7c271f6 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -40,6 +40,7 @@
cc_defaults {
name: "test_libbinder_ndk_test_defaults",
defaults: ["test_libbinder_ndk_defaults"],
+ // critical that libbinder/libbinder_ndk are shared for VTS
shared_libs: [
"libandroid_runtime_lazy",
"libbase",
@@ -63,7 +64,7 @@
"IBinderNdkUnitTest-cpp",
"IBinderNdkUnitTest-ndk_platform",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts"],
require_root: true,
// force since binderVendorDoubleLoadTest has its own
@@ -81,13 +82,14 @@
"IBinderVendorDoubleLoadTest-ndk_platform",
"libbinder_aidl_test_stub-ndk_platform",
],
+ // critical that libbinder/libbinder_ndk are shared for VTS
shared_libs: [
"libbase",
"libbinder",
"libbinder_ndk",
"libutils",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts"],
}
aidl_interface {
diff --git a/libs/binder/ndk/test/AndroidTest.xml b/libs/binder/ndk/tests/AndroidTest.xml
similarity index 100%
rename from libs/binder/ndk/test/AndroidTest.xml
rename to libs/binder/ndk/tests/AndroidTest.xml
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
similarity index 93%
rename from libs/binder/ndk/test/IBinderNdkUnitTest.aidl
rename to libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
index 6e8e463..dc77467 100644
--- a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
+++ b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
@@ -22,6 +22,10 @@
import IEmpty;
interface IBinderNdkUnitTest {
+ int repeatInt(int a);
+
void takeInterface(IEmpty test);
void forceFlushCommands();
+
+ boolean getsRequestedSid();
}
diff --git a/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl b/libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
similarity index 100%
rename from libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl
rename to libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/tests/IEmpty.aidl
similarity index 100%
rename from libs/binder/ndk/test/IEmpty.aidl
rename to libs/binder/ndk/tests/IEmpty.aidl
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
similarity index 100%
rename from libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
rename to libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/tests/iface.cpp
similarity index 100%
rename from libs/binder/ndk/test/iface.cpp
rename to libs/binder/ndk/tests/iface.cpp
diff --git a/libs/binder/ndk/test/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
similarity index 100%
rename from libs/binder/ndk/test/include/iface/iface.h
rename to libs/binder/ndk/tests/include/iface/iface.h
diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
similarity index 87%
rename from libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
rename to libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index fd30d87..e3fdb4b 100644
--- a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -19,6 +19,7 @@
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
#include <android/binder_ibinder_jni.h>
+#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
@@ -34,6 +35,7 @@
#include <sys/prctl.h>
#include <chrono>
#include <condition_variable>
+#include <iostream>
#include <mutex>
using namespace android;
@@ -42,6 +44,10 @@
constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+ ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) {
+ *out = in;
+ return ndk::ScopedAStatus::ok();
+ }
ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
(void)empty;
return ndk::ScopedAStatus::ok();
@@ -52,6 +58,12 @@
android::IPCThreadState::self()->flushCommands();
return ndk::ScopedAStatus::ok();
}
+ ndk::ScopedAStatus getsRequestedSid(bool* out) {
+ const char* sid = AIBinder_getCallingSid();
+ std::cout << "Got security context: " << (sid ?: "null") << std::endl;
+ *out = sid != nullptr;
+ return ndk::ScopedAStatus::ok();
+ }
binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args,
uint32_t numArgs) override {
for (uint32_t i = 0; i < numArgs; i++) {
@@ -66,8 +78,11 @@
ABinderProcess_setThreadPoolMaxThreadCount(0);
auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
- binder_status_t status =
- AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+ auto binder = service->asBinder();
+
+ AIBinder_setRequestingSid(binder.get(), true);
+
+ binder_status_t status = AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
if (status != STATUS_OK) {
LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
@@ -274,6 +289,16 @@
EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}
+TEST(NdkBinder, RequestedSidWorks) {
+ ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+ bool gotSid = false;
+ EXPECT_TRUE(service->getsRequestedSid(&gotSid).isOk());
+ EXPECT_TRUE(gotSid);
+}
+
TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
static volatile bool destroyed = false;
static std::mutex dMutex;
@@ -308,6 +333,30 @@
EXPECT_TRUE(destroyed);
}
+TEST(NdkBinder, ConvertToPlatformBinder) {
+ for (const ndk::SpAIBinder& binder :
+ {// remote
+ ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+ // local
+ ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
+ // convert to platform binder
+ EXPECT_NE(binder.get(), nullptr);
+ sp<IBinder> platformBinder = AIBinder_toPlatformBinder(binder.get());
+ EXPECT_NE(platformBinder.get(), nullptr);
+ auto proxy = interface_cast<IBinderNdkUnitTest>(platformBinder);
+ EXPECT_NE(proxy, nullptr);
+
+ // use platform binder
+ int out;
+ EXPECT_TRUE(proxy->repeatInt(4, &out).isOk());
+ EXPECT_EQ(out, 4);
+
+ // convert back
+ ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder));
+ EXPECT_EQ(backBinder.get(), binder.get());
+ }
+}
+
class MyResultReceiver : public BnResultReceiver {
public:
Mutex mMutex;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 69fdd7c..a03835b 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -27,7 +27,9 @@
defaults: ["binder_test_defaults"],
srcs: ["binderDriverInterfaceTest.cpp"],
compile_multilib: "32",
+ multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
+ test_suites: ["vts"],
}
cc_test {
@@ -52,7 +54,10 @@
"libutils",
],
compile_multilib: "32",
+ multilib: { lib32: { suffix: "" } },
cflags: ["-DBINDER_IPC_32BIT=1"],
+ test_suites: ["vts"],
+ require_root: true,
}
cc_test {
@@ -150,6 +155,7 @@
"binderStabilityTest.cpp",
],
+ // critical that libbinder/libbinder_ndk are shared for VTS
shared_libs: [
"libbinder_ndk",
"libbinder",
@@ -161,6 +167,21 @@
"binderStabilityTestIface-ndk_platform",
],
+ test_suites: ["device-tests", "vts"],
+ require_root: true,
+}
+
+cc_test {
+ name: "binderAllocationLimits",
+ defaults: ["binder_test_defaults"],
+ srcs: ["binderAllocationLimits.cpp"],
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libutilscallstack",
+ "libbase",
+ ],
test_suites: ["device-tests"],
require_root: true,
}
diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h
new file mode 100644
index 0000000..369b55d
--- /dev/null
+++ b/libs/binder/tests/binderAbiHelper.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <iostream>
+
+#ifdef BINDER_IPC_32BIT
+static constexpr bool kBuild32Abi = true;
+#else
+static constexpr bool kBuild32Abi = false;
+#endif
+
+// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported
+static inline bool ReadKernelConfigIs32BitAbi() {
+ // failure case implies we run with standard ABI
+ return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\"");
+}
+
+static inline void ExitIfWrongAbi() {
+ bool runtime32Abi = ReadKernelConfigIs32BitAbi();
+
+ if (kBuild32Abi != runtime32Abi) {
+ std::cout << "[==========] Running 1 test from 1 test suite." << std::endl;
+ std::cout << "[----------] Global test environment set-up." << std::endl;
+ std::cout << "[----------] 1 tests from BinderLibTest" << std::endl;
+ std::cout << "[ RUN ] BinderTest.AbortForWrongAbi" << std::endl;
+ std::cout << "[ INFO ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl;
+ std::cout << "[ OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl;
+ std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "[----------] Global test environment tear-down" << std::endl;
+ std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl;
+ std::cout << "[ PASSED ] 1 tests." << std::endl;
+ exit(0);
+ }
+}
+
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
new file mode 100644
index 0000000..e1f5ed5
--- /dev/null
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 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 <android-base/logging.h>
+#include <binder/Parcel.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <utils/CallStack.h>
+
+#include <malloc.h>
+#include <functional>
+#include <vector>
+
+struct DestructionAction {
+ DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
+ ~DestructionAction() { mF(); };
+private:
+ std::function<void()> mF;
+};
+
+// Group of hooks
+struct MallocHooks {
+ decltype(__malloc_hook) malloc_hook;
+ decltype(__realloc_hook) realloc_hook;
+
+ static MallocHooks save() {
+ return {
+ .malloc_hook = __malloc_hook,
+ .realloc_hook = __realloc_hook,
+ };
+ }
+
+ void overwrite() const {
+ __malloc_hook = malloc_hook;
+ __realloc_hook = realloc_hook;
+ }
+};
+
+static const MallocHooks orig_malloc_hooks = MallocHooks::save();
+
+// When malloc is hit, executes lambda.
+namespace LambdaHooks {
+ using AllocationHook = std::function<void(size_t)>;
+ static std::vector<AllocationHook> lambdas = {};
+
+ static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg);
+ static void* lambda_malloc_hook(size_t bytes, const void* arg);
+
+ static const MallocHooks lambda_malloc_hooks = {
+ .malloc_hook = lambda_malloc_hook,
+ .realloc_hook = lambda_realloc_hook,
+ };
+
+ static void* lambda_malloc_hook(size_t bytes, const void* arg) {
+ {
+ orig_malloc_hooks.overwrite();
+ lambdas.at(lambdas.size() - 1)(bytes);
+ lambda_malloc_hooks.overwrite();
+ }
+ return orig_malloc_hooks.malloc_hook(bytes, arg);
+ }
+
+ static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) {
+ {
+ orig_malloc_hooks.overwrite();
+ lambdas.at(lambdas.size() - 1)(bytes);
+ lambda_malloc_hooks.overwrite();
+ }
+ return orig_malloc_hooks.realloc_hook(ptr, bytes, arg);
+ }
+
+}
+
+// Action to execute when malloc is hit. Supports nesting. Malloc is not
+// restricted when the allocation hook is being processed.
+__attribute__((warn_unused_result))
+DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
+ MallocHooks before = MallocHooks::save();
+ LambdaHooks::lambdas.emplace_back(std::move(f));
+ LambdaHooks::lambda_malloc_hooks.overwrite();
+ return DestructionAction([before]() {
+ before.overwrite();
+ LambdaHooks::lambdas.pop_back();
+ });
+}
+
+// exported symbol, to force compiler not to optimize away pointers we set here
+const void* imaginary_use;
+
+TEST(TestTheTest, OnMalloc) {
+ size_t mallocs = 0;
+ {
+ const auto on_malloc = OnMalloc([&](size_t bytes) {
+ mallocs++;
+ EXPECT_EQ(bytes, 40);
+ });
+
+ imaginary_use = new int[10];
+ }
+ EXPECT_EQ(mallocs, 1);
+}
+
+
+__attribute__((warn_unused_result))
+DestructionAction ScopeDisallowMalloc() {
+ return OnMalloc([&](size_t bytes) {
+ ADD_FAILURE() << "Unexpected allocation: " << bytes;
+ using android::CallStack;
+ std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
+ << std::endl;
+ });
+}
+
+using android::IBinder;
+using android::Parcel;
+using android::String16;
+using android::defaultServiceManager;
+using android::sp;
+using android::IServiceManager;
+
+static sp<IBinder> GetRemoteBinder() {
+ // This gets binder representing the service manager
+ // the current IServiceManager API doesn't expose the binder, and
+ // I want to avoid adding usages of the AIDL generated interface it
+ // is using underneath, so to avoid people copying it.
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager"));
+ EXPECT_NE(nullptr, binder);
+ return binder;
+}
+
+TEST(BinderAllocation, ParcelOnStack) {
+ const auto m = ScopeDisallowMalloc();
+ Parcel p;
+ imaginary_use = p.data();
+}
+
+TEST(BinderAllocation, GetServiceManager) {
+ defaultServiceManager(); // first call may alloc
+ const auto m = ScopeDisallowMalloc();
+ defaultServiceManager();
+}
+
+// note, ping does not include interface descriptor
+TEST(BinderAllocation, PingTransaction) {
+ sp<IBinder> a_binder = GetRemoteBinder();
+ const auto m = ScopeDisallowMalloc();
+ a_binder->pingBinder();
+}
+
+TEST(BinderAllocation, SmallTransaction) {
+ String16 empty_descriptor = String16("");
+ sp<IServiceManager> manager = defaultServiceManager();
+
+ size_t mallocs = 0;
+ const auto on_malloc = OnMalloc([&](size_t bytes) {
+ mallocs++;
+ // Parcel should allocate a small amount by default
+ EXPECT_EQ(bytes, 128);
+ });
+ manager->checkService(empty_descriptor);
+
+ EXPECT_EQ(mallocs, 1);
+}
+
+int main(int argc, char** argv) {
+ if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
+ CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
+ execv(argv[0], argv);
+ return 1;
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index f3ed6a6..8cc3054 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -25,6 +25,8 @@
#include <sys/mman.h>
#include <poll.h>
+#include "binderAbiHelper.h"
+
#define BINDER_DEV_NAME "/dev/binder"
testing::Environment* binder_env;
@@ -361,6 +363,7 @@
}
int main(int argc, char **argv) {
+ ExitIfWrongAbi();
::testing::InitGoogleTest(&argc, argv);
binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv());
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e343df7..917751e 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -32,6 +32,8 @@
#include <sys/epoll.h>
#include <sys/prctl.h>
+#include "binderAbiHelper.h"
+
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
@@ -48,6 +50,9 @@
static char *binderserversuffix;
static char binderserverarg[] = "--binderserver";
+static constexpr int kSchedPolicy = SCHED_RR;
+static constexpr int kSchedPriority = 7;
+
static String16 binderLibTestServiceName = String16("test.binderLib");
enum BinderLibTestTranscationCode {
@@ -73,6 +78,7 @@
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
+ BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_BUF,
};
@@ -1013,6 +1019,22 @@
EXPECT_EQ(NO_ERROR, ret2);
}
+TEST_F(BinderLibTest, SchedPolicySet) {
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ Parcel data, reply;
+ status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ int policy = reply.readInt32();
+ int priority = reply.readInt32();
+
+ EXPECT_EQ(kSchedPolicy, policy & (~SCHED_RESET_ON_FORK));
+ EXPECT_EQ(kSchedPriority, priority);
+}
+
+
TEST_F(BinderLibTest, VectorSent) {
Parcel data, reply;
sp<IBinder> server = addServer();
@@ -1330,6 +1352,16 @@
reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_SCHEDULING_POLICY: {
+ int policy = 0;
+ sched_param param;
+ if (0 != pthread_getschedparam(pthread_self(), &policy, ¶m)) {
+ return UNKNOWN_ERROR;
+ }
+ reply->writeInt32(policy);
+ reply->writeInt32(param.sched_priority);
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
@@ -1366,6 +1398,8 @@
{
sp<BinderLibTestService> testService = new BinderLibTestService(index);
+ testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority);
+
/*
* Normally would also contain functionality as well, but we are only
* testing the extension mechanism.
@@ -1451,6 +1485,8 @@
}
int main(int argc, char **argv) {
+ ExitIfWrongAbi();
+
if (argc == 4 && !strcmp(argv[1], "--servername")) {
binderservername = argv[2];
} else {
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index b790997..3b1faa8 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -116,7 +116,7 @@
if (time > max_time_bucket) {
m_long_transactions++;
}
- m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1;
+ m_buckets[min((uint32_t)(time / time_per_bucket), num_buckets - 1)] += 1;
m_best = min(time, m_best);
m_worst = max(time, m_worst);
m_transactions += 1;
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 5eb509c..88752ee 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -22,7 +22,7 @@
shared_libs: [
"libbinder",
- "libhidlbase", // libhwbinder is in here
+ "libhidlbase", // libhwbinder is in here
],
export_include_dirs: ["include"],
@@ -31,6 +31,7 @@
"-Wall",
"-Werror",
],
+ min_sdk_version: "29",
}
hidl_package_root {
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index 68cc225..44e2fd1 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -165,7 +165,6 @@
android::ProcessState::self()->startThreadPool();
// HIDL
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str()));
@@ -176,7 +175,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::details::setTrebleTestingOverride(true);
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
return server(kP1Id, kP2Id);
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 0b77ab3..50f6289 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -88,16 +88,6 @@
return policyN1 - policyN2;
}
-static int bpf_obj_get_wronly(const char *pathname) {
- union bpf_attr attr;
-
- memset(&attr, 0, sizeof(attr));
- attr.pathname = ptr_to_u64((void *)pathname);
- attr.file_flags = BPF_F_WRONLY;
-
- return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
-}
-
static bool initGlobals() {
std::lock_guard<std::mutex> guard(gInitializedMutex);
if (gInitialized) return true;
@@ -156,7 +146,7 @@
static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
eventType.c_str(), eventName.c_str());
- int prog_fd = bpfFdGet(path.c_str(), BPF_F_RDONLY);
+ int prog_fd = retrieveProgram(path.c_str());
if (prog_fd < 0) return false;
return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
}
@@ -183,7 +173,7 @@
if (!initGlobals()) return false;
if (gTracking) return true;
- unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
+ unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
if (cpuPolicyFd < 0) return false;
for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) {
@@ -192,7 +182,7 @@
}
}
- unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
+ unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
if (freqToIdxFd < 0) return false;
freq_idx_key_t key;
for (uint32_t i = 0; i < gNPolicies; ++i) {
@@ -207,23 +197,23 @@
}
}
- unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
+ unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
if (cpuLastUpdateFd < 0) return false;
std::vector<uint64_t> zeros(get_nprocs_conf(), 0);
uint32_t zero = 0;
if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false;
- unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map"));
+ unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_nr_active_map"));
if (nrActiveFd < 0) return false;
if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false;
- unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
+ unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
if (policyNrActiveFd < 0) return false;
for (uint32_t i = 0; i < gNPolicies; ++i) {
if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false;
}
- unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
+ unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
if (policyFreqIdxFd < 0) return false;
for (uint32_t i = 0; i < gNPolicies; ++i) {
auto freqIdx = getPolicyFreqIdx(i);
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index fd557b7..3ec4b3a 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -56,6 +56,10 @@
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
+ "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+ "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
+ "android.hardware.automotive.evs@1.0::IEvsCamera",
+ "android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
"android.hardware.bluetooth@1.0::IBluetoothHci",
@@ -67,16 +71,12 @@
"android.hardware.media.c2@1.0::IComponentStore",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
+ "android.hardware.neuralnetworks@1.0::IDevice",
"android.hardware.power@1.3::IPower",
"android.hardware.power.stats@1.0::IPowerStats",
"android.hardware.sensors@1.0::ISensors",
"android.hardware.thermal@2.0::IThermal",
"android.hardware.vr@1.0::IVr",
- "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
- "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
- "android.hardware.automotive.vehicle@2.0::IVehicle",
- "android.hardware.automotive.evs@1.0::IEvsCamera",
- "android.hardware.neuralnetworks@1.0::IDevice",
NULL,
};
@@ -86,7 +86,7 @@
static void read_extra_hals_to_dump_from_property() {
// extra hals to dump are already filled
- if (extra_hal_interfaces_to_dump.size() > 0) {
+ if (!extra_hal_interfaces_to_dump.empty()) {
return;
}
std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index de32ff4..6909637 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -1,5 +1,6 @@
cc_defaults {
name: "fakeservicemanager_defaults",
+ host_supported: true,
srcs: [
"ServiceManager.cpp",
],
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
index 67743cd..4a95778 100644
--- a/libs/gralloc/OWNERS
+++ b/libs/gralloc/OWNERS
@@ -1,2 +1,2 @@
-marissaw@google.com
+chrisforbes@google.com
vhau@google.com
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 00f7484..66fb295 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -27,6 +27,11 @@
enabled: true,
support_system_process: true,
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
srcs: [
"Gralloc4.cpp"
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 8d12754..1a7c2c9 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -431,6 +431,12 @@
static_cast<int64_t>(
aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ PlaneLayoutComponentType_RAW =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::RAW)};
+
/*---------------------------------------------------------------------------------------------*/
/**
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4809c1f..119b3e0 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -29,7 +29,6 @@
#include <android-base/strings.h>
#include <android/dlext.h>
#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
#include <graphicsenv/IGpuService.h>
#include <log/log.h>
#include <nativeloader/dlext_namespaces.h>
@@ -40,6 +39,13 @@
#include <string>
#include <thread>
+// TODO(b/159240322): Extend this to x86 ABI.
+#if defined(__LP64__)
+#define UPDATABLE_DRIVER_ABI "arm64-v8a"
+#else
+#define UPDATABLE_DRIVER_ABI "armeabi-v7a"
+#endif // defined(__LP64__)
+
// TODO(ianelliott@): Get the following from an ANGLE header:
#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
// Version-2 API:
@@ -67,7 +73,7 @@
static std::string vndkVersionStr() {
#ifdef __BIONIC__
- return android::base::GetProperty("ro.vndk.version", "");
+ return base::GetProperty("ro.vndk.version", "");
#endif
return "";
}
@@ -338,10 +344,8 @@
}
bool GraphicsEnv::checkAngleRules(void* so) {
- char manufacturer[PROPERTY_VALUE_MAX];
- char model[PROPERTY_VALUE_MAX];
- property_get("ro.product.manufacturer", manufacturer, "UNSET");
- property_get("ro.product.model", model, "UNSET");
+ auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
+ auto model = base::GetProperty("ro.product.model", "UNSET");
auto ANGLEGetFeatureSupportUtilAPIVersion =
(fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
@@ -394,7 +398,8 @@
ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
break;
}
- if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+ if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
+ systemInfoHandle)) {
ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
break;
}
@@ -584,7 +589,28 @@
}
if (mDriverPath.empty()) {
- return nullptr;
+ // For an application process, driver path is empty means this application is not opted in
+ // to use updatable driver. Application process doesn't have the ability to set up
+ // environment variables and hence before `getenv` call will return.
+ // For a process that is not an application process, if it's run from an environment,
+ // for example shell, where environment variables can be set, then it can opt into using
+ // udpatable driver by setting UPDATABLE_GFX_DRIVER to 1. By setting to 1 the developer
+ // driver will be used currently.
+ // TODO(b/159240322) Support the production updatable driver.
+ const char* id = getenv("UPDATABLE_GFX_DRIVER");
+ if (id == nullptr || std::strcmp(id, "1")) {
+ return nullptr;
+ }
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ return nullptr;
+ }
+ mDriverPath = gpuService->getUpdatableDriverPath();
+ if (mDriverPath.empty()) {
+ return nullptr;
+ }
+ mDriverPath.append(UPDATABLE_DRIVER_ABI);
+ ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
}
auto vndkNamespace = android_get_exported_namespace("vndk");
@@ -625,8 +651,7 @@
mAngleNamespace = android_create_namespace("ANGLE",
nullptr, // ld_library_path
mAnglePath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
nullptr, // permitted_when_isolated_path
nullptr);
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index de3503b..fa25c55 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -27,11 +27,11 @@
public:
explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
- virtual void setGpuStats(const std::string& driverPackageName,
- const std::string& driverVersionName, uint64_t driverVersionCode,
- int64_t driverBuildTime, const std::string& appPackageName,
- const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
- bool isDriverLoaded, int64_t driverLoadingTime) {
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t driverVersionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, const int32_t vulkanVersion,
+ GpuStatsInfo::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -48,8 +48,8 @@
remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
- const GpuStatsInfo::Stats stats, const uint64_t value) {
+ void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
+ const GpuStatsInfo::Stats stats, const uint64_t value) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -60,6 +60,27 @@
remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ void setUpdatableDriverPath(const std::string& driverPath) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+ data.writeUtf8AsUtf16(driverPath);
+
+ remote()->transact(BnGpuService::SET_UPDATABLE_DRIVER_PATH, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+
+ std::string getUpdatableDriverPath() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ status_t error = remote()->transact(BnGpuService::GET_UPDATABLE_DRIVER_PATH, data, &reply);
+ std::string driverPath;
+ if (error == OK) {
+ error = reply.readUtf8FromUtf16(&driverPath);
+ }
+ return driverPath;
+ }
};
IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -126,6 +147,21 @@
return OK;
}
+ case SET_UPDATABLE_DRIVER_PATH: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPath;
+ if ((status = data.readUtf8FromUtf16(&driverPath)) != OK) return status;
+
+ setUpdatableDriverPath(driverPath);
+ return OK;
+ }
+ case GET_UPDATABLE_DRIVER_PATH: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPath = getUpdatableDriverPath();
+ return reply->writeUtf8AsUtf16(driverPath);
+ }
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index c7c6d1e..2d59fa0 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -42,6 +42,10 @@
// set target stats.
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
+
+ // setter and getter for updatable driver path.
+ virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
+ virtual std::string getUpdatableDriverPath() = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
@@ -49,6 +53,8 @@
enum IGpuServiceTag {
SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
SET_TARGET_STATS,
+ SET_UPDATABLE_DRIVER_PATH,
+ GET_UPDATABLE_DRIVER_PATH,
// Always append new enum to the end.
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 7f57f5d..686e274 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -27,6 +27,7 @@
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
],
+ min_sdk_version: "29",
}
cc_library_shared {
@@ -91,6 +92,7 @@
export_shared_lib_headers: [
"libbinder",
+ "libinput",
],
// bufferhub is not used when building libgui for vendors
@@ -129,6 +131,11 @@
cc_library_static {
name: "libgui_bufferqueue_static",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
cflags: [
"-DNO_BUFFERHUB",
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 8d80833..5023b6b 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -75,10 +75,12 @@
if (NULL != fp) {
const size_t size = 64;
char proc_name[size];
- fgets(proc_name, size, fp);
+ char* result = fgets(proc_name, size, fp);
fclose(fp);
- name = proc_name;
- return NO_ERROR;
+ if (result != nullptr) {
+ name = proc_name;
+ return NO_ERROR;
+ }
}
return INVALID_OPERATION;
}
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 15f966d..b33bc9e 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,10 +36,7 @@
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::ConfigChanged configChanged)
- : mLooper(looper),
- mReceiver(vsyncSource, configChanged),
- mWaitingForVsync(false),
- mConfigChangeFlag(configChanged) {
+ : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}
@@ -92,16 +89,12 @@
return OK;
}
-void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
- if (mConfigChangeFlag == configChangeFlag) {
- return;
- }
- status_t status = mReceiver.toggleConfigEvents(configChangeFlag);
+void DisplayEventDispatcher::requestLatestConfig() {
+ status_t status = mReceiver.requestLatestConfig();
if (status) {
ALOGW("Failed enable config events, status=%d", status);
return;
}
- mConfigChangeFlag = configChangeFlag;
}
int DisplayEventDispatcher::getFd() const {
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index fd6aaf8..1fed509 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,10 +79,9 @@
return NO_INIT;
}
-status_t DisplayEventReceiver::toggleConfigEvents(
- ISurfaceComposer::ConfigChanged configChangeFlag) {
+status_t DisplayEventReceiver::requestLatestConfig() {
if (mEventConnection != nullptr) {
- mEventConnection->toggleConfigEvents(configChangeFlag);
+ mEventConnection->requestLatestConfig();
return NO_ERROR;
}
return NO_INIT;
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index dda5acf..aa74bfd 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,8 +26,8 @@
STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
SET_VSYNC_RATE,
REQUEST_NEXT_VSYNC,
- TOGGLE_CONFIG_EVENTS,
- LAST = TOGGLE_CONFIG_EVENTS,
+ REQUEST_LATEST_CONFIG,
+ LAST = REQUEST_LATEST_CONFIG,
};
} // Anonymous namespace
@@ -55,10 +55,9 @@
Tag::REQUEST_NEXT_VSYNC);
}
- void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override {
- callRemoteAsync<decltype(
- &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS,
- configChangeFlag);
+ void requestLatestConfig() override {
+ callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>(
+ Tag::REQUEST_LATEST_CONFIG);
}
};
@@ -81,8 +80,8 @@
return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
case Tag::REQUEST_NEXT_VSYNC:
return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
- case Tag::TOGGLE_CONFIG_EVENTS:
- return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents);
+ case Tag::REQUEST_LATEST_CONFIG:
+ return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig);
}
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2a27a9a..6881be3 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -371,10 +371,8 @@
data.writeStrongBinder(display);
remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
const status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
- }
- return result;
+ if (result != NO_ERROR) return result;
+ return reply.read(*info);
}
virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
@@ -691,8 +689,7 @@
return result;
}
- virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
- {
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
if (!outLayers) {
return UNEXPECTED_NULL;
}
@@ -1094,22 +1091,22 @@
return NO_ERROR;
}
- virtual status_t notifyPowerHint(int32_t hintId) {
+ virtual status_t notifyPowerBoost(int32_t boostId) {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+ ALOGE("notifyPowerBoost: failed to write interface token: %d", error);
return error;
}
- error = data.writeInt32(hintId);
+ error = data.writeInt32(boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to write boostId: %d", error);
return error;
}
- error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+ error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply,
IBinder::FLAG_ONEWAY);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to transact: %d", error);
+ ALOGE("notifyPowerBoost: failed to transact: %d", error);
return error;
}
return NO_ERROR;
@@ -1443,10 +1440,8 @@
const sp<IBinder> display = data.readStrongBinder();
const status_t result = getDisplayInfo(display, &info);
reply->writeInt32(result);
- if (result == NO_ERROR) {
- memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
- }
- return NO_ERROR;
+ if (result != NO_ERROR) return result;
+ return reply->write(info);
}
case GET_DISPLAY_CONFIGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1990,15 +1985,15 @@
}
return setDisplayBrightness(displayToken, brightness);
}
- case NOTIFY_POWER_HINT: {
+ case NOTIFY_POWER_BOOST: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- int32_t hintId;
- status_t error = data.readInt32(&hintId);
+ int32_t boostId;
+ status_t error = data.readInt32(&boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to read boostId: %d", error);
return error;
}
- return notifyPowerHint(hintId);
+ return notifyPowerBoost(boostId);
}
case SET_GLOBAL_SHADOW_SETTINGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index f7158d0..117ce58 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -55,7 +55,7 @@
output.writeFloat(color.g);
output.writeFloat(color.b);
#ifndef NO_INPUT
- inputInfo.write(output);
+ inputHandle->writeToParcel(&output);
#endif
output.write(transparentRegion);
output.writeUint32(transform);
@@ -116,6 +116,7 @@
output.writeInt32(frameRateSelectionPriority);
output.writeFloat(frameRate);
output.writeByte(frameRateCompatibility);
+ output.writeUint32(fixedTransformHint);
return NO_ERROR;
}
@@ -151,7 +152,7 @@
color.b = input.readFloat();
#ifndef NO_INPUT
- inputInfo = InputWindowInfo::read(input);
+ inputHandle->readFromParcel(&input);
#endif
input.read(transparentRegion);
@@ -198,6 +199,7 @@
frameRateSelectionPriority = input.readInt32();
frameRate = input.readFloat();
frameRateCompatibility = input.readByte();
+ fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32());
return NO_ERROR;
}
@@ -402,7 +404,7 @@
#ifndef NO_INPUT
if (other.what & eInputInfoChanged) {
what |= eInputInfoChanged;
- inputInfo = other.inputInfo;
+ inputHandle = new InputWindowHandle(*other.inputHandle);
}
#endif
@@ -433,6 +435,10 @@
frameRate = other.frameRate;
frameRateCompatibility = other.frameRateCompatibility;
}
+ if (other.what & eFixedTransformHintChanged) {
+ what |= eFixedTransformHintChanged;
+ fixedTransformHint = other.fixedTransformHint;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
@@ -442,19 +448,36 @@
// ------------------------------- InputWindowCommands ----------------------------------------
-void InputWindowCommands::merge(const InputWindowCommands& other) {
+bool InputWindowCommands::merge(const InputWindowCommands& other) {
+ bool changes = false;
+#ifndef NO_INPUT
+ changes |= !other.focusRequests.empty();
+ focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()),
+ std::make_move_iterator(other.focusRequests.end()));
+#endif
+ changes |= other.syncInputWindows && !syncInputWindows;
syncInputWindows |= other.syncInputWindows;
+ return changes;
}
void InputWindowCommands::clear() {
+#ifndef NO_INPUT
+ focusRequests.clear();
+#endif
syncInputWindows = false;
}
void InputWindowCommands::write(Parcel& output) const {
+#ifndef NO_INPUT
+ output.writeParcelableVector(focusRequests);
+#endif
output.writeBool(syncInputWindows);
}
void InputWindowCommands::read(const Parcel& input) {
+#ifndef NO_INPUT
+ input.readParcelableVector(&focusRequests);
+#endif
syncInputWindows = input.readBool();
}
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index c13401d..ecccf29 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,12 +1,25 @@
adyabr@google.com
akrulec@google.com
alecmouri@google.com
+chaviw@google.com
+chrisforbes@google.com
jessehall@google.com
-jwcai@google.com
lpy@google.com
-marissaw@google.com
mathias@google.com
racarr@google.com
steventhomas@google.com
stoza@google.com
vhau@google.com
+vishnun@google.com
+
+per-file EndToEndNativeInputTest.cpp = svv@google.com
+
+# BufferQueue is feature-frozen
+per-file BufferQueue* = set noparent
+per-file BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file IGraphicBuffer* = set noparent
+per-file IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file include/gui/BufferQueue* = set noparent
+per-file include/gui/BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file include/gui/IGraphicBuffer* = set noparent
+per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
\ No newline at end of file
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index f911e70..a86eafa 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -57,7 +57,8 @@
return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
op == NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR ||
- op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR;
+ op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR ||
+ op == NATIVE_WINDOW_SET_QUERY_INTERCEPTOR;
}
} // namespace
@@ -109,7 +110,7 @@
mConnectedToCpu = false;
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
- mMaxBufferCount = 0;
+ mMaxBufferCount = NUM_BUFFER_SLOTS;
}
Surface::~Surface() {
@@ -501,6 +502,19 @@
int Surface::hook_query(const ANativeWindow* window, int what, int* value) {
const Surface* c = getSelf(window);
+ {
+ std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+ if (c->mQueryInterceptor != nullptr) {
+ auto interceptor = c->mQueryInterceptor;
+ auto data = c->mQueryInterceptorData;
+ return interceptor(window, Surface::queryInternal, data, what, value);
+ }
+ }
+ return c->query(what, value);
+}
+
+int Surface::queryInternal(const ANativeWindow* window, int what, int* value) {
+ const Surface* c = getSelf(window);
return c->query(what, value);
}
@@ -1177,6 +1191,9 @@
case NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR:
res = dispatchAddQueueInterceptor(args);
break;
+ case NATIVE_WINDOW_SET_QUERY_INTERCEPTOR:
+ res = dispatchAddQueryInterceptor(args);
+ break;
case NATIVE_WINDOW_ALLOCATE_BUFFERS:
allocateBuffers();
res = NO_ERROR;
@@ -1457,6 +1474,15 @@
return NO_ERROR;
}
+int Surface::dispatchAddQueryInterceptor(va_list args) {
+ ANativeWindow_queryInterceptor interceptor = va_arg(args, ANativeWindow_queryInterceptor);
+ void* data = va_arg(args, void*);
+ std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+ mQueryInterceptor = interceptor;
+ mQueryInterceptorData = data;
+ return NO_ERROR;
+}
+
int Surface::dispatchGetLastQueuedBuffer(va_list args) {
AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
int* fence = va_arg(args, int*);
@@ -1467,7 +1493,7 @@
int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
if (graphicBuffer != nullptr) {
- *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+ *buffer = graphicBuffer->toAHardwareBuffer();
AHardwareBuffer_acquire(*buffer);
} else {
*buffer = nullptr;
@@ -1559,6 +1585,7 @@
mStickyTransform = 0;
mAutoPrerotation = false;
mEnableFrameTimestamps = false;
+ mMaxBufferCount = NUM_BUFFER_SLOTS;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a52f298..229a5c2 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -353,6 +353,8 @@
mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeup(other.mEarlyWakeup),
+ mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
+ mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
mContainsBuffer(other.mContainsBuffer),
mDesiredPresentTime(other.mDesiredPresentTime) {
mDisplayStates = other.mDisplayStates;
@@ -375,6 +377,8 @@
const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
const bool earlyWakeup = parcel->readBool();
+ const bool explicitEarlyWakeupStart = parcel->readBool();
+ const bool explicitEarlyWakeupEnd = parcel->readBool();
const bool containsBuffer = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
@@ -443,6 +447,8 @@
mTransactionNestCount = transactionNestCount;
mAnimation = animation;
mEarlyWakeup = earlyWakeup;
+ mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
mDisplayStates = displayStates;
@@ -470,6 +476,8 @@
parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
parcel->writeBool(mEarlyWakeup);
+ parcel->writeBool(mExplicitEarlyWakeupStart);
+ parcel->writeBool(mExplicitEarlyWakeupEnd);
parcel->writeBool(mContainsBuffer);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -545,6 +553,8 @@
mContainsBuffer |= other.mContainsBuffer;
mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
+ mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
other.clear();
return *this;
}
@@ -559,6 +569,8 @@
mTransactionNestCount = 0;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
mDesiredPresentTime = -1;
}
@@ -682,9 +694,20 @@
flags |= ISurfaceComposer::eEarlyWakeup;
}
+ // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+ // it is equivalent for none
+ if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+ }
+ if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+ }
+
mForceSynchronous = false;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
@@ -731,6 +754,14 @@
mEarlyWakeup = true;
}
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
+ mExplicitEarlyWakeupStart = true;
+}
+
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
+ mExplicitEarlyWakeupEnd = true;
+}
+
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
@@ -1328,11 +1359,26 @@
mStatus = BAD_INDEX;
return *this;
}
- s->inputInfo = info;
+ s->inputHandle = new InputWindowHandle(info);
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+ const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos) {
+ FocusRequest request;
+ request.token = token;
+ request.focusedToken = focusedToken;
+ request.timestamp = timestampNanos;
+ return setFocusedWindow(request);
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
+ const FocusRequest& request) {
+ mInputWindowCommands.focusRequests.push_back(request);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
mInputWindowCommands.syncInputWindows = true;
return *this;
@@ -1437,6 +1483,22 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
+ const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ const ui::Transform::RotationFlags transform = fixedTransformHint == -1
+ ? ui::Transform::ROT_INVALID
+ : ui::Transform::toRotationFlags(static_cast<ui::Rotation>(fixedTransformHint));
+ s->what |= layer_state_t::eFixedTransformHintChanged;
+ s->fixedTransformHint = transform;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1846,8 +1908,8 @@
return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
}
-status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
- return ComposerService::getComposerService()->notifyPowerHint(hintId);
+status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
+ return ComposerService::getComposerService()->notifyPowerBoost(boostId);
}
status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index a332a1f..8dcb71b 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -178,17 +178,28 @@
}
sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
- sp<IBinder> client = parcel->readStrongBinder();
- sp<IBinder> handle = parcel->readStrongBinder();
- if (client == nullptr || handle == nullptr)
- {
- ALOGE("Invalid parcel");
- return nullptr;
+ bool invalidParcel = false;
+ status_t status;
+ sp<IBinder> client;
+ if ((status = parcel->readStrongBinder(&client)) != OK) {
+ ALOGE("Failed to read client: %s", statusToString(status).c_str());
+ invalidParcel = true;
+ }
+ sp<IBinder> handle;
+ if ((status = parcel->readStrongBinder(&handle)) != OK) {
+ ALOGE("Failed to read handle: %s", statusToString(status).c_str());
+ invalidParcel = true;
}
sp<IBinder> gbp;
- parcel->readNullableStrongBinder(&gbp);
-
+ if ((status = parcel->readNullableStrongBinder(&gbp)) != OK) {
+ ALOGE("Failed to read gbp: %s", statusToString(status).c_str());
+ invalidParcel = true;
+ }
uint32_t transformHint = parcel->readUint32();
+
+ if (invalidParcel) {
+ return nullptr;
+ }
// We aren't the original owner of the surface.
return new SurfaceControl(new SurfaceComposerClient(
interface_cast<ISurfaceComposerClient>(client)),
diff --git a/libs/gui/bufferqueue/OWNERS b/libs/gui/bufferqueue/OWNERS
index cbe9317..615dd79 100644
--- a/libs/gui/bufferqueue/OWNERS
+++ b/libs/gui/bufferqueue/OWNERS
@@ -1,5 +1,4 @@
-chz@google.com
-lajos@google.com
-pawin@google.com
-taklee@google.com
-wonsik@google.com
+# BufferQueue is feature-frozen
+jreck@google.com
+sumir@google.com
+alecmouri@google.com
\ No newline at end of file
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index fcdf6bf..f210c34 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,7 +31,7 @@
status_t initialize();
void dispose();
status_t scheduleVsync();
- void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+ void requestLatestConfig();
int getFd() const;
virtual int handleEvent(int receiveFd, int events, void* data);
@@ -42,7 +42,6 @@
sp<Looper> mLooper;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
- ISurfaceComposer::ConfigChanged mConfigChangeFlag;
virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index d9a0253..8d49184 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -147,9 +147,10 @@
status_t requestNextVsync();
/*
- * toggleConfigEvents() toggles delivery of config change events.
+ * requestLatestConfig() force-requests the current config for the primary
+ * display.
*/
- status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+ status_t requestLatestConfig();
private:
sp<IDisplayEventConnection> mEventConnection;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index 8b35ef6..674aafd 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -53,11 +53,9 @@
virtual void requestNextVsync() = 0; // Asynchronous
/*
- * togglesConfigEvents() configures whether or not display config changes
- * should be propagated.
+ * requestLatestConfig() requests the config for the primary display.
*/
- virtual void toggleConfigEvents(
- ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous
+ virtual void requestLatestConfig() = 0; // Asynchronous
};
class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 0d33b3f..645714a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -81,12 +81,20 @@
// flags for setTransactionState()
enum {
eSynchronous = 0x01,
- eAnimation = 0x02,
+ eAnimation = 0x02,
- // Indicates that this transaction will likely result in a lot of layers being composed, and
- // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
- // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
- eEarlyWakeup = 0x04
+ // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
+ eEarlyWakeup = 0x04,
+
+ // Explicit indication that this transaction and others to follow will likely result in a
+ // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
+ // missing frame deadlines. In this case SurfaceFlinger will wake up at
+ // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
+ // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+ // expected to be used by WindowManager only and are guarded by
+ // android.permission.ACCESS_SURFACE_FLINGER
+ eExplicitEarlyWakeupStart = 0x08,
+ eExplicitEarlyWakeupEnd = 0x10,
};
enum VsyncSource {
@@ -354,7 +362,7 @@
*
* Requires the ACCESS_SURFACE_FLINGER permission.
*/
- virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
+ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) = 0;
virtual status_t getColorManagement(bool* outGetColorManagement) const = 0;
@@ -488,14 +496,14 @@
virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- virtual status_t notifyPowerHint(int32_t hintId) = 0;
+ virtual status_t notifyPowerBoost(int32_t boostId) = 0;
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -583,7 +591,7 @@
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
CAPTURE_SCREEN_BY_ID,
- NOTIFY_POWER_HINT,
+ NOTIFY_POWER_BOOST,
SET_GLOBAL_SHADOW_SETTINGS,
GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
SET_AUTO_LOW_LATENCY_MODE,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 6366529..3afbabf 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -33,7 +33,7 @@
DECLARE_META_INTERFACE(SurfaceComposerClient)
// flags for createSurface()
- enum { // (keep in sync with Surface.java)
+ enum { // (keep in sync with SurfaceControl.java)
eHidden = 0x00000004,
eDestroyBackbuffer = 0x00000020,
eSecure = 0x00000080,
@@ -42,6 +42,7 @@
eProtectedByApp = 0x00000800,
eProtectedByDRM = 0x00001000,
eCursorWindow = 0x00002000,
+ eNoColorFill = 0x00004000,
eFXSurfaceBufferQueue = 0x00000000,
eFXSurfaceEffect = 0x00020000,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2b2f773..41aad0d 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,6 +26,7 @@
#include <math/mat4.h>
#ifndef NO_INPUT
+#include <android/FocusRequest.h>
#include <input/InputWindow.h>
#endif
@@ -35,6 +36,7 @@
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
+#include <ui/Transform.h>
#include <utils/Errors.h>
namespace android {
@@ -103,6 +105,7 @@
eFrameRateChanged = 0x40'00000000,
eBackgroundBlurRadiusChanged = 0x80'00000000,
eProducerDisconnect = 0x100'00000000,
+ eFixedTransformHintChanged = 0x200'00000000,
};
layer_state_t()
@@ -136,7 +139,8 @@
shadowRadius(0.0f),
frameRateSelectionPriority(-1),
frameRate(0.0f),
- frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) {
+ frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ fixedTransformHint(ui::Transform::ROT_INVALID) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -198,7 +202,7 @@
mat4 colorTransform;
#ifndef NO_INPUT
- InputWindowInfo inputInfo;
+ sp<InputWindowHandle> inputHandle = new InputWindowHandle();
#endif
client_cache_t cachedBuffer;
@@ -225,6 +229,15 @@
// Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
float frameRate;
int8_t frameRateCompatibility;
+
+ // Set by window manager indicating the layer and all its children are
+ // in a different orientation than the display. The hint suggests that
+ // the graphic producers should receive a transform hint as if the
+ // display was in this orientation. When the display changes to match
+ // the layer orientation, the graphic producer may not need to allocate
+ // a buffer of a different size. -1 means the transform hint is not set,
+ // otherwise the value will be a valid ui::Rotation.
+ ui::Transform::RotationFlags fixedTransformHint;
};
struct ComposerState {
@@ -271,9 +284,13 @@
};
struct InputWindowCommands {
+#ifndef NO_INPUT
+ std::vector<FocusRequest> focusRequests;
+#endif
bool syncInputWindows{false};
- void merge(const InputWindowCommands& other);
+ // Merges the passed in commands and returns true if there were any changes.
+ bool merge(const InputWindowCommands& other);
void clear();
void write(Parcel& output) const;
void read(const Parcel& input);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 917c0d4..49c83da 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -209,6 +209,7 @@
int* fenceFd);
static int performInternal(ANativeWindow* window, int operation, va_list args);
static int queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+ static int queryInternal(const ANativeWindow* window, int what, int* value);
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
@@ -261,6 +262,7 @@
int dispatchAddDequeueInterceptor(va_list args);
int dispatchAddPerformInterceptor(va_list args);
int dispatchAddQueueInterceptor(va_list args);
+ int dispatchAddQueryInterceptor(va_list args);
int dispatchGetLastQueuedBuffer(va_list args);
bool transformToDisplayInverse();
@@ -468,7 +470,7 @@
mutable Mutex mMutex;
// mInterceptorMutex is the mutex guarding interceptors.
- std::shared_mutex mInterceptorMutex;
+ mutable std::shared_mutex mInterceptorMutex;
ANativeWindow_cancelBufferInterceptor mCancelInterceptor = nullptr;
void* mCancelInterceptorData = nullptr;
@@ -478,6 +480,8 @@
void* mPerformInterceptorData = nullptr;
ANativeWindow_queueBufferInterceptor mQueueInterceptor = nullptr;
void* mQueueInterceptorData = nullptr;
+ ANativeWindow_queryInterceptor mQueryInterceptor = nullptr;
+ void* mQueryInterceptorData = nullptr;
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 531aed7..d5ca0f6 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -217,14 +217,14 @@
static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- static status_t notifyPowerHint(int32_t hintId);
+ static status_t notifyPowerBoost(int32_t boostId);
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -345,10 +345,12 @@
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
- uint32_t mForceSynchronous = 0;
- uint32_t mTransactionNestCount = 0;
- bool mAnimation = false;
- bool mEarlyWakeup = false;
+ uint32_t mForceSynchronous = 0;
+ uint32_t mTransactionNestCount = 0;
+ bool mAnimation = false;
+ bool mEarlyWakeup = false;
+ bool mExplicitEarlyWakeupStart = false;
+ bool mExplicitEarlyWakeupEnd = false;
// Indicates that the Transaction contains a buffer that should be cached
bool mContainsBuffer = false;
@@ -505,6 +507,9 @@
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
+ Transaction& setFocusedWindow(const sp<IBinder>& token, const sp<IBinder>& focusedToken,
+ nsecs_t timestampNanos);
+ Transaction& setFocusedWindow(const FocusRequest& request);
Transaction& syncInputWindows();
#endif
@@ -519,6 +524,14 @@
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
int8_t compatibility);
+ // Set by window manager indicating the layer and all its children are
+ // in a different orientation than the display. The hint suggests that
+ // the graphic producers should receive a transform hint as if the
+ // display was in this orientation. When the display changes to match
+ // the layer orientation, the graphic producer may not need to allocate
+ // a buffer of a different size.
+ Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -539,6 +552,8 @@
void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
void setAnimationTransaction();
void setEarlyWakeup();
+ void setExplicitEarlyWakeupStart();
+ void setExplicitEarlyWakeupEnd();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp
index e7f7c1f..64b1eac 100644
--- a/libs/gui/sysprop/Android.bp
+++ b/libs/gui/sysprop/Android.bp
@@ -4,4 +4,7 @@
api_packages: ["android.sysprop"],
property_owner: "Platform",
vendor_available: true,
+ cpp: {
+ min_sdk_version: "29",
+ },
}
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c59afba..cca8ddd 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -36,15 +36,16 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <input/InputWindow.h>
-#include <input/IInputFlinger.h>
-#include <input/InputTransport.h>
+#include <android/os/IInputFlinger.h>
#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <ui/DisplayConfig.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+using android::os::IInputFlinger;
namespace android {
namespace test {
@@ -67,11 +68,12 @@
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
mSurfaceControl = sc;
-
- InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+ std::unique_ptr<InputChannel> clientChannel;
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, clientChannel);
+ mClientChannel = std::move(clientChannel);
mInputFlinger = getInputFlinger();
- mInputFlinger->registerInputChannel(mServerChannel);
+ mInputFlinger->registerInputChannel(*mServerChannel);
populateInputInfo(width, height);
@@ -104,6 +106,15 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
+ static std::unique_ptr<InputSurface> makeCursorInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eCursorWindow);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
InputEvent* consumeEvent() {
waitForEventAvailable();
@@ -134,17 +145,17 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
EXPECT_EQ(x, mev->getX(0));
EXPECT_EQ(y, mev->getY(0));
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- ~InputSurface() {
- mInputFlinger->unregisterInputChannel(mServerChannel);
- }
+ ~InputSurface() { mInputFlinger->unregisterInputChannel(*mServerChannel); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
const sp<SurfaceControl>&)> transactionBody) {
@@ -176,9 +187,9 @@
void populateInputInfo(int width, int height) {
mInputInfo.token = mServerChannel->getConnectionToken();
mInputInfo.name = "Test info";
- mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
- mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
- mInputInfo.dispatchingTimeout = 100000;
+ mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+ mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
+ mInputInfo.dispatchingTimeout = 5s;
mInputInfo.globalScaleFactor = 1.0;
mInputInfo.canReceiveKeys = true;
mInputInfo.hasFocus = true;
@@ -190,19 +201,19 @@
// TODO: Fill in from SF?
mInputInfo.ownerPid = 11111;
mInputInfo.ownerUid = 11111;
- mInputInfo.inputFeatures = 0;
mInputInfo.displayId = 0;
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
- aInfo.dispatchingTimeout = 100000;
+ aInfo.dispatchingTimeout = 5s;
mInputInfo.applicationInfo = aInfo;
}
public:
sp<SurfaceControl> mSurfaceControl;
- sp<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputChannel> mServerChannel;
+ std::shared_ptr<InputChannel> mClientChannel;
sp<IInputFlinger> mInputFlinger;
InputWindowInfo mInputInfo;
@@ -537,5 +548,18 @@
injectTap(0, 0);
surface->expectTap(1, 1);
}
+
+TEST_F(InputSurfacesTest, input_ignores_cursor_layer) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> cursorSurface =
+ InputSurface::makeCursorInputSurface(mComposerClient, 10, 10);
+
+ surface->showAt(10, 10);
+ surface->assertFocusChange(true);
+ cursorSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
}
}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a1d12a5..6d92143 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -30,6 +30,7 @@
#include <gui/SurfaceComposerClient.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/Rect.h>
#include <utils/String8.h>
@@ -785,7 +786,7 @@
return NO_ERROR;
}
status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
- status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
+ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) override {
return NO_ERROR;
}
status_t getCompositionPreference(
@@ -849,7 +850,7 @@
float* /*outAppRequestRefreshRateMax*/) override {
return NO_ERROR;
};
- status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
float /*lightPosY*/, float /*lightPosZ*/,
@@ -1974,4 +1975,29 @@
ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
}
+TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+
+ int count = -1;
+ ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+
+ consumer->setMaxBufferCount(10);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU));
+ EXPECT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(10, count);
+
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+}
+
} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8efaf3d..7dd5276 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -34,23 +34,35 @@
clang: true,
+ header_libs: ["jni_headers"],
+ export_header_lib_headers: ["jni_headers"],
+
shared_libs: [
"libbase",
"liblog",
"libcutils",
],
+ static_libs: [
+ "libui-types",
+ ],
+
+ export_static_lib_headers: [
+ "libui-types",
+ ],
+
target: {
android: {
srcs: [
- "IInputFlinger.cpp",
"InputApplication.cpp",
"InputTransport.cpp",
"InputWindow.cpp",
- "ISetInputWindowsListener.cpp",
"LatencyStatistics.cpp",
"VelocityControl.cpp",
"VelocityTracker.cpp",
+ "android/FocusRequest.aidl",
+ "android/os/IInputFlinger.aidl",
+ "android/os/ISetInputWindowsListener.aidl",
],
shared_libs: [
@@ -69,6 +81,11 @@
},
},
},
+
+ aidl: {
+ local_include_dirs: ["."],
+ export_aidl_headers: true
+ },
}
subdirs = ["tests"]
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
deleted file mode 100644
index 8ec5165..0000000
--- a/libs/input/IInputFlinger.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <input/IInputFlinger.h>
-
-namespace android {
-
-class BpInputFlinger : public BpInterface<IInputFlinger> {
-public:
- explicit BpInputFlinger(const sp<IBinder>& impl) :
- BpInterface<IInputFlinger>(impl) { }
-
- virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo,
- const sp<ISetInputWindowsListener>& setInputWindowsListener) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
-
- data.writeUint32(static_cast<uint32_t>(inputInfo.size()));
- for (const auto& info : inputInfo) {
- info.write(data);
- }
- data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener));
-
- remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-
- virtual void registerInputChannel(const sp<InputChannel>& channel) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- channel->write(data);
- remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
- }
-
- virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
- Parcel data, reply;
- data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- channel->write(data);
- remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
- }
-};
-
-IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
-
-status_t BnInputFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch(code) {
- case SET_INPUT_WINDOWS_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- size_t count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
- std::vector<InputWindowInfo> handles;
- for (size_t i = 0; i < count; i++) {
- handles.push_back(InputWindowInfo::read(data));
- }
- const sp<ISetInputWindowsListener> setInputWindowsListener =
- ISetInputWindowsListener::asInterface(data.readStrongBinder());
- setInputWindows(handles, setInputWindowsListener);
- break;
- }
- case REGISTER_INPUT_CHANNEL_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = InputChannel::read(data);
- registerInputChannel(channel);
- break;
- }
- case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
- CHECK_INTERFACE(IInputFlinger, data, reply);
- sp<InputChannel> channel = InputChannel::read(data);
- unregisterInputChannel(channel);
- break;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
- return NO_ERROR;
-}
-
-};
diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp
deleted file mode 100644
index a0330da..0000000
--- a/libs/input/ISetInputWindowsListener.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 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 <input/ISetInputWindowsListener.h>
-
-namespace android {
-
-class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> {
-public:
- explicit BpSetInputWindowsListener(const sp<IBinder>& impl)
- : BpInterface<ISetInputWindowsListener>(impl) {
- }
-
- virtual ~BpSetInputWindowsListener() = default;
-
- virtual void onSetInputWindowsFinished() {
- Parcel data, reply;
- data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor());
- remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener");
-
-status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch(code) {
- case ON_SET_INPUT_WINDOWS_FINISHED: {
- CHECK_INTERFACE(ISetInputWindowsListener, data, reply);
- onSetInputWindowsFinished();
- return NO_ERROR;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c243767..fc73de3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -169,6 +169,18 @@
mEventTime = from.mEventTime;
}
+const char* KeyEvent::actionToString(int32_t action) {
+ // Convert KeyEvent action to string
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AKEY_EVENT_ACTION_UP:
+ return "UP";
+ case AKEY_EVENT_ACTION_MULTIPLE:
+ return "MULTIPLE";
+ }
+ return "UNKNOWN";
+}
// --- PointerCoords ---
@@ -289,6 +301,11 @@
}
}
+void PointerCoords::transform(const ui::Transform& transform) {
+ vec2 newCoords = transform.transform(getX(), getY());
+ setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x);
+ setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y);
+}
// --- PointerProperties ---
@@ -308,10 +325,10 @@
void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xScale,
- float yScale, float xOffset, float yOffset, float xPrecision,
- float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
- nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ int32_t buttonState, MotionClassification classification,
+ const ui::Transform& transform, float xPrecision, float yPrecision,
+ float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+ nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -322,10 +339,7 @@
mMetaState = metaState;
mButtonState = buttonState;
mClassification = classification;
- mXScale = xScale;
- mYScale = yScale;
- mXOffset = xOffset;
- mYOffset = yOffset;
+ mTransform = transform;
mXPrecision = xPrecision;
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
@@ -348,10 +362,7 @@
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mClassification = other->mClassification;
- mXScale = other->mXScale;
- mYScale = other->mYScale;
- mXOffset = other->mXOffset;
- mYOffset = other->mYOffset;
+ mTransform = other->mTransform;
mXPrecision = other->mXPrecision;
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
@@ -381,18 +392,20 @@
}
float MotionEvent::getXCursorPosition() const {
- const float rawX = getRawXCursorPosition();
- return rawX * mXScale + mXOffset;
+ vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+ return vals.x;
}
float MotionEvent::getYCursorPosition() const {
- const float rawY = getRawYCursorPosition();
- return rawY * mYScale + mYOffset;
+ vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
+ return vals.y;
}
void MotionEvent::setCursorPosition(float x, float y) {
- mRawXCursorPosition = (x - mXOffset) / mXScale;
- mRawYCursorPosition = (y - mYOffset) / mYScale;
+ ui::Transform inverse = mTransform.inverse();
+ vec2 vals = inverse.transform(x, y);
+ mRawXCursorPosition = vals.x;
+ mRawYCursorPosition = vals.y;
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -404,14 +417,7 @@
}
float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
- float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
- switch (axis) {
- case AMOTION_EVENT_AXIS_X:
- return value * mXScale + mXOffset;
- case AMOTION_EVENT_AXIS_Y:
- return value * mYScale + mYOffset;
- }
- return value;
+ return getHistoricalAxisValue(axis, pointerIndex, getHistorySize());
}
const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
@@ -426,14 +432,23 @@
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+ return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+ }
+
+ float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
+ float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
+ vec2 vals = mTransform.transform(rawX, rawY);
+
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value * mXScale + mXOffset;
+ return vals.x;
case AMOTION_EVENT_AXIS_Y:
- return value * mYScale + mYOffset;
+ return vals.y;
}
- return value;
+
+ // This should never happen
+ return 0;
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -447,23 +462,24 @@
}
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
- mXOffset += xOffset;
- mYOffset += yOffset;
+ float currXOffset = mTransform.tx();
+ float currYOffset = mTransform.ty();
+ mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
}
void MotionEvent::scale(float globalScaleFactor) {
- mXOffset *= globalScaleFactor;
- mYOffset *= globalScaleFactor;
+ mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
mXPrecision *= globalScaleFactor;
mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
+ mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor,
+ globalScaleFactor);
}
}
-static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) {
+static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) {
// Apply perspective transform like Skia.
float newX = matrix[0] * x + matrix[1] * y + matrix[2];
float newY = matrix[3] * x + matrix[4] * y + matrix[5];
@@ -471,22 +487,25 @@
if (newZ) {
newZ = 1.0f / newZ;
}
- *outX = newX * newZ;
- *outY = newY * newZ;
+ vec2 transformedPoint;
+ transformedPoint.x = newX * newZ;
+ transformedPoint.y = newY * newZ;
+ return transformedPoint;
}
-static float transformAngle(const float matrix[9], float angleRadians,
- float originX, float originY) {
+static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX,
+ float originY) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
float x = sinf(angleRadians);
float y = -cosf(angleRadians);
- transformPoint(matrix, x, y, &x, &y);
- x -= originX;
- y -= originY;
+ vec2 transformedPoint = transformPoint(matrix, x, y);
+
+ transformedPoint.x -= originX;
+ transformedPoint.y -= originY;
// Derive the transformed vector's clockwise angle from vertical.
- float result = atan2f(x, -y);
+ float result = atan2f(transformedPoint.x, -transformedPoint.y);
if (result < - M_PI_2) {
result += M_PI;
} else if (result > M_PI_2) {
@@ -495,51 +514,51 @@
return result;
}
-void MotionEvent::transform(const float matrix[9]) {
- // The tricky part of this implementation is to preserve the value of
- // rawX and rawY. So we apply the transformation to the first point
- // then derive an appropriate new X/Y offset that will preserve rawX
- // and rawY for that point.
- float oldXOffset = mXOffset;
- float oldYOffset = mYOffset;
- float newX, newY;
- float scaledRawX = getRawX(0) * mXScale;
- float scaledRawY = getRawY(0) * mYScale;
- transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
- mXOffset = newX - scaledRawX;
- mYOffset = newY - scaledRawY;
+void MotionEvent::transform(const std::array<float, 9>& matrix) {
+ // We want to preserve the rawX and rawY so we just update the transform
+ // using the values of the transform passed in
+ ui::Transform newTransform;
+ newTransform.set(matrix);
+ mTransform = newTransform * mTransform;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
- float originX, originY;
- transformPoint(matrix, 0, 0, &originX, &originY);
-
- // Apply the transformation to cursor position.
- if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- float x = mRawXCursorPosition * mXScale + oldXOffset;
- float y = mRawYCursorPosition * mYScale + oldYOffset;
- transformPoint(matrix, x, y, &x, &y);
- mRawXCursorPosition = (x - mXOffset) / mXScale;
- mRawYCursorPosition = (y - mYOffset) / mYScale;
- }
+ vec2 origin = transformPoint(matrix, 0, 0);
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
- transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
-
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(matrix, orientation, originX, originY));
+ transformAngle(matrix, orientation, origin.x, origin.y));
}
}
#ifdef __ANDROID__
+static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ status_t status = parcel.readFloat(&dsdx);
+ status |= parcel.readFloat(&dtdx);
+ status |= parcel.readFloat(&tx);
+ status |= parcel.readFloat(&dtdy);
+ status |= parcel.readFloat(&dsdy);
+ status |= parcel.readFloat(&ty);
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ return status;
+}
+
+static status_t writeToParcel(const ui::Transform& transform, Parcel& parcel) {
+ status_t status = parcel.writeFloat(transform.dsdx());
+ status |= parcel.writeFloat(transform.dtdx());
+ status |= parcel.writeFloat(transform.tx());
+ status |= parcel.writeFloat(transform.dtdy());
+ status |= parcel.writeFloat(transform.dsdy());
+ status |= parcel.writeFloat(transform.ty());
+ return status;
+}
+
status_t MotionEvent::readFromParcel(Parcel* parcel) {
size_t pointerCount = parcel->readInt32();
size_t sampleCount = parcel->readInt32();
@@ -565,10 +584,11 @@
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mClassification = static_cast<MotionClassification>(parcel->readByte());
- mXScale = parcel->readFloat();
- mYScale = parcel->readFloat();
- mXOffset = parcel->readFloat();
- mYOffset = parcel->readFloat();
+
+ result = android::readFromParcel(mTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
mXPrecision = parcel->readFloat();
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
@@ -623,10 +643,11 @@
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeByte(static_cast<int8_t>(mClassification));
- parcel->writeFloat(mXScale);
- parcel->writeFloat(mYScale);
- parcel->writeFloat(mXOffset);
- parcel->writeFloat(mYOffset);
+
+ status_t result = android::writeToParcel(mTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
parcel->writeFloat(mXPrecision);
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
@@ -678,6 +699,25 @@
return getAxisByLabel(label);
}
+const char* MotionEvent::actionToString(int32_t action) {
+ // Convert MotionEvent action to string
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
+ case AMOTION_EVENT_ACTION_UP:
+ return "UP";
+ case AMOTION_EVENT_ACTION_CANCEL:
+ return "CANCEL";
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ return "POINTER_DOWN";
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ return "POINTER_UP";
+ }
+ return "UNKNOWN";
+}
+
// --- FocusEvent ---
void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
index 1d9f8a7..41721a7 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -22,29 +22,34 @@
namespace android {
+status_t InputApplicationInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ token = parcel->readStrongBinder();
+ dispatchingTimeout = decltype(dispatchingTimeout)(parcel->readInt64());
+ status_t status = parcel->readUtf8FromUtf16(&name);
+
+ return status;
+}
+
+status_t InputApplicationInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ status_t status = parcel->writeStrongBinder(token)
+ ?: parcel->writeInt64(dispatchingTimeout.count())
+ ?: parcel->writeUtf8AsUtf16(name) ;
+
+ return status;
+}
+
// --- InputApplicationHandle ---
-InputApplicationHandle::InputApplicationHandle() {
-}
+InputApplicationHandle::InputApplicationHandle() {}
-InputApplicationHandle::~InputApplicationHandle() {
-}
-
-InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
- InputApplicationInfo ret;
- ret.token = from.readStrongBinder();
- ret.name = from.readString8().c_str();
- ret.dispatchingTimeout = from.readInt64();
-
- return ret;
-}
-
-status_t InputApplicationInfo::write(Parcel& output) const {
- output.writeStrongBinder(token);
- output.writeString8(String8(name.c_str()));
- output.writeInt64(dispatchingTimeout);
-
- return OK;
-}
+InputApplicationHandle::~InputApplicationHandle() {}
} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4db9e06..dbd6293 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -46,9 +46,9 @@
static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
const std::string& name, InputDeviceConfigurationFileType type) {
- path += CONFIGURATION_FILE_DIR[type];
+ path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)];
path += name;
- path += CONFIGURATION_FILE_EXTENSION[type];
+ path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)];
}
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 11af23e..b088ee7 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -133,12 +133,11 @@
// Write the header
msg->header.type = header.type;
+ msg->header.seq = header.seq;
// Write the body
switch(header.type) {
case InputMessage::Type::KEY: {
- // uint32_t seq
- msg->body.key.seq = body.key.seq;
// int32_t eventId
msg->body.key.eventId = body.key.eventId;
// nsecs_t eventTime
@@ -168,8 +167,6 @@
break;
}
case InputMessage::Type::MOTION: {
- // uint32_t seq
- msg->body.motion.seq = body.motion.seq;
// int32_t eventId
msg->body.motion.eventId = body.motion.eventId;
// nsecs_t eventTime
@@ -198,14 +195,14 @@
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
msg->body.motion.downTime = body.motion.downTime;
- // float xScale
- msg->body.motion.xScale = body.motion.xScale;
- // float yScale
- msg->body.motion.yScale = body.motion.yScale;
- // float xOffset
- msg->body.motion.xOffset = body.motion.xOffset;
- // float yOffset
- msg->body.motion.yOffset = body.motion.yOffset;
+
+ msg->body.motion.dsdx = body.motion.dsdx;
+ msg->body.motion.dtdx = body.motion.dtdx;
+ msg->body.motion.dtdy = body.motion.dtdy;
+ msg->body.motion.dsdy = body.motion.dsdy;
+ msg->body.motion.tx = body.motion.tx;
+ msg->body.motion.ty = body.motion.ty;
+
// float xPrecision
msg->body.motion.xPrecision = body.motion.xPrecision;
// float yPrecision
@@ -232,12 +229,10 @@
break;
}
case InputMessage::Type::FINISHED: {
- msg->body.finished.seq = body.finished.seq;
msg->body.finished.handled = body.finished.handled;
break;
}
case InputMessage::Type::FOCUS: {
- msg->body.focus.seq = body.focus.seq;
msg->body.focus.eventId = body.focus.eventId;
msg->body.focus.hasFocus = body.focus.hasFocus;
msg->body.focus.inTouchMode = body.focus.inTouchMode;
@@ -248,39 +243,40 @@
// --- InputChannel ---
-sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
- sp<IBinder> token) {
+std::unique_ptr<InputChannel> InputChannel::create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
if (result != 0) {
LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
strerror(errno));
return nullptr;
}
- return new InputChannel(name, std::move(fd), token);
+ // using 'new' to access a non-public constructor
+ return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token));
}
-InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
- : mName(name), mFd(std::move(fd)), mToken(token) {
+InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token)
+ : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) {
if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}
InputChannel::~InputChannel() {
if (DEBUG_CHANNEL_LIFECYCLE) {
- ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
+ ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get());
}
}
status_t InputChannel::openInputChannelPair(const std::string& name,
- sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+ std::unique_ptr<InputChannel>& outServerChannel,
+ std::unique_ptr<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
- ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
- name.c_str(), errno);
- outServerChannel.clear();
- outClientChannel.clear();
+ ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.c_str(), errno);
+ outServerChannel.reset();
+ outClientChannel.reset();
return result;
}
@@ -308,7 +304,7 @@
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
@@ -343,7 +339,7 @@
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
+ nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
@@ -380,10 +376,10 @@
return OK;
}
-sp<InputChannel> InputChannel::dup() const {
+std::unique_ptr<InputChannel> InputChannel::dup() const {
android::base::unique_fd newFd(::dup(getFd()));
if (!newFd.ok()) {
- ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
+ ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
strerror(errno));
const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
// If this process is out of file descriptors, then throwing that might end up exploding
@@ -394,34 +390,25 @@
getName().c_str());
return nullptr;
}
- return InputChannel::create(mName, std::move(newFd), mToken);
+ return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
}
-status_t InputChannel::write(Parcel& out) const {
- status_t s = out.writeCString(getName().c_str());
- if (s != OK) {
- return s;
+status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
}
-
- s = out.writeStrongBinder(mToken);
- if (s != OK) {
- return s;
- }
-
- s = out.writeUniqueFileDescriptor(mFd);
- return s;
+ return parcel->writeStrongBinder(mToken)
+ ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd);
}
-sp<InputChannel> InputChannel::read(const Parcel& from) {
- std::string name = from.readCString();
- sp<IBinder> token = from.readStrongBinder();
- android::base::unique_fd rawFd;
- status_t fdResult = from.readUniqueFileDescriptor(&rawFd);
- if (fdResult != OK) {
- return nullptr;
+status_t InputChannel::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
}
-
- return InputChannel::create(name, std::move(rawFd), token);
+ mToken = parcel->readStrongBinder();
+ return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd);
}
sp<IBinder> InputChannel::getConnectionToken() const {
@@ -430,9 +417,7 @@
// --- InputPublisher ---
-InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
- mChannel(channel) {
-}
+InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
InputPublisher::~InputPublisher() {
}
@@ -463,7 +448,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
- msg.body.key.seq = seq;
+ msg.header.seq = seq;
msg.body.key.eventId = eventId;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
@@ -484,10 +469,10 @@
uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xScale, float yScale, float xOffset,
- float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
+ MotionClassification classification, const ui::Transform& transform, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
"publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -495,17 +480,18 @@
ATRACE_NAME(message.c_str());
}
if (DEBUG_TRANSPORT_ACTIONS) {
+ std::string transformString;
+ transform.dump(transformString, "");
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
- "xOffset=%.1f, yOffset=%.1f, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32,
+ "pointerCount=%" PRIu32 " transform=%s",
mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
- xPrecision, yPrecision, downTime, eventTime, pointerCount);
+ motionClassificationToString(classification), xPrecision, yPrecision, downTime,
+ eventTime, pointerCount, transformString.c_str());
}
if (!seq) {
@@ -521,7 +507,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::MOTION;
- msg.body.motion.seq = seq;
+ msg.header.seq = seq;
msg.body.motion.eventId = eventId;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
@@ -534,10 +520,12 @@
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.classification = classification;
- msg.body.motion.xScale = xScale;
- msg.body.motion.yScale = yScale;
- msg.body.motion.xOffset = xOffset;
- msg.body.motion.yOffset = yOffset;
+ msg.body.motion.dsdx = transform.dsdx();
+ msg.body.motion.dtdx = transform.dtdx();
+ msg.body.motion.dtdy = transform.dtdy();
+ msg.body.motion.dsdy = transform.dsdy();
+ msg.body.motion.tx = transform.tx();
+ msg.body.motion.ty = transform.ty();
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
@@ -565,7 +553,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::FOCUS;
- msg.body.focus.seq = seq;
+ msg.header.seq = seq;
msg.body.focus.eventId = eventId;
msg.body.focus.hasFocus = hasFocus ? 1 : 0;
msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
@@ -589,17 +577,15 @@
mChannel->getName().c_str(), msg.header.type);
return UNKNOWN_ERROR;
}
- *outSeq = msg.body.finished.seq;
+ *outSeq = msg.header.seq;
*outHandled = msg.body.finished.handled == 1;
return OK;
}
// --- InputConsumer ---
-InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
- mResampleTouch(isTouchResamplingEnabled()),
- mChannel(channel), mMsgDeferred(false) {
-}
+InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
+ : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {}
InputConsumer::~InputConsumer() {
}
@@ -650,7 +636,7 @@
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
- *outSeq = mMsg.body.key.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = keyEvent;
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
@@ -662,9 +648,9 @@
case InputMessage::Type::MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
- Batch& batch = mBatches.editItemAt(batchIndex);
+ Batch& batch = mBatches[batchIndex];
if (canAddSample(batch, &mMsg)) {
- batch.samples.push(mMsg);
+ batch.samples.push_back(mMsg);
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ appended to batch event",
mChannel->getName().c_str());
@@ -675,18 +661,18 @@
// No need to process events that we are going to cancel anyways
const size_t count = batch.samples.size();
for (size_t i = 0; i < count; i++) {
- const InputMessage& msg = batch.samples.itemAt(i);
- sendFinishedSignal(msg.body.motion.seq, false);
+ const InputMessage& msg = batch.samples[i];
+ sendFinishedSignal(msg.header.seq, false);
}
- batch.samples.removeItemsAt(0, count);
- mBatches.removeAt(batchIndex);
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
+ mBatches.erase(mBatches.begin() + batchIndex);
} else {
// We cannot append to the batch in progress, so we need to consume
// the previous batch right now and defer the new message until later.
mMsgDeferred = true;
status_t result = consumeSamples(factory, batch, batch.samples.size(),
outSeq, outEvent);
- mBatches.removeAt(batchIndex);
+ mBatches.erase(mBatches.begin() + batchIndex);
if (result) {
return result;
}
@@ -702,9 +688,9 @@
// Start a new batch if needed.
if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
+ Batch batch;
+ batch.samples.push_back(mMsg);
+ mBatches.push_back(batch);
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' consumer ~ started batch event",
mChannel->getName().c_str());
@@ -717,7 +703,7 @@
updateTouchState(mMsg);
initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = motionEvent;
if (DEBUG_TRANSPORT_ACTIONS) {
@@ -738,7 +724,7 @@
if (!focusEvent) return NO_MEMORY;
initializeFocusEvent(focusEvent, &mMsg);
- *outSeq = mMsg.body.focus.seq;
+ *outSeq = mMsg.header.seq;
*outEvent = focusEvent;
break;
}
@@ -752,10 +738,10 @@
status_t result;
for (size_t i = mBatches.size(); i > 0; ) {
i--;
- Batch& batch = mBatches.editItemAt(i);
+ Batch& batch = mBatches[i];
if (frameTime < 0) {
result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
- mBatches.removeAt(i);
+ mBatches.erase(mBatches.begin() + i);
return result;
}
@@ -770,11 +756,11 @@
result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
const InputMessage* next;
- if (batch.samples.isEmpty()) {
- mBatches.removeAt(i);
+ if (batch.samples.empty()) {
+ mBatches.erase(mBatches.begin() + i);
next = nullptr;
} else {
- next = &batch.samples.itemAt(0);
+ next = &batch.samples[0];
}
if (!result && mResampleTouch) {
resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
@@ -792,20 +778,20 @@
uint32_t chain = 0;
for (size_t i = 0; i < count; i++) {
- InputMessage& msg = batch.samples.editItemAt(i);
+ InputMessage& msg = batch.samples[i];
updateTouchState(msg);
if (i) {
SeqChain seqChain;
- seqChain.seq = msg.body.motion.seq;
+ seqChain.seq = msg.header.seq;
seqChain.chain = chain;
- mSeqChains.push(seqChain);
+ mSeqChains.push_back(seqChain);
addSample(motionEvent, &msg);
} else {
initializeMotionEvent(motionEvent, &msg);
}
- chain = msg.body.motion.seq;
+ chain = msg.header.seq;
}
- batch.samples.removeItemsAt(0, count);
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
*outSeq = chain;
*outEvent = motionEvent;
@@ -827,10 +813,10 @@
case AMOTION_EVENT_ACTION_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index < 0) {
- mTouchStates.push();
+ mTouchStates.push_back({});
index = mTouchStates.size() - 1;
}
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.initialize(deviceId, source);
touchState.addHistory(msg);
break;
@@ -839,7 +825,7 @@
case AMOTION_EVENT_ACTION_MOVE: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.addHistory(msg);
rewriteMessage(touchState, msg);
}
@@ -849,7 +835,7 @@
case AMOTION_EVENT_ACTION_POINTER_DOWN: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
rewriteMessage(touchState, msg);
}
@@ -859,7 +845,7 @@
case AMOTION_EVENT_ACTION_POINTER_UP: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
}
@@ -869,7 +855,7 @@
case AMOTION_EVENT_ACTION_SCROLL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
}
break;
@@ -879,9 +865,9 @@
case AMOTION_EVENT_ACTION_CANCEL: {
ssize_t index = findTouchState(deviceId, source);
if (index >= 0) {
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
rewriteMessage(touchState, msg);
- mTouchStates.removeAt(index);
+ mTouchStates.erase(mTouchStates.begin() + index);
}
break;
}
@@ -938,7 +924,7 @@
return;
}
- TouchState& touchState = mTouchStates.editItemAt(index);
+ TouchState& touchState = mTouchStates[index];
if (touchState.historySize < 1) {
#if DEBUG_RESAMPLING
ALOGD("Not resampled, no history for device.");
@@ -1084,11 +1070,11 @@
size_t chainIndex = 0;
for (size_t i = seqChainCount; i > 0; ) {
i--;
- const SeqChain& seqChain = mSeqChains.itemAt(i);
+ const SeqChain& seqChain = mSeqChains[i];
if (seqChain.seq == currentSeq) {
currentSeq = seqChain.chain;
chainSeqs[chainIndex++] = currentSeq;
- mSeqChains.removeAt(i);
+ mSeqChains.erase(mSeqChains.begin() + i);
}
}
status_t status = OK;
@@ -1102,7 +1088,7 @@
SeqChain seqChain;
seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
seqChain.chain = chainSeqs[chainIndex];
- mSeqChains.push(seqChain);
+ mSeqChains.push_back(seqChain);
if (!chainIndex) break;
chainIndex--;
}
@@ -1117,7 +1103,7 @@
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
- msg.body.finished.seq = seq;
+ msg.header.seq = seq;
msg.body.finished.handled = handled ? 1 : 0;
return mChannel->sendMessage(&msg);
}
@@ -1127,23 +1113,23 @@
}
bool InputConsumer::hasPendingBatch() const {
- return !mBatches.isEmpty();
+ return !mBatches.empty();
}
int32_t InputConsumer::getPendingBatchSource() const {
- if (mBatches.isEmpty()) {
+ if (mBatches.empty()) {
return AINPUT_SOURCE_CLASS_NONE;
}
- const Batch& batch = mBatches.itemAt(0);
- const InputMessage& head = batch.samples.itemAt(0);
+ const Batch& batch = mBatches[0];
+ const InputMessage& head = batch.samples[0];
return head.body.motion.source;
}
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
- const Batch& batch = mBatches.itemAt(i);
- const InputMessage& head = batch.samples.itemAt(0);
+ const Batch& batch = mBatches[i];
+ const InputMessage& head = batch.samples[0];
if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
return i;
}
@@ -1153,7 +1139,7 @@
ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mTouchStates.size(); i++) {
- const TouchState& touchState = mTouchStates.itemAt(i);
+ const TouchState& touchState = mTouchStates[i];
if (touchState.deviceId == deviceId && touchState.source == source) {
return i;
}
@@ -1183,16 +1169,18 @@
pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
}
+ ui::Transform transform;
+ transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
+ msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
msg->body.motion.edgeFlags, msg->body.motion.metaState,
- msg->body.motion.buttonState, msg->body.motion.classification,
- msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
- msg->body.motion.yOffset, msg->body.motion.xPrecision,
- msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
- msg->body.motion.yCursorPosition, msg->body.motion.downTime,
- msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ msg->body.motion.buttonState, msg->body.motion.classification, transform,
+ msg->body.motion.xPrecision, msg->body.motion.yPrecision,
+ msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+ msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
+ pointerProperties, pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1207,7 +1195,7 @@
}
bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
- const InputMessage& head = batch.samples.itemAt(0);
+ const InputMessage& head = batch.samples[0];
uint32_t pointerCount = msg->body.motion.pointerCount;
if (head.body.motion.pointerCount != pointerCount
|| head.body.motion.action != msg->body.motion.action) {
@@ -1225,11 +1213,72 @@
ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
size_t numSamples = batch.samples.size();
size_t index = 0;
- while (index < numSamples
- && batch.samples.itemAt(index).body.motion.eventTime <= time) {
+ while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) {
index += 1;
}
return ssize_t(index) - 1;
}
+std::string InputConsumer::dump() const {
+ std::string out;
+ out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n";
+ out = out + "mChannel = " + mChannel->getName() + "\n";
+ out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
+ if (mMsgDeferred) {
+ out = out + "mMsg : " + InputMessage::typeToString(mMsg.header.type) + "\n";
+ }
+ out += "Batches:\n";
+ for (const Batch& batch : mBatches) {
+ out += " Batch:\n";
+ for (const InputMessage& msg : batch.samples) {
+ out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
+ InputMessage::typeToString(msg.header.type));
+ switch (msg.header.type) {
+ case InputMessage::Type::KEY: {
+ out += android::base::StringPrintf("action=%s keycode=%" PRId32,
+ KeyEvent::actionToString(
+ msg.body.key.action),
+ msg.body.key.keyCode);
+ break;
+ }
+ case InputMessage::Type::MOTION: {
+ out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action);
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ const float x = msg.body.motion.pointers[i].coords.getX();
+ const float y = msg.body.motion.pointers[i].coords.getY();
+ out += android::base::StringPrintf("\n Pointer %" PRIu32
+ " : x=%.1f y=%.1f",
+ i, x, y);
+ }
+ break;
+ }
+ case InputMessage::Type::FINISHED: {
+ out += android::base::StringPrintf("handled=%s",
+ toString(msg.body.finished.handled));
+ break;
+ }
+ case InputMessage::Type::FOCUS: {
+ out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
+ toString(msg.body.focus.hasFocus),
+ toString(msg.body.focus.inTouchMode));
+ break;
+ }
+ }
+ out += "\n";
+ }
+ }
+ if (mBatches.empty()) {
+ out += " <empty>\n";
+ }
+ out += "mSeqChains:\n";
+ for (const SeqChain& chain : mSeqChains) {
+ out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq,
+ chain.chain);
+ }
+ if (mSeqChains.empty()) {
+ out += " <empty>\n";
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 6e4c97d..51190a0 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -14,20 +14,20 @@
* limitations under the License.
*/
+#include <type_traits>
#define LOG_TAG "InputWindow"
#define LOG_NDEBUG 0
+#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
-#include <input/InputWindow.h>
#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
namespace android {
+
// --- InputWindowInfo ---
void InputWindowInfo::addTouchableRegion(const Rect& region) {
touchableRegion.orSelf(region);
@@ -42,21 +42,8 @@
&& y >= frameTop && y < frameBottom;
}
-bool InputWindowInfo::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD
- || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
- || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
- || layoutParamsType == TYPE_STATUS_BAR
- || layoutParamsType == TYPE_NAVIGATION_BAR
- || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
- || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
- || layoutParamsType == TYPE_DOCK_DIVIDER
- || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
- || layoutParamsType == TYPE_INPUT_CONSUMER;
-}
-
bool InputWindowInfo::supportsSplitTouch() const {
- return layoutParamsFlags & FLAG_SPLIT_TOUCH;
+ return flags.test(Flag::SPLIT_TOUCH);
}
bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
@@ -64,94 +51,150 @@
&& frameTop < other->frameBottom && frameBottom > other->frameTop;
}
-status_t InputWindowInfo::write(Parcel& output) const {
+bool InputWindowInfo::operator==(const InputWindowInfo& info) const {
+ return info.token == token && info.id == id && info.name == name && info.flags == flags &&
+ info.type == type && info.dispatchingTimeout == dispatchingTimeout &&
+ info.frameLeft == frameLeft && info.frameTop == frameTop &&
+ info.frameRight == frameRight && info.frameBottom == frameBottom &&
+ info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
+ info.transform == transform &&
+ info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
+ info.canReceiveKeys == canReceiveKeys && info.trustedOverlay == trustedOverlay &&
+ info.hasFocus == hasFocus && info.hasWallpaper == hasWallpaper &&
+ info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.inputFeatures == inputFeatures && info.displayId == displayId &&
+ info.portalToDisplayId == portalToDisplayId &&
+ info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
+ info.applicationInfo.name == applicationInfo.name &&
+ info.applicationInfo.token == applicationInfo.token &&
+ info.applicationInfo.dispatchingTimeout == applicationInfo.dispatchingTimeout;
+}
+
+status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
if (name.empty()) {
- output.writeInt32(0);
+ parcel->writeInt32(0);
return OK;
}
- output.writeInt32(1);
- status_t s = output.writeStrongBinder(token);
- if (s != OK) return s;
+ parcel->writeInt32(1);
- output.writeInt32(id);
- output.writeString8(String8(name.c_str()));
- output.writeInt32(layoutParamsFlags);
- output.writeInt32(layoutParamsType);
- output.writeInt64(dispatchingTimeout);
- output.writeInt32(frameLeft);
- output.writeInt32(frameTop);
- output.writeInt32(frameRight);
- output.writeInt32(frameBottom);
- output.writeInt32(surfaceInset);
- output.writeFloat(globalScaleFactor);
- output.writeFloat(windowXScale);
- output.writeFloat(windowYScale);
- output.writeBool(visible);
- output.writeBool(canReceiveKeys);
- output.writeBool(hasFocus);
- output.writeBool(hasWallpaper);
- output.writeBool(paused);
- output.writeInt32(ownerPid);
- output.writeInt32(ownerUid);
- output.writeInt32(inputFeatures);
- output.writeInt32(displayId);
- output.writeInt32(portalToDisplayId);
- applicationInfo.write(output);
- output.write(touchableRegion);
- output.writeBool(replaceTouchableRegionWithCrop);
- output.writeStrongBinder(touchableRegionCropHandle.promote());
- return OK;
+ status_t status = parcel->writeStrongBinder(token) ?:
+ parcel->writeInt64(dispatchingTimeout.count()) ?:
+ parcel->writeInt32(id) ?:
+ parcel->writeUtf8AsUtf16(name) ?:
+ parcel->writeInt32(flags.get()) ?:
+ parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?:
+ parcel->writeInt32(frameLeft) ?:
+ parcel->writeInt32(frameTop) ?:
+ parcel->writeInt32(frameRight) ?:
+ parcel->writeInt32(frameBottom) ?:
+ parcel->writeInt32(surfaceInset) ?:
+ parcel->writeFloat(globalScaleFactor) ?:
+ parcel->writeFloat(transform.dsdx()) ?:
+ parcel->writeFloat(transform.dtdx()) ?:
+ parcel->writeFloat(transform.tx()) ?:
+ parcel->writeFloat(transform.dtdy()) ?:
+ parcel->writeFloat(transform.dsdy()) ?:
+ parcel->writeFloat(transform.ty()) ?:
+ parcel->writeBool(visible) ?:
+ parcel->writeBool(canReceiveKeys) ?:
+ parcel->writeBool(hasFocus) ?:
+ parcel->writeBool(hasWallpaper) ?:
+ parcel->writeBool(paused) ?:
+ parcel->writeBool(trustedOverlay) ?:
+ parcel->writeInt32(ownerPid) ?:
+ parcel->writeInt32(ownerUid) ?:
+ parcel->writeInt32(inputFeatures.get()) ?:
+ parcel->writeInt32(displayId) ?:
+ parcel->writeInt32(portalToDisplayId) ?:
+ applicationInfo.writeToParcel(parcel) ?:
+ parcel->write(touchableRegion) ?:
+ parcel->writeBool(replaceTouchableRegionWithCrop) ?:
+ parcel->writeStrongBinder(touchableRegionCropHandle.promote());
+
+ return status;
}
-InputWindowInfo InputWindowInfo::read(const Parcel& from) {
- InputWindowInfo ret;
-
- if (from.readInt32() == 0) {
- return ret;
+status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ if (parcel->readInt32() == 0) {
+ return OK;
}
- ret.token = from.readStrongBinder();
- ret.id = from.readInt32();
- ret.name = from.readString8().c_str();
- ret.layoutParamsFlags = from.readInt32();
- ret.layoutParamsType = from.readInt32();
- ret.dispatchingTimeout = from.readInt64();
- ret.frameLeft = from.readInt32();
- ret.frameTop = from.readInt32();
- ret.frameRight = from.readInt32();
- ret.frameBottom = from.readInt32();
- ret.surfaceInset = from.readInt32();
- ret.globalScaleFactor = from.readFloat();
- ret.windowXScale = from.readFloat();
- ret.windowYScale = from.readFloat();
- ret.visible = from.readBool();
- ret.canReceiveKeys = from.readBool();
- ret.hasFocus = from.readBool();
- ret.hasWallpaper = from.readBool();
- ret.paused = from.readBool();
- ret.ownerPid = from.readInt32();
- ret.ownerUid = from.readInt32();
- ret.inputFeatures = from.readInt32();
- ret.displayId = from.readInt32();
- ret.portalToDisplayId = from.readInt32();
- ret.applicationInfo = InputApplicationInfo::read(from);
- from.read(ret.touchableRegion);
- ret.replaceTouchableRegionWithCrop = from.readBool();
- ret.touchableRegionCropHandle = from.readStrongBinder();
+ token = parcel->readStrongBinder();
+ dispatchingTimeout = static_cast<decltype(dispatchingTimeout)>(parcel->readInt64());
+ status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name);
+ if (status != OK) {
+ return status;
+ }
- return ret;
-}
+ flags = Flags<Flag>(parcel->readInt32());
+ type = static_cast<Type>(parcel->readInt32());
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ status = parcel->readInt32(&frameLeft) ?:
+ parcel->readInt32(&frameTop) ?:
+ parcel->readInt32(&frameRight) ?:
+ parcel->readInt32(&frameBottom) ?:
+ parcel->readInt32(&surfaceInset) ?:
+ parcel->readFloat(&globalScaleFactor) ?:
+ parcel->readFloat(&dsdx) ?:
+ parcel->readFloat(&dtdx) ?:
+ parcel->readFloat(&tx) ?:
+ parcel->readFloat(&dtdy) ?:
+ parcel->readFloat(&dsdy) ?:
+ parcel->readFloat(&ty) ?:
+ parcel->readBool(&visible) ?:
+ parcel->readBool(&canReceiveKeys) ?:
+ parcel->readBool(&hasFocus) ?:
+ parcel->readBool(&hasWallpaper) ?:
+ parcel->readBool(&paused) ?:
+ parcel->readBool(&trustedOverlay) ?:
+ parcel->readInt32(&ownerPid) ?:
+ parcel->readInt32(&ownerUid);
-InputWindowInfo::InputWindowInfo(const Parcel& from) {
- *this = read(from);
+ if (status != OK) {
+ return status;
+ }
+
+ inputFeatures = Flags<Feature>(parcel->readInt32());
+ status = parcel->readInt32(&displayId) ?:
+ parcel->readInt32(&portalToDisplayId) ?:
+ applicationInfo.readFromParcel(parcel) ?:
+ parcel->read(touchableRegion) ?:
+ parcel->readBool(&replaceTouchableRegionWithCrop);
+
+ if (status != OK) {
+ return status;
+ }
+
+ touchableRegionCropHandle = parcel->readStrongBinder();
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+ return OK;
}
// --- InputWindowHandle ---
-InputWindowHandle::InputWindowHandle() {
+InputWindowHandle::InputWindowHandle() {}
+
+InputWindowHandle::~InputWindowHandle() {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {}
+
+InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {}
+
+status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const {
+ return mInfo.writeToParcel(parcel);
}
-InputWindowHandle::~InputWindowHandle() {
+status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) {
+ return mInfo.readFromParcel(parcel);
}
void InputWindowHandle::releaseChannel() {
@@ -165,5 +208,4 @@
void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
mInfo = handle->mInfo;
}
-
} // namespace android
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 56900c1..25025f2 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -105,8 +105,7 @@
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
- std::string path(getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
+ std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT));
if (path.empty()) {
return NAME_NOT_FOUND;
}
@@ -122,8 +121,8 @@
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
- std::string path = getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP);
+ std::string path =
+ getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP);
if (path.empty()) {
return NAME_NOT_FOUND;
}
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index c6cc4fc..7c28ac5 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -104,107 +104,73 @@
// --- VelocityTracker ---
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use. Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
- mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
- char value[PROPERTY_VALUE_MAX];
-
- // Allow the default strategy to be overridden using a system property for debugging.
- if (!strategy) {
- int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
- if (length > 0) {
- strategy = value;
- } else {
- strategy = DEFAULT_STRATEGY;
- }
- }
-
+VelocityTracker::VelocityTracker(const Strategy strategy)
+ : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
// Configure the strategy.
if (!configureStrategy(strategy)) {
- ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
- if (!configureStrategy(DEFAULT_STRATEGY)) {
- LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
- strategy);
+ ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+ if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+ LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+ "'!",
+ strategy);
}
}
}
VelocityTracker::~VelocityTracker() {
- delete mStrategy;
}
-bool VelocityTracker::configureStrategy(const char* strategy) {
- mStrategy = createStrategy(strategy);
+bool VelocityTracker::configureStrategy(Strategy strategy) {
+ if (strategy == VelocityTracker::Strategy::DEFAULT) {
+ mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+ } else {
+ mStrategy = createStrategy(strategy);
+ }
return mStrategy != nullptr;
}
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
- if (!strcmp("impulse", strategy)) {
- // Physical model of pushing an object. Quality: VERY GOOD.
- // Works with duplicate coordinates, unclean finger liftoff.
- return new ImpulseVelocityTrackerStrategy();
- }
- if (!strcmp("lsq1", strategy)) {
- // 1st order least squares. Quality: POOR.
- // Frequently underfits the touch data especially when the finger accelerates
- // or changes direction. Often underestimates velocity. The direction
- // is overly influenced by historical touch points.
- return new LeastSquaresVelocityTrackerStrategy(1);
- }
- if (!strcmp("lsq2", strategy)) {
- // 2nd order least squares. Quality: VERY GOOD.
- // Pretty much ideal, but can be confused by certain kinds of touch data,
- // particularly if the panel has a tendency to generate delayed,
- // duplicate or jittery touch coordinates when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(2);
- }
- if (!strcmp("lsq3", strategy)) {
- // 3rd order least squares. Quality: UNUSABLE.
- // Frequently overfits the touch data yielding wildly divergent estimates
- // of the velocity when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(3);
- }
- if (!strcmp("wlsq2-delta", strategy)) {
- // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
- }
- if (!strcmp("wlsq2-central", strategy)) {
- // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
- }
- if (!strcmp("wlsq2-recent", strategy)) {
- // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
- }
- if (!strcmp("int1", strategy)) {
- // 1st order integrating filter. Quality: GOOD.
- // Not as good as 'lsq2' because it cannot estimate acceleration but it is
- // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
- // the velocity of a fling but this strategy tends to respond to changes in
- // direction more quickly and accurately.
- return new IntegratingVelocityTrackerStrategy(1);
- }
- if (!strcmp("int2", strategy)) {
- // 2nd order integrating filter. Quality: EXPERIMENTAL.
- // For comparison purposes only. Unlike 'int1' this strategy can compensate
- // for acceleration but it typically overestimates the effect.
- return new IntegratingVelocityTrackerStrategy(2);
- }
- if (!strcmp("legacy", strategy)) {
- // Legacy velocity tracker algorithm. Quality: POOR.
- // For comparison purposes only. This algorithm is strongly influenced by
- // old data points, consistently underestimates velocity and takes a very long
- // time to adjust to changes in direction.
- return new LegacyVelocityTrackerStrategy();
+std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
+ VelocityTracker::Strategy strategy) {
+ switch (strategy) {
+ case VelocityTracker::Strategy::IMPULSE:
+ return std::make_unique<ImpulseVelocityTrackerStrategy>();
+
+ case VelocityTracker::Strategy::LSQ1:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::LSQ2:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LSQ3:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3);
+
+ case VelocityTracker::Strategy::WLSQ2_DELTA:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_DELTA);
+ case VelocityTracker::Strategy::WLSQ2_CENTRAL:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_CENTRAL);
+ case VelocityTracker::Strategy::WLSQ2_RECENT:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_RECENT);
+
+ case VelocityTracker::Strategy::INT1:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::INT2:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LEGACY:
+ return std::make_unique<LegacyVelocityTrackerStrategy>();
+
+ default:
+ break;
}
return nullptr;
}
diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl
new file mode 100644
index 0000000..a5034a4
--- /dev/null
+++ b/libs/input/android/FocusRequest.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android;
+
+/** @hide */
+parcelable FocusRequest {
+ /**
+ * Input channel token used to identify the window that should gain focus.
+ */
+ IBinder token;
+ /**
+ * The token that the caller expects currently to be focused. If the
+ * specified token does not match the currently focused window, this request will be dropped.
+ * If the specified focused token matches the currently focused window, the call will succeed.
+ * Set this to "null" if this call should succeed no matter what the currently focused token
+ * is.
+ */
+ @nullable IBinder focusedToken;
+ /**
+ * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus
+ * change. This determines which request gets precedence if there is a focus change request
+ * from another source such as pointer down.
+ */
+ long timestamp;
+}
diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/InputChannel.aidl
new file mode 100644
index 0000000..c2d1112
--- /dev/null
+++ b/libs/input/android/InputChannel.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android;
+
+parcelable InputChannel cpp_header "input/InputTransport.h";
diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/input/android/InputWindowInfo.aidl
new file mode 100644
index 0000000..eeaf400
--- /dev/null
+++ b/libs/input/android/InputWindowInfo.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android;
+
+parcelable InputWindowInfo cpp_header "input/InputWindow.h";
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
new file mode 100644
index 0000000..5eefad3
--- /dev/null
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlinger
+{
+ // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the
+ // ordering when needed.
+ // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer
+ // shouldn't be a concern.
+ oneway void setInputWindows(in InputWindowInfo[] inputHandles,
+ in @nullable ISetInputWindowsListener setInputWindowsListener);
+ void registerInputChannel(in InputChannel channel);
+ void unregisterInputChannel(in InputChannel channel);
+ /**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ */
+ oneway void setFocusedWindow(in FocusRequest request);
+}
diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/input/android/os/ISetInputWindowsListener.aidl
new file mode 100644
index 0000000..bb58fb6
--- /dev/null
+++ b/libs/input/android/os/ISetInputWindowsListener.aidl
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** @hide */
+oneway interface ISetInputWindowsListener
+{
+ void onSetInputWindowsFinished();
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 3b57146..9782c1a 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,6 +2,7 @@
cc_test {
name: "libinput_tests",
srcs: [
+ "Flags_test.cpp",
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputDevice_test.cpp",
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
new file mode 100644
index 0000000..0dbb4cf
--- /dev/null
+++ b/libs/input/tests/Flags_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2020 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 <gtest/gtest.h>
+#include <input/Flags.h>
+
+#include <type_traits>
+
+namespace android::test {
+
+using namespace android::flag_operators;
+
+enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+
+TEST(Flags, Test) {
+ Flags<TestFlags> flags = TestFlags::ONE;
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_FALSE(flags.test(TestFlags::THREE));
+}
+
+TEST(Flags, Any) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.any(TestFlags::ONE));
+ ASSERT_TRUE(flags.any(TestFlags::TWO));
+ ASSERT_FALSE(flags.any(TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO));
+ ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, All) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.all(TestFlags::ONE));
+ ASSERT_TRUE(flags.all(TestFlags::TWO));
+ ASSERT_FALSE(flags.all(TestFlags::THREE));
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO));
+ ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE));
+ ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, DefaultConstructor_hasNoFlagsSet) {
+ Flags<TestFlags> flags;
+ ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) {
+ Flags<TestFlags> flags;
+ flags = ~flags;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) {
+ Flags<TestFlags> flags = TestFlags::TWO;
+ flags = ~flags;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withNewFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE;
+ Flags<TestFlags> flags2 = flags | TestFlags::TWO;
+ ASSERT_FALSE(flags2.test(TestFlags::THREE));
+ ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrOperator_withExistingFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> flags2 = flags | TestFlags::THREE;
+ ASSERT_FALSE(flags2.test(TestFlags::TWO));
+ ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE));
+}
+
+TEST(Flags, OrEqualsOperator_withNewFlag) {
+ Flags<TestFlags> flags;
+ flags |= TestFlags::THREE;
+ ASSERT_TRUE(flags.test(TestFlags::THREE));
+ ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, OrEqualsOperator_withExistingFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ flags |= TestFlags::THREE;
+ ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withOneSetFlag) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & TestFlags::THREE;
+ ASSERT_TRUE(andFlags.test(TestFlags::THREE));
+ ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withMultipleSetFlags) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & (TestFlags::ONE | TestFlags::THREE);
+ ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE));
+ ASSERT_FALSE(andFlags.test(TestFlags::TWO));
+}
+
+TEST(Flags, AndOperator_withNoSetFlags) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE;
+ Flags<TestFlags> andFlags = flags & TestFlags::TWO;
+ ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, Equality) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags1, flags2);
+}
+
+TEST(Flags, Inequality) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::THREE;
+ ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, EqualsOperator) {
+ Flags<TestFlags> flags;
+ flags = TestFlags::ONE;
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE));
+}
+
+TEST(Flags, EqualsOperator_DontShareState) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2 = flags1;
+ ASSERT_EQ(flags1, flags2);
+
+ flags1 &= TestFlags::TWO;
+ ASSERT_NE(flags1, flags2);
+}
+
+TEST(Flags, String_NoFlags) {
+ Flags<TestFlags> flags;
+ ASSERT_EQ(flags.string(), "0x0");
+}
+
+TEST(Flags, String_KnownValues) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_EQ(flags.string(), "ONE | TWO");
+}
+
+TEST(Flags, String_UnknownValues) {
+ auto flags = Flags<TestFlags>(0b1011);
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+}
+
+TEST(FlagsIterator, IteratesOverAllFlags) {
+ Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO;
+ Flags<TestFlags> flags2;
+ for (TestFlags f : flags1) {
+ flags2 |= f;
+ }
+ ASSERT_EQ(flags2, flags1);
+}
+
+TEST(FlagsIterator, IteratesInExpectedOrder) {
+ const std::vector<TestFlags> flagOrder = {TestFlags::ONE, TestFlags::TWO};
+ Flags<TestFlags> flags;
+ for (TestFlags f : flagOrder) {
+ flags |= f;
+ }
+
+ size_t idx = 0;
+ auto iter = flags.begin();
+ while (iter != flags.end() && idx < flagOrder.size()) {
+ // Make sure the order is what we expect
+ ASSERT_EQ(*iter, flagOrder[idx]);
+ iter++;
+ idx++;
+ }
+ ASSERT_EQ(iter, flags.end());
+}
+TEST(FlagsIterator, PostFixIncrement) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ auto iter = flags.begin();
+ ASSERT_EQ(*(iter++), TestFlags::ONE);
+ ASSERT_EQ(*iter, TestFlags::TWO);
+ ASSERT_EQ(*(iter++), TestFlags::TWO);
+ ASSERT_EQ(iter, flags.end());
+}
+
+TEST(FlagsIterator, PreFixIncrement) {
+ Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ auto iter = flags.begin();
+ ASSERT_EQ(*++iter, TestFlags::TWO);
+ ASSERT_EQ(++iter, flags.end());
+}
+
+TEST(FlagNames, RuntimeFlagName) {
+ TestFlags f = TestFlags::ONE;
+ ASSERT_EQ(flag_name(f), "ONE");
+}
+
+TEST(FlagNames, RuntimeUnknownFlagName) {
+ TestFlags f = static_cast<TestFlags>(0x8);
+ ASSERT_EQ(flag_name(f), std::nullopt);
+}
+
+TEST(FlagNames, CompileTimeFlagName) {
+ static_assert(flag_name<TestFlags::TWO>() == "TWO");
+}
+
+} // namespace android::test
\ No newline at end of file
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index ada275d..0661261 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -23,6 +23,7 @@
#include <errno.h>
#include <binder/Binder.h>
+#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
#include <utils/StopWatch.h>
@@ -32,9 +33,6 @@
namespace android {
class InputChannelTest : public testing::Test {
-protected:
- virtual void SetUp() { }
- virtual void TearDown() { }
};
@@ -46,7 +44,7 @@
android::base::unique_fd sendFd(pipe.sendFd);
- sp<InputChannel> inputChannel =
+ std::unique_ptr<InputChannel> inputChannel =
InputChannel::create("channel name", std::move(sendFd), new BBinder());
EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
@@ -61,14 +59,14 @@
TEST_F(InputChannelTest, SetAndGetToken) {
Pipe pipe;
sp<IBinder> token = new BBinder();
- sp<InputChannel> channel =
+ std::unique_ptr<InputChannel> channel =
InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
EXPECT_EQ(token, channel->getConnectionToken());
}
TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -102,7 +100,7 @@
InputMessage clientReply;
memset(&clientReply, 0, sizeof(InputMessage));
clientReply.header.type = InputMessage::Type::FINISHED;
- clientReply.body.finished.seq = 0x11223344;
+ clientReply.header.seq = 0x11223344;
clientReply.body.finished.handled = true;
EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
<< "client channel should be able to send message to server channel";
@@ -112,14 +110,14 @@
<< "server channel should be able to receive message from client channel";
EXPECT_EQ(clientReply.header.type, serverReply.header.type)
<< "server channel should receive the correct message from client channel";
- EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq)
+ EXPECT_EQ(clientReply.header.seq, serverReply.header.seq)
<< "server channel should receive the correct message from client channel";
EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled)
<< "server channel should receive the correct message from client channel";
}
TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -133,7 +131,7 @@
}
TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -141,7 +139,7 @@
ASSERT_EQ(OK, result)
<< "should have successfully opened a channel pair";
- serverChannel.clear(); // close server channel
+ serverChannel.reset(); // close server channel
InputMessage msg;
EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
@@ -149,7 +147,7 @@
}
TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
@@ -157,7 +155,7 @@
ASSERT_EQ(OK, result)
<< "should have successfully opened a channel pair";
- serverChannel.clear(); // close server channel
+ serverChannel.reset(); // close server channel
InputMessage msg;
msg.header.type = InputMessage::Type::KEY;
@@ -166,7 +164,7 @@
}
TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
ASSERT_EQ(OK, result)
@@ -180,7 +178,7 @@
InputMessage serverMsg = {}, clientMsg;
serverMsg.header.type = InputMessage::Type::MOTION;
- serverMsg.body.motion.seq = 1;
+ serverMsg.header.seq = 1;
serverMsg.body.motion.pointerCount = 1;
for (MotionClassification classification : classifications) {
@@ -197,5 +195,36 @@
}
}
+TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ status_t result =
+ InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ InputChannel chan;
+ Parcel parcel;
+ ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel));
+ parcel.setDataPosition(0);
+ chan.readFromParcel(&parcel);
+
+ EXPECT_EQ(chan == *serverChannel, true)
+ << "inputchannel should be equal after parceling and unparceling.\n"
+ << "name " << chan.getName() << " name " << serverChannel->getName();
+}
+
+TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ status_t result =
+ InputChannel::openInputChannelPair("channel dup", serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ std::unique_ptr<InputChannel> dupChan = serverChannel->dup();
+
+ EXPECT_EQ(*serverChannel == *dupChan, true) << "inputchannel should be equal after duplication";
+}
} // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 553dc4c..069bc0e 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -225,6 +225,7 @@
static constexpr float Y_OFFSET = 1.1;
int32_t mId;
+ ui::Transform mTransform;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
@@ -233,6 +234,7 @@
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
+ mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
@@ -266,7 +268,7 @@
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
pointerCoords);
@@ -326,8 +328,7 @@
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
- EXPECT_EQ(X_SCALE, event->getXScale());
- EXPECT_EQ(Y_SCALE, event->getYScale());
+ EXPECT_EQ(mTransform, event->getTransform());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -545,7 +546,7 @@
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
}
-static void setRotationMatrix(float matrix[9], float angle) {
+static void setRotationMatrix(std::array<float, 9>& matrix, float angle) {
float sin = sinf(angle);
float cos = cosf(angle);
matrix[0] = cos;
@@ -584,13 +585,14 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
- 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+ MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -606,7 +608,7 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
// Apply a rotation about the origin by ROTATION degrees clockwise.
- float matrix[9];
+ std::array<float, 9> matrix;
setRotationMatrix(matrix, ROTATION * PI_180);
event.transform(matrix);
@@ -648,11 +650,12 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
for (MotionClassification classification : classifications) {
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/,
- 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
@@ -670,10 +673,11 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
- AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0,
- 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+ AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 8e2eec8..e1f2562 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -30,33 +30,21 @@
class InputPublisherAndConsumerTest : public testing::Test {
protected:
- sp<InputChannel> serverChannel, clientChannel;
- InputPublisher* mPublisher;
- InputConsumer* mConsumer;
+ std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputPublisher> mPublisher;
+ std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
- virtual void SetUp() {
+ void SetUp() override {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
ASSERT_EQ(OK, result);
+ mServerChannel = std::move(serverChannel);
+ mClientChannel = std::move(clientChannel);
- mPublisher = new InputPublisher(serverChannel);
- mConsumer = new InputConsumer(clientChannel);
- }
-
- virtual void TearDown() {
- if (mPublisher) {
- delete mPublisher;
- mPublisher = nullptr;
- }
-
- if (mConsumer) {
- delete mConsumer;
- mConsumer = nullptr;
- }
-
- serverChannel.clear();
- clientChannel.clear();
+ mPublisher = std::make_unique<InputPublisher>(mServerChannel);
+ mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
void PublishAndConsumeKeyEvent();
@@ -65,8 +53,8 @@
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
- EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
- EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+ EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get());
+ EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get());
}
void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
@@ -185,12 +173,13 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
+ ui::Transform transform;
+ transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
- classification, xScale, yScale, xOffset, yOffset,
- xPrecision, yPrecision, xCursorPosition,
- yCursorPosition, downTime, eventTime, pointerCount,
- pointerProperties, pointerCoords);
+ classification, transform, xPrecision, yPrecision,
+ xCursorPosition, yCursorPosition, downTime, eventTime,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -218,8 +207,7 @@
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
EXPECT_EQ(classification, motionEvent->getClassification());
- EXPECT_EQ(xScale, motionEvent->getXScale());
- EXPECT_EQ(yScale, motionEvent->getYScale());
+ EXPECT_EQ(transform, motionEvent->getTransform());
EXPECT_EQ(xOffset, motionEvent->getXOffset());
EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
@@ -338,10 +326,10 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -354,10 +342,10 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
@@ -375,10 +363,10 @@
pointerCoords[i].clear();
}
+ ui::Transform identityTransform;
status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index d1cb527..1b58460 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -22,17 +22,19 @@
#include <input/InputWindow.h>
#include <input/InputTransport.h>
+using std::chrono_literals::operator""s;
+
namespace android {
namespace test {
TEST(InputWindowInfo, ParcellingWithoutToken) {
- InputWindowInfo i;
+ InputWindowInfo i, i2;
i.token = nullptr;
Parcel p;
- ASSERT_EQ(OK, i.write(p));
+ ASSERT_EQ(OK, i.writeToParcel(&p));
p.setDataPosition(0);
- InputWindowInfo i2 = InputWindowInfo::read(p);
+ i2.readFromParcel(&p);
ASSERT_TRUE(i2.token == nullptr);
}
@@ -42,17 +44,16 @@
i.token = new BBinder();
i.id = 1;
i.name = "Foobar";
- i.layoutParamsFlags = 7;
- i.layoutParamsType = 39;
- i.dispatchingTimeout = 12;
+ i.flags = InputWindowInfo::Flag::SLIPPERY;
+ i.type = InputWindowInfo::Type::INPUT_METHOD;
+ i.dispatchingTimeout = 12s;
i.frameLeft = 93;
i.frameTop = 34;
i.frameRight = 16;
i.frameBottom = 19;
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
- i.windowXScale = 0.4;
- i.windowYScale = 0.5;
+ i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.visible = false;
i.canReceiveKeys = false;
i.hasFocus = false;
@@ -60,22 +61,22 @@
i.paused = false;
i.ownerPid = 19;
i.ownerUid = 24;
- i.inputFeatures = 29;
+ i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
i.displayId = 34;
i.portalToDisplayId = 2;
i.replaceTouchableRegionWithCrop = true;
i.touchableRegionCropHandle = touchableRegionCropHandle;
Parcel p;
- i.write(p);
-
+ i.writeToParcel(&p);
p.setDataPosition(0);
- InputWindowInfo i2 = InputWindowInfo::read(p);
+ InputWindowInfo i2;
+ i2.readFromParcel(&p);
ASSERT_EQ(i.token, i2.token);
ASSERT_EQ(i.id, i2.id);
ASSERT_EQ(i.name, i2.name);
- ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
- ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
+ ASSERT_EQ(i.flags, i2.flags);
+ ASSERT_EQ(i.type, i2.type);
ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
ASSERT_EQ(i.frameLeft, i2.frameLeft);
ASSERT_EQ(i.frameTop, i2.frameTop);
@@ -83,8 +84,7 @@
ASSERT_EQ(i.frameBottom, i2.frameBottom);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
- ASSERT_EQ(i.windowXScale, i2.windowXScale);
- ASSERT_EQ(i.windowYScale, i2.windowYScale);
+ ASSERT_EQ(i.transform, i2.transform);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
ASSERT_EQ(i.hasFocus, i2.hasFocus);
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 1fe7bb9..3c5fb22 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -34,8 +34,7 @@
void TestInputMessageAlignment() {
CHECK_OFFSET(InputMessage, body, 8);
- CHECK_OFFSET(InputMessage::Body::Key, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Key, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Key, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Key, source, 20);
@@ -49,8 +48,7 @@
CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4);
+ CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -64,27 +62,29 @@
CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
- CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
- CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
- CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
- CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
- CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
- CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
- CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdx, 100);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdy, 104);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdy, 108);
+ CHECK_OFFSET(InputMessage::Body::Motion, tx, 112);
+ CHECK_OFFSET(InputMessage::Body::Motion, ty, 116);
+ CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 120);
+ CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
+ CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
+ CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
- CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
- CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14);
+ CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
- CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
void TestHeaderSize() {
+ CHECK_OFFSET(InputMessage::Header, type, 0);
+ CHECK_OFFSET(InputMessage::Header, seq, 4);
static_assert(sizeof(InputMessage::Header) == 8);
}
@@ -98,7 +98,7 @@
offsetof(InputMessage::Body::Motion, pointers) +
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
static_assert(sizeof(InputMessage::Body::Finished) == 8);
- static_assert(sizeof(InputMessage::Body::Focus) == 16);
+ static_assert(sizeof(InputMessage::Body::Focus) == 8);
}
// --- VerifiedInputEvent ---
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index bf452c0..e7db4b0 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,12 +176,12 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
+ ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
@@ -191,8 +191,9 @@
return events;
}
-static void computeAndCheckVelocity(const char* strategy,
- const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) {
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+ const std::vector<MotionEventEntry>& motions, int32_t axis,
+ float targetVelocity) {
VelocityTracker vt(strategy);
float Vx, Vy;
@@ -217,7 +218,7 @@
static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
const std::array<float, 3>& coefficients) {
- VelocityTracker vt("lsq2");
+ VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
std::vector<MotionEvent> events = createMotionEventStream(motions);
for (MotionEvent event : events) {
vt.addMovement(&event);
@@ -238,36 +239,34 @@
// It is difficult to determine the correct answer here, but at least the direction
// of the reported velocity should be positive.
std::vector<MotionEventEntry> motions = {
- {0ms, {{ 273, NAN}}},
- {12585us, {{293, NAN}}},
- {14730us, {{293, NAN}}},
- {14730us, {{293, NAN}}}, // ACTION_UP
+ {0ms, {{273, 0}}},
+ {12585us, {{293, 0}}},
+ {14730us, {{293, 0}}},
+ {14730us, {{293, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1600);
}
TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
// Same coordinate is reported 3 times in a row
std::vector<MotionEventEntry> motions = {
- { 0ms, {{293, NAN}} },
- { 6132us, {{293, NAN}} },
- { 11283us, {{293, NAN}} },
- { 11283us, {{293, NAN}} }, // ACTION_UP
+ {0ms, {{293, 0}}},
+ {6132us, {{293, 0}}},
+ {11283us, {{293, 0}}},
+ {11283us, {{293, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
}
TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
// Fixed velocity at 5 points per 10 milliseconds
std::vector<MotionEventEntry> motions = {
- { 0ms, {{0, NAN}} },
- { 10ms, {{5, NAN}} },
- { 20ms, {{10, NAN}} },
- { 20ms, {{10, NAN}} }, // ACTION_UP
+ {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
}
@@ -297,8 +296,10 @@
{ 96948871ns, {{274.79245, 428.113159}} },
{ 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 623.577637);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 5970.7309);
}
// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -339,10 +340,14 @@
{ 235089162955851ns, {{560.66, 843.82}} },
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181);
- computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 872.794617);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 951.698181);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3604.819336);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3044.966064);
}
@@ -368,8 +373,10 @@
{ 235110660368000ns, {{530.00, 980.00}} },
{ 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4096.583008);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3455.094238);
}
@@ -396,10 +403,14 @@
{ 792629200000ns, {{619.00, 1115.00}} },
{ 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 574.33429);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 617.40564);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -2361.982666);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -2500.055664);
}
@@ -426,10 +437,14 @@
{ 235160520366000ns, {{679.00, 814.00}} },
{ 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1274.141724);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 1438.53186);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3001.4348);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3695.859619);
}
@@ -452,8 +467,10 @@
{ 847237986000ns, {{610.00, 1095.00}} },
{ 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4280.07959);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -4241.004395);
}
@@ -476,8 +493,10 @@
{ 235200616933000ns, {{590.00, 844.00}} },
{ 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -8715.686523);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -7639.026367);
}
@@ -499,10 +518,14 @@
{ 920989261000ns, {{715.00, 903.00}} },
{ 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 5670.329102);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 5991.866699);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -13021.101562);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -15093.995117);
}
@@ -522,8 +545,10 @@
{ 235247220736000ns, {{620.00, 641.00}} },
{ 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -20286.958984);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -20494.587891);
}
@@ -541,8 +566,10 @@
{ 235302613019881ns, {{679.26, 526.73}} },
{ 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -39295.941406);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -36461.421875);
}
@@ -569,10 +596,14 @@
{ 235655842893000ns, {{563.00, 649.00}} },
{ 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -419.749695);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -398.303894);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3309.016357);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 3969.099854);
}
@@ -599,10 +630,14 @@
{ 235671246532000ns, {{470.00, 799.00}} },
{ 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -262.80426);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -243.665344);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4215.682129);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4587.986816);
}
@@ -622,10 +657,14 @@
{ 171051052000ns, {{536.00, 586.00}} },
{ 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -723.413513);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -651.038452);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 2091.502441);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 1934.517456);
}
@@ -652,8 +691,10 @@
{ 235695373403000ns, {{564.00, 744.00}} },
{ 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4254.639648);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4698.415039);
}
@@ -677,10 +718,14 @@
{ 235709710626776ns, {{511.72, 741.85}} },
{ 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -430.440247);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -447.600311);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3953.859375);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4316.155273);
}
@@ -706,8 +751,10 @@
{ 235727721580000ns, {{516.00, 658.00}} },
{ 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4484.617676);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4927.92627);
}
@@ -725,8 +772,10 @@
{ 235762396429369ns, {{404.37, 680.67}} },
{ 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 14227.0224);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16064.685547);
}
@@ -744,8 +793,10 @@
{ 235772537635000ns, {{484.00, 589.00}} },
{ 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 18660.048828);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16918.439453);
}
@@ -764,10 +815,14 @@
{ 507703352649ns, {{443.71, 857.77}} },
{ 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -4111.8173);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -6388.48877);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 29765.908203);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 28354.796875);
}
/**
@@ -789,10 +844,10 @@
// Velocity should actually be zero, but we expect 0.016 here instead.
// This is close enough to zero, and is likely caused by division by a very small number.
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
}
/**
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 4e8e840..21cfe8c 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -39,13 +39,14 @@
pointerCoords[i].clear();
}
+ ui::Transform transform;
+ transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
- MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
- 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
- 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
- pointerProperties, pointerCoords);
+ MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
+ 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 693bace..22d4712 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -16,7 +16,20 @@
name: "libmath",
host_supported: true,
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ "com.android.neuralnetworks",
+ ],
+ min_sdk_version: "29",
+
export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ }
+ }
}
subdirs = ["tests"]
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 7682973..617a0ab 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -82,6 +82,7 @@
};
public:
+ CONSTEXPR half() noexcept { }
CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
CONSTEXPR operator float() const noexcept { return htof(mBits); }
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 496a7ef..604072e 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -35,6 +35,7 @@
EXPECT_EQ(2UL, sizeof(half));
// test +/- zero
+ EXPECT_EQ(0x0000, half().getBits());
EXPECT_EQ(0x0000, half( 0.0f).getBits());
EXPECT_EQ(0x8000, half(-0.0f).getBits());
diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp
index 7375a2b..8399e8c 100644
--- a/libs/nativebase/Android.bp
+++ b/libs/nativebase/Android.bp
@@ -16,6 +16,8 @@
name: "libnativebase_headers",
vendor_available: true,
host_supported: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -25,5 +27,6 @@
windows: {
enabled: true,
},
- }
+ },
+ min_sdk_version: "29",
}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0ff33ac..e458b2e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -17,24 +17,63 @@
#define LOG_TAG "Choreographer"
//#define LOG_NDEBUG 0
-#include <apex/choreographer.h>
+#include <android-base/thread_annotations.h>
#include <gui/DisplayEventDispatcher.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <private/android/choreographer.h>
#include <utils/Looper.h>
-#include <utils/Mutex.h>
#include <utils/Timers.h>
#include <cinttypes>
+#include <mutex>
#include <optional>
#include <queue>
#include <thread>
-namespace android {
+namespace {
+struct {
+ // Global JVM that is provided by zygote
+ JavaVM* jvm = nullptr;
+ struct {
+ jclass clazz;
+ jmethodID getInstance;
+ jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+ jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+ } displayManagerGlobal;
+} gJni;
-static inline const char* toString(bool value) {
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+ if (gJni.jvm == nullptr) {
+ ALOGW("AChoreographer: No JVM provided!");
+ return nullptr;
+ }
+
+ JNIEnv* env = nullptr;
+ if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGD("Attaching thread to JVM for AChoreographer");
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+ jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+ if (attachResult != JNI_OK) {
+ ALOGE("Unable to attach thread. Error: %d", attachResult);
+ return nullptr;
+ }
+ }
+ if (env == nullptr) {
+ ALOGW("AChoreographer: No JNI env available!");
+ }
+ return env;
+}
+
+inline const char* toString(bool value) {
return value ? "true" : "false";
}
+} // namespace
+
+namespace android {
struct FrameCallback {
AChoreographer_frameCallback callback;
@@ -52,24 +91,43 @@
struct RefreshRateCallback {
AChoreographer_refreshRateCallback callback;
void* data;
+ bool firstCallbackFired = false;
};
+class Choreographer;
+
+struct {
+ std::mutex lock;
+ std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+ bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+ std::atomic<nsecs_t> mLastKnownVsync = -1;
+} gChoreographers;
+
class Choreographer : public DisplayEventDispatcher, public MessageHandler {
public:
- explicit Choreographer(const sp<Looper>& looper);
+ explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
- void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+ void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+ EXCLUDES(gChoreographers.lock);
void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+ // Drains the queue of pending vsync periods and dispatches refresh rate
+ // updates to callbacks.
+ // The assumption is that this method is only called on a single
+ // processing thread, either by looper or by AChoreographer_handleEvents
+ void handleRefreshRateUpdates();
+ void scheduleLatestConfigRequest();
enum {
MSG_SCHEDULE_CALLBACKS = 0,
- MSG_SCHEDULE_VSYNC = 1
+ MSG_SCHEDULE_VSYNC = 1,
+ MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
};
virtual void handleMessage(const Message& message) override;
static Choreographer* getForThread();
- virtual ~Choreographer() = default;
+ virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
private:
Choreographer(const Choreographer&) = delete;
@@ -81,21 +139,17 @@
void scheduleCallbacks();
+ std::mutex mLock;
// Protected by mLock
std::priority_queue<FrameCallback> mFrameCallbacks;
-
- // Protected by mLock
std::vector<RefreshRateCallback> mRefreshRateCallbacks;
- nsecs_t mVsyncPeriod = 0;
- mutable Mutex mLock;
+ nsecs_t mLatestVsyncPeriod = -1;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
- const std::optional<PhysicalDisplayId> mInternalDisplayId;
};
-
static thread_local Choreographer* gChoreographer;
Choreographer* Choreographer::getForThread() {
if (gChoreographer == nullptr) {
@@ -115,17 +169,47 @@
}
Choreographer::Choreographer(const sp<Looper>& looper)
- : DisplayEventDispatcher(looper),
+ : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged::eConfigChangedDispatch),
mLooper(looper),
- mThreadId(std::this_thread::get_id()),
- mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {}
+ mThreadId(std::this_thread::get_id()) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+ gChoreographers.ptrs.end(),
+ [=](Choreographer* c) { return c == this; }),
+ gChoreographers.ptrs.end());
+ // Only poke DisplayManagerGlobal to unregister if we previously registered
+ // callbacks.
+ if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .unregisterNativeChoreographerForRefreshRateCallbacks);
+ env->DeleteLocalRef(dmg);
+ }
+ }
+}
void Choreographer::postFrameCallbackDelayed(
AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
FrameCallback callback{cb, cb64, data, now + delay};
{
- AutoMutex _l{mLock};
+ std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
}
if (callback.dueTime <= now) {
@@ -150,37 +234,68 @@
}
void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (const auto& callback : mRefreshRateCallbacks) {
+ // Don't re-add callbacks.
+ if (cb == callback.callback && data == callback.data) {
+ return;
+ }
+ }
+ mRefreshRateCallbacks.emplace_back(
+ RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+ bool needsRegistration = false;
{
- AutoMutex _l{mLock};
- for (const auto& callback : mRefreshRateCallbacks) {
- // Don't re-add callbacks.
- if (cb == callback.callback && data == callback.data) {
- return;
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ needsRegistration = !gChoreographers.registeredToDisplayManager;
+ }
+ if (needsRegistration) {
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping registration");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet: skipping registration");
+ return;
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .registerNativeChoreographerForRefreshRateCallbacks,
+ reinterpret_cast<int64_t>(this));
+ env->DeleteLocalRef(dmg);
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ gChoreographers.registeredToDisplayManager = true;
}
}
- mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
- toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
+ } else {
+ scheduleLatestConfigRequest();
}
}
void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
void* data) {
- {
- AutoMutex _l{mLock};
- mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
- mRefreshRateCallbacks.end(),
- [&](const RefreshRateCallback& callback) {
- return cb == callback.callback &&
- data == callback.data;
- }),
- mRefreshRateCallbacks.end());
- if (mRefreshRateCallbacks.empty()) {
- toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
- // If callbacks are empty then clear out the most recently seen
- // vsync period so that when another callback is registered then the
- // up-to-date refresh rate can be communicated to the app again.
- mVsyncPeriod = 0;
- }
+ std::lock_guard<std::mutex> _l{mLock};
+ mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+ mRefreshRateCallbacks.end(),
+ [&](const RefreshRateCallback& callback) {
+ return cb == callback.callback &&
+ data == callback.data;
+ }),
+ mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+ if (mLooper != nullptr) {
+ Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+ mLooper->sendMessage(this, m);
+ } else {
+ // If the looper thread is detached from Choreographer, then refresh rate
+ // changes will be handled in AChoreographer_handlePendingEvents, so we
+ // need to redispatch a config from SF
+ requestLatestConfig();
}
}
@@ -188,7 +303,7 @@
const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
nsecs_t dueTime;
{
- AutoMutex _{mLock};
+ std::lock_guard<std::mutex> _l{mLock};
// If there are no pending callbacks then don't schedule a vsync
if (mFrameCallbacks.empty()) {
return;
@@ -203,13 +318,35 @@
}
}
+void Choreographer::handleRefreshRateUpdates() {
+ std::vector<RefreshRateCallback> callbacks{};
+ const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+ const nsecs_t lastPeriod = mLatestVsyncPeriod;
+ if (pendingPeriod > 0) {
+ mLatestVsyncPeriod = pendingPeriod;
+ }
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (auto& cb : mRefreshRateCallbacks) {
+ callbacks.push_back(cb);
+ cb.firstCallbackFired = true;
+ }
+ }
+
+ for (auto& cb : callbacks) {
+ if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+ cb.callback(pendingPeriod, cb.data);
+ }
+ }
+}
+
// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
// the internal display implicitly.
void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
std::vector<FrameCallback> callbacks{};
{
- AutoMutex _l{mLock};
+ std::lock_guard<std::mutex> _l{mLock};
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
callbacks.push_back(mFrameCallbacks.top());
@@ -236,20 +373,29 @@
// display, so as such Choreographer does not support the notion of multiple
// displays. When multi-display choreographer is properly supported, then
// PhysicalDisplayId should no longer be ignored.
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t,
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
nsecs_t vsyncPeriod) {
+ ALOGV("choreographer %p ~ received config change event "
+ "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
+ this, displayId, configId);
+
+ const nsecs_t lastPeriod = mLatestVsyncPeriod;
+ std::vector<RefreshRateCallback> callbacks{};
{
- AutoMutex _l{mLock};
- for (const auto& cb : mRefreshRateCallbacks) {
- // Only perform the callback when the old refresh rate is different
- // from the new refresh rate, so that we don't dispatch the callback
- // on every single configuration change.
- if (mVsyncPeriod != vsyncPeriod) {
- cb.callback(vsyncPeriod, cb.data);
- }
+ std::lock_guard<std::mutex> _l{mLock};
+ for (auto& cb : mRefreshRateCallbacks) {
+ callbacks.push_back(cb);
+ cb.firstCallbackFired = true;
}
- mVsyncPeriod = vsyncPeriod;
}
+
+ for (auto& cb : callbacks) {
+ if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) {
+ cb.callback(vsyncPeriod, cb.data);
+ }
+ }
+
+ mLatestVsyncPeriod = vsyncPeriod;
}
void Choreographer::handleMessage(const Message& message) {
@@ -260,19 +406,80 @@
case MSG_SCHEDULE_VSYNC:
scheduleVsync();
break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
}
}
-}
-
-/* Glue for the NDK interface */
-
+} // namespace android
using namespace android;
static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
return reinterpret_cast<Choreographer*>(choreographer);
}
+// Glue for private C api
+namespace android {
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+ for (auto c : gChoreographers.ptrs) {
+ c->scheduleLatestConfigRequest();
+ }
+}
+
+void AChoreographer_initJVM(JNIEnv* env) {
+ env->GetJavaVM(&gJni.jvm);
+ // Now we need to find the java classes.
+ jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+ gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+ gJni.displayManagerGlobal.getInstance =
+ env->GetStaticMethodID(dmgClass, "getInstance",
+ "()Landroid/hardware/display/DisplayManagerGlobal;");
+ gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+ gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+ "()V");
+}
+
+AChoreographer* AChoreographer_routeGetInstance() {
+ return AChoreographer_getInstance();
+}
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data) {
+ return AChoreographer_postFrameCallback(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis) {
+ return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data) {
+ return AChoreographer_postFrameCallback64(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback,
+ void* data, uint32_t delayMillis) {
+ return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ return AChoreographer_registerRefreshRateCallback(choreographer, callback, data);
+}
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
+}
+
+} // namespace android
+
+/* Glue for the NDK interface */
+
static inline const Choreographer* AChoreographer_to_Choreographer(
const AChoreographer* choreographer) {
return reinterpret_cast<const Choreographer*>(choreographer);
@@ -343,5 +550,6 @@
// Pass dummy fd and events args to handleEvent, since the underlying
// DisplayEventDispatcher doesn't need them outside of validating that a
// Looper instance didn't break, but these args circumvent those checks.
- AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data);
+ Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
+ impl->handleEvent(-1, Looper::EVENT_INPUT, data);
}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index c956578..f56b3a2 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -12,23 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-ndk_headers {
- name: "libnativedisplay_ndk_headers",
- from: "include/android",
- to: "android",
- srcs: ["include/android/*.h"],
- license: "NOTICE",
-}
-
cc_library_headers {
name: "libnativedisplay_headers",
- export_include_dirs: ["include"],
+ export_include_dirs: ["include",],
}
-cc_library {
+cc_library_shared {
name: "libnativedisplay",
export_include_dirs: [
"include",
+ "include-private",
],
clang: true,
@@ -63,6 +56,10 @@
"libnativehelper",
],
+ export_shared_lib_headers: [
+ "libnativehelper",
+ ],
+
header_libs: [
"libnativedisplay_headers",
],
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
new file mode 100644
index 0000000..2164930
--- /dev/null
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <apex/choreographer.h>
+#include <inttypes.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+// Registers the global JVM for AChoreographer
+void AChoreographer_initJVM(JNIEnv* env);
+
+// Signals all AChoregorapher* instances that a new vsync period is available
+// for consumption by callbacks.
+void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
+
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+AChoreographer* AChoreographer_routeGetInstance();
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis);
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback,
+ void* data, uint32_t delayMillis);
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data);
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback callback,
+ void* data);
+
+} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 6a94a77..f371667 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,7 +19,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-
+#include <nativehelper/JNIHelp.h>
#include <system/graphics.h>
// This file provides a facade API on top of SurfaceTexture, which avoids using
@@ -30,6 +30,20 @@
namespace android {
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st);
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName);
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st);
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st);
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st);
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]);
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st);
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture);
+
/**
* ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
* current texture.
@@ -65,6 +79,8 @@
/**
* ASurfaceTexture_dequeueBuffer returns the next available AHardwareBuffer.
+ * The caller gets ownership of the buffer and need to release it with
+ * AHardwareBuffer_release.
*/
AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
android_dataspace* outDataspace,
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 483fb25..fc59431 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -20,6 +20,15 @@
LIBNATIVEDISPLAY_PLATFORM {
global:
extern "C++" {
+ android::AChoreographer_initJVM*;
+ android::AChoreographer_routeGetInstance*;
+ android::AChoreographer_routePostFrameCallback*;
+ android::AChoreographer_routePostFrameCallbackDelayed*;
+ android::AChoreographer_routePostFrameCallback64*;
+ android::AChoreographer_routePostFrameCallbackDelayed64*;
+ android::AChoreographer_routeRegisterRefreshRateCallback*;
+ android::AChoreographer_routeUnregisterRefreshRateCallback*;
+ android::AChoreographer_signalRefreshRateCallbacks*;
android::ADisplay_acquirePhysicalDisplays*;
android::ADisplay_release*;
android::ADisplay_getMaxSupportedFps*;
@@ -36,6 +45,14 @@
android::ASurfaceTexture_takeConsumerOwnership*;
android::ASurfaceTexture_releaseConsumerOwnership*;
android::ASurfaceTexture_dequeueBuffer*;
+ android::ASurfaceTexture_routeAcquireANativeWindow*;
+ android::ASurfaceTexture_routeAttachToGLContext*;
+ android::ASurfaceTexture_routeDetachFromGLContext*;
+ android::ASurfaceTexture_routeGetTimestamp*;
+ android::ASurfaceTexture_routeGetTransformMatrix*;
+ android::ASurfaceTexture_routeUpdateTexImage*;
+ android::ASurfaceTexture_routeFromSurfaceTexture*;
+ android::ASurfaceTexture_routeRelease*;
android::SurfaceTexture*;
};
ASurfaceTexture_acquireANativeWindow;
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index 1670fbb..ebe4484 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -149,6 +149,37 @@
// The following functions are private/unstable API.
namespace android {
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st) {
+ return ASurfaceTexture_acquireANativeWindow(st);
+}
+
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName) {
+ return ASurfaceTexture_attachToGLContext(st, texName);
+}
+
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st) {
+ return ASurfaceTexture_release(st);
+}
+
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st) {
+ return ASurfaceTexture_detachFromGLContext(st);
+}
+
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st) {
+ return ASurfaceTexture_updateTexImage(st);
+}
+
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ return ASurfaceTexture_getTransformMatrix(st, mtx);
+}
+
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st) {
+ return ASurfaceTexture_getTimestamp(st);
+}
+
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ return ASurfaceTexture_fromSurfaceTexture(env, surfacetexture);
+}
unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
return st->consumer->getCurrentTextureTarget();
@@ -177,7 +208,15 @@
*outNewContent = true;
}
} while (buffer.get() && (!queueEmpty));
- return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+ AHardwareBuffer* result = nullptr;
+ if (buffer.get()) {
+ result = buffer->toAHardwareBuffer();
+ // add a reference to keep the hardware buffer alive, even if
+ // BufferQueueProducer is disconnected. This is needed, because
+ // sp reference is destroyed at the end of this function.
+ AHardwareBuffer_acquire(result);
+ }
+ return result;
}
} // namespace android
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 5baec2f..52d73e0 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -25,6 +25,9 @@
name: "libnativewindow_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+ min_sdk_version: "29",
}
ndk_library {
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 25130e2..36aad2e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -236,7 +236,11 @@
/** Compatibility value for ANativeWindow_setFrameRate. */
enum ANativeWindow_FrameRateCompatibility {
/**
- * There are no inherent restrictions on the frame rate of this window.
+ * There are no inherent restrictions on the frame rate of this window. When
+ * the system selects a frame rate other than what the app requested, the
+ * app will be able to run at the system frame rate without requiring pull
+ * down. This value should be used when displaying game content, UIs, and
+ * anything that isn't video.
*/
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
/**
@@ -246,7 +250,7 @@
* to do pull down or use some other technique to adapt to the system's
* frame rate. The user experience is likely to be worse (e.g. more frame
* stuttering) than it would be if the system had chosen the app's requested
- * frame rate.
+ * frame rate. This value should be used for video content.
*/
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
};
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 869ca9e..b78fc5d 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -254,6 +254,7 @@
NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR = 44, /* private */
NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */
NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */
+ NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
// clang-format on
};
@@ -1062,4 +1063,38 @@
return value;
}
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_query is called.
+ */
+typedef int (*ANativeWindow_queryFn)(const ANativeWindow* window, int what, int* value);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queryFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_queryFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queryInterceptor)(const ANativeWindow* window,
+ ANativeWindow_queryFn perform, void* data,
+ int what, int* value);
+
+/**
+ * Registers an interceptor for ANativeWindow_query. Instead of calling
+ * the underlying query function, instead the provided interceptor is
+ * called, which may optionally call the underlying query function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+static inline int ANativeWindow_setQueryInterceptor(ANativeWindow* window,
+ ANativeWindow_queryInterceptor interceptor,
+ void* data) {
+ return window->perform(window, NATIVE_WINDOW_SET_QUERY_INTERCEPTOR, interceptor, data);
+}
+
__END_DECLS
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb6080f..3dcb498 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -64,6 +64,13 @@
],
}
+filegroup {
+ name: "librenderengine_threaded_sources",
+ srcs: [
+ "threaded/RenderEngineThreaded.cpp",
+ ],
+}
+
cc_library_static {
name: "librenderengine",
defaults: ["librenderengine_defaults"],
@@ -80,6 +87,7 @@
srcs: [
":librenderengine_sources",
":librenderengine_gl_sources",
+ ":librenderengine_threaded_sources",
],
lto: {
thin: true,
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0fdf093..c3fbb60 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -20,19 +20,34 @@
#include <log/log.h>
#include <private/gui/SyncFeatures.h>
#include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
namespace android {
namespace renderengine {
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+ RenderEngineType renderEngineType = args.renderEngineType;
+
+ // Keep the ability to override by PROPERTIES:
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
if (strcmp(prop, "gles") == 0) {
- ALOGD("RenderEngine GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(args);
+ renderEngineType = RenderEngineType::GLES;
}
- ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
- return renderengine::gl::GLESRenderEngine::create(args);
+ if (strcmp(prop, "threaded") == 0) {
+ renderEngineType = RenderEngineType::THREADED;
+ }
+
+ switch (renderEngineType) {
+ case RenderEngineType::THREADED:
+ ALOGD("Threaded RenderEngine with GLES Backend");
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+ case RenderEngineType::GLES:
+ default:
+ ALOGD("RenderEngine with GLES Backend");
+ return renderengine::gl::GLESRenderEngine::create(args);
+ }
}
RenderEngine::~RenderEngine() = default;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 92e7e71..d102696 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -982,7 +982,7 @@
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* const buffer,
+ const sp<GraphicBuffer>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
ATRACE_CALL();
@@ -1019,7 +1019,9 @@
const auto blurLayersSize = blurLayers.size();
if (blurLayersSize == 0) {
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()->getNativeBuffer(),
+ useFramebufferCache);
if (fbo->getStatus() != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1076,7 +1078,9 @@
if (blurLayers.size() == 0) {
// Done blurring, time to bind the native FBO and render our blur onto it.
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()
+ ->getNativeBuffer(),
useFramebufferCache);
status = fbo->getStatus();
setViewportAndProjection(display.physicalDisplay, display.clip);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 42b8537..9ab5ee6 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -73,7 +73,7 @@
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
bool cleanupPostRender() override;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e06e128..b137023 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -50,6 +50,10 @@
class Texture;
struct RenderEngineCreationArgs;
+namespace threaded {
+class RenderEngineThreaded;
+}
+
namespace impl {
class RenderEngine;
}
@@ -67,7 +71,12 @@
HIGH = 3,
};
- static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
+ enum class RenderEngineType {
+ GLES = 1,
+ THREADED = 2,
+ };
+
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
@@ -163,7 +172,7 @@
// now, this always returns NO_ERROR.
virtual status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
protected:
@@ -174,6 +183,7 @@
// live longer than RenderEngine.
virtual Framebuffer* getFramebufferForDrawing() = 0;
friend class BindNativeBufferAsFramebuffer;
+ friend class threaded::RenderEngineThreaded;
};
struct RenderEngineCreationArgs {
@@ -184,26 +194,25 @@
bool precacheToneMapperShaderOnly;
bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
+ RenderEngine::RenderEngineType renderEngineType;
struct Builder;
private:
// must be created by Builder via constructor with full argument list
- RenderEngineCreationArgs(
- int _pixelFormat,
- uint32_t _imageCacheSize,
- bool _useColorManagement,
- bool _enableProtectedContext,
- bool _precacheToneMapperShaderOnly,
- bool _supportsBackgroundBlur,
- RenderEngine::ContextPriority _contextPriority)
- : pixelFormat(_pixelFormat)
- , imageCacheSize(_imageCacheSize)
- , useColorManagement(_useColorManagement)
- , enableProtectedContext(_enableProtectedContext)
- , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
- , supportsBackgroundBlur(_supportsBackgroundBlur)
- , contextPriority(_contextPriority) {}
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
+ RenderEngine::ContextPriority _contextPriority,
+ RenderEngine::RenderEngineType _renderEngineType)
+ : pixelFormat(_pixelFormat),
+ imageCacheSize(_imageCacheSize),
+ useColorManagement(_useColorManagement),
+ enableProtectedContext(_enableProtectedContext),
+ precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+ supportsBackgroundBlur(_supportsBackgroundBlur),
+ contextPriority(_contextPriority),
+ renderEngineType(_renderEngineType) {}
RenderEngineCreationArgs() = delete;
};
@@ -238,10 +247,14 @@
this->contextPriority = contextPriority;
return *this;
}
+ Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+ this->renderEngineType = renderEngineType;
+ return *this;
+ }
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
enableProtectedContext, precacheToneMapperShaderOnly,
- supportsBackgroundBlur, contextPriority);
+ supportsBackgroundBlur, contextPriority, renderEngineType);
}
private:
@@ -253,6 +266,7 @@
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+ RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
};
class BindNativeBufferAsFramebuffer {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index df0f17a..d0343ba 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -59,7 +59,8 @@
MOCK_METHOD0(cleanupPostRender, bool());
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*));
};
} // namespace mock
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index e98babc..bcf389b 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -18,10 +18,12 @@
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
+ "RenderEngineThreadedTest.cpp",
],
static_libs: [
"libgmock",
"librenderengine",
+ "librenderengine_mocks",
],
shared_libs: [
"libbase",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 16a8a0d..77b6c0f 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,12 +22,13 @@
#include <condition_variable>
#include <fstream>
-#include <gtest/gtest.h>
#include <cutils/properties.h>
+#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
#include "../gl/GLESRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -40,14 +41,15 @@
static void SetUpTestSuite() {
sRE = renderengine::gl::GLESRenderEngine::create(
renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setUseColorManagerment(false)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build());
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+ .build());
}
static void TearDownTestSuite() {
@@ -252,8 +254,8 @@
std::vector<const renderengine::LayerSettings*> layers,
sp<GraphicBuffer> buffer) {
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
- base::unique_fd(), &fence);
+ status_t status =
+ sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
sCurrentBuffer = buffer;
int fd = fence.release();
@@ -1004,8 +1006,7 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
- base::unique_fd(), nullptr);
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
@@ -1023,8 +1024,7 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
- base::unique_fd(), nullptr);
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
@@ -1414,11 +1414,9 @@
layers.push_back(&layer);
base::unique_fd fenceOne;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(),
- &fenceOne);
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
base::unique_fd fenceTwo;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne),
- &fenceTwo);
+ sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
const int fd = fenceTwo.get();
if (fd >= 0) {
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
new file mode 100644
index 0000000..69a0e19
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2020 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 <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+ ~RenderEngineThreadedTest() {}
+
+ void SetUp() override {
+ mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+ }
+
+ std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+TEST_F(RenderEngineThreadedTest, dump) {
+ std::string testString = "XYZ";
+ EXPECT_CALL(*mRenderEngine, dump(_));
+ mThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+ EXPECT_CALL(*mRenderEngine, primeCache());
+ mThreadedRE->primeCache();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+ mThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+ mThreadedRE->deleteTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) {
+ EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr)))
+ .WillOnce(Return(BAD_VALUE));
+ status_t result = mThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
+ .WillOnce(Return(NO_ERROR));
+ status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
+ mThreadedRE->cacheExternalTextureBuffer(nullptr);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
+ mThreadedRE->cacheExternalTextureBuffer(buf);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
+ EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
+ mThreadedRE->unbindExternalTextureBuffer(0x0);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE));
+ status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR));
+ status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get()));
+ mThreadedRE->unbindFrameBuffer(framebuffer.get());
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
+ size_t size = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+ size_t size = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+ size_t dims = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+ size_t dims = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+ status_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+ size_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false));
+ status_t result = mThreadedRE->cleanupPostRender();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true));
+ status_t result = mThreadedRE->cleanupPostRender();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+
+ EXPECT_CALL(*mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t { return NO_ERROR; });
+
+ status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
+ std::move(bufferFence), &drawFence);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..ad61718
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2020 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory));
+}
+
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+ ATRACE_CALL();
+
+ std::lock_guard lockThread(mThreadMutex);
+ mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+ {
+ std::lock_guard lock(mThreadMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO");
+ }
+
+ mRenderEngine = factory();
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ pthread_setname_np(pthread_self(), mThreadName);
+
+ while (mRunning) {
+ if (!mFunctionCalls.empty()) {
+ auto task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ task(*mRenderEngine);
+ }
+ mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+ return !mRunning || !mFunctionCalls.empty();
+ });
+ }
+}
+
+void RenderEngineThreaded::primeCache() const {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::primeCache");
+ instance.primeCache();
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+ std::promise<std::string> resultPromise;
+ std::future<std::string> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::dump");
+ std::string localResult = result;
+ instance.dump(localResult);
+ resultPromise.set_value(std::move(localResult));
+ });
+ }
+ mCondition.notify_one();
+ // Note: This is an rvalue.
+ result.assign(resultFuture.get());
+}
+
+bool RenderEngineThreaded::useNativeFenceSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useNativeFenceSync");
+ bool returnValue = SyncFeatures::getInstance().useNativeFenceSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useWaitSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useWaitSync");
+ bool returnValue = SyncFeatures::getInstance().useWaitSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::genTextures");
+ instance.genTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::deleteTextures");
+ instance.deleteTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, texName, &image](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindExternalTextureImage");
+ instance.bindExternalTextureImage(texName, image);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindExternalTextureBuffer");
+ status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+ instance.cacheExternalTextureBuffer(buffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+ instance.unbindExternalTextureBuffer(bufferId);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindFrameBuffer");
+ status_t status = instance.bindFrameBuffer(framebuffer);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindFrameBuffer");
+ instance.unbindFrameBuffer(framebuffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+size_t RenderEngineThreaded::getMaxTextureSize() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxTextureSize");
+ size_t size = instance.getMaxTextureSize();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxViewportDims");
+ size_t size = instance.getMaxViewportDims();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::isProtected() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::isProtected");
+ bool returnValue = instance.isProtected();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::supportsProtectedContent");
+ bool returnValue = instance.supportsProtectedContent();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::useProtectedContext");
+ bool returnValue = instance.useProtectedContext(useProtectedContext);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() {
+ std::promise<Framebuffer*> resultPromise;
+ std::future<Framebuffer*> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getFramebufferForDrawing");
+ Framebuffer* framebuffer = instance.getFramebufferForDrawing();
+ resultPromise.set_value(framebuffer);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::cleanupPostRender() {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanupPostRender");
+ bool returnValue = instance.cleanupPostRender();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+ &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::drawLayers");
+ status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence), drawFence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..ec18e1f
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>;
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public RenderEngine {
+public:
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+
+ RenderEngineThreaded(CreateInstanceFactory factory);
+ ~RenderEngineThreaded() override;
+ void primeCache() const override;
+
+ void dump(std::string& result) override;
+
+ bool useNativeFenceSync() const override;
+ bool useWaitSync() const override;
+ void genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) override;
+ void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ void unbindExternalTextureBuffer(uint64_t bufferId) override;
+ status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+ void unbindFrameBuffer(Framebuffer* framebuffer) override;
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+ bool isProtected() const override;
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ bool cleanupPostRender() override;
+
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+
+protected:
+ Framebuffer* getFramebufferForDrawing() override;
+
+private:
+ void threadMain(CreateInstanceFactory factory);
+
+ /* ------------------------------------------------------------------------
+ * Threading
+ */
+ const char* const mThreadName = "RenderEngineThread";
+ // Protects the creation and destruction of mThread.
+ mutable std::mutex mThreadMutex;
+ std::thread mThread GUARDED_BY(mThreadMutex);
+ bool mRunning GUARDED_BY(mThreadMutex) = true;
+ mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+ GUARDED_BY(mThreadMutex);
+ mutable std::condition_variable mCondition;
+
+ /* ------------------------------------------------------------------------
+ * Render Engine
+ */
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 8ed09f8..a6b0aaf 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -216,14 +216,25 @@
int32_t type;
Vector<float> floats;
Vector<int32_t> ints;
+ uint32_t count;
handle = data.readInt32();
type = data.readInt32();
- floats.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(float))) {
+ return BAD_VALUE;
+ }
+ floats.resize(count);
for (auto &i : floats) {
i = data.readFloat();
}
- ints.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(int32_t))) {
+ return BAD_VALUE;
+ }
+ ints.resize(count);
for (auto &i : ints) {
i = data.readInt32();
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 458ee67..2acc5bb 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -12,6 +12,74 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_defaults {
+ name: "libui-defaults",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ cppflags: [
+ "-Wextra",
+ ],
+
+ sanitize: {
+ integer_overflow: true,
+ misc_undefined: ["bounds"],
+ },
+
+}
+
+cc_library_static {
+ name: "libui-types",
+ vendor_available: true,
+ host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ }
+ },
+
+ defaults: [
+ "libui-defaults",
+ ],
+
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libarect",
+ "libmath",
+ ],
+
+ srcs: [
+ "ColorSpace.cpp",
+ "Rect.cpp",
+ "Region.cpp",
+ "Transform.cpp",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "include_private",
+ "include_types",
+ ],
+
+ export_static_lib_headers: [
+ "libarect",
+ "libmath",
+ ],
+
+}
+
cc_library_shared {
name: "libui",
vendor_available: true,
@@ -35,8 +103,9 @@
},
srcs: [
- "ColorSpace.cpp",
"DebugUtils.cpp",
+ "DeviceProductInfo.cpp",
+ "DisplayInfo.cpp",
"Fence.cpp",
"FenceTime.cpp",
"FrameStats.cpp",
@@ -50,10 +119,7 @@
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
- "Rect.cpp",
- "Region.cpp",
"Size.cpp",
- "Transform.cpp",
"UiConfig.cpp",
],
@@ -65,8 +131,11 @@
"include_private",
],
- // Uncomment the following line to enable VALIDATE_REGIONS traces
- //defaults: ["libui-validate-regions-defaults"],
+ defaults: [
+ "libui-defaults",
+ // Uncomment the following line to enable VALIDATE_REGIONS traces
+ //defaults: ["libui-validate-regions-defaults"],
+ ],
shared_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -100,6 +169,10 @@
"libmath",
],
+ whole_static_libs: [
+ "libui-types",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
@@ -133,6 +206,7 @@
"libhardware_headers",
"libui_headers",
],
+ min_sdk_version: "29",
}
cc_library_headers {
@@ -151,6 +225,7 @@
export_header_lib_headers: [
"libnativewindow_headers",
],
+ min_sdk_version: "29",
}
// defaults to enable VALIDATE_REGIONS traces
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
new file mode 100644
index 0000000..4d6ce43
--- /dev/null
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 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 <ui/DeviceProductInfo.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/FlattenableHelpers.h>
+#include <utils/Log.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+using base::StringAppendF;
+
+size_t DeviceProductInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(name) +
+ FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
+ FlattenableHelpers::getFlattenedSize(productId) +
+ FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) +
+ FlattenableHelpers::getFlattenedSize(relativeAddress);
+}
+
+status_t DeviceProductInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress));
+ return OK;
+}
+
+status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress));
+ return OK;
+}
+
+void DeviceProductInfo::dump(std::string& result) const {
+ StringAppendF(&result, "{name=%s, ", name.c_str());
+ StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
+ StringAppendF(&result, "productId=%s, ", productId.c_str());
+
+ if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "modelYear=%u, ", model->year);
+ } else if (const auto* manufactureWeekAndYear =
+ std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
+ } else if (const auto* manufactureYear =
+ std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
+ } else {
+ ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ }
+
+ result.append("relativeAddress=[");
+ for (size_t i = 0; i < relativeAddress.size(); i++) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ StringAppendF(&result, "%u", relativeAddress[i]);
+ }
+ result.append("]}");
+}
+
+} // namespace android
diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp
new file mode 100644
index 0000000..73a78af
--- /dev/null
+++ b/libs/ui/DisplayInfo.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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 <ui/DisplayInfo.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+size_t DisplayInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(connectionType) +
+ FlattenableHelpers::getFlattenedSize(density) +
+ FlattenableHelpers::getFlattenedSize(secure) +
+ FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+}
+
+status_t DisplayInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+ return OK;
+}
+
+status_t DisplayInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 97ead21..203a739 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,7 +1,6 @@
+chrisforbes@google.com
lpy@google.com
-marissaw@google.com
mathias@google.com
romainguy@google.com
stoza@google.com
-jwcai@google.com
-tianyuj@google.com
+vhau@google.com
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 82ce757..e01309b 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -224,6 +224,11 @@
validate(*this, "this->operator=");
validate(rhs, "rhs.operator=");
#endif
+ if (this == &rhs) {
+ // Already equal to itself
+ return *this;
+ }
+
mStorage.clear();
mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
return *this;
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 06b6bfe..5424a3c 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -55,7 +55,6 @@
mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
mMatrix[2][2] == other.mMatrix[2][2];
- ;
}
Transform Transform::operator * (const Transform& rhs) const
@@ -87,6 +86,19 @@
return r;
}
+Transform Transform::operator * (float value) const {
+ Transform r(*this);
+ const mat33& M(mMatrix);
+ mat33& R(r.mMatrix);
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j = 0; j < 2; j++) {
+ R[i][j] = M[i][j] * value;
+ }
+ }
+ r.type();
+ return r;
+}
+
Transform& Transform::operator=(const Transform& other) {
mMatrix = other.mMatrix;
mType = other.mType;
@@ -105,14 +117,30 @@
return mMatrix[2][1];
}
-float Transform::sx() const {
+float Transform::dsdx() const {
return mMatrix[0][0];
}
-float Transform::sy() const {
+float Transform::dtdx() const {
+ return mMatrix[1][0];
+}
+
+float Transform::dtdy() const {
+ return mMatrix[0][1];
+}
+
+float Transform::dsdy() const {
return mMatrix[1][1];
}
+float Transform::getScaleX() const {
+ return sqrt(dsdx() * dsdx()) + (dtdx() * dtdx());
+}
+
+float Transform::getScaleY() const {
+ return sqrt((dtdy() * dtdy()) + (dsdy() * dsdy()));
+}
+
void Transform::reset() {
mType = IDENTITY;
for(size_t i = 0; i < 3; i++) {
@@ -187,6 +215,15 @@
return NO_ERROR;
}
+void Transform::set(const std::array<float, 9>& matrix) {
+ mat33& M(mMatrix);
+ M[0][0] = matrix[0]; M[1][0] = matrix[1]; M[2][0] = matrix[2];
+ M[0][1] = matrix[3]; M[1][1] = matrix[4]; M[2][1] = matrix[5];
+ M[0][2] = matrix[6]; M[1][2] = matrix[7]; M[2][2] = matrix[8];
+ mType = UNKNOWN_TYPE;
+ type();
+}
+
vec2 Transform::transform(const vec2& v) const {
vec2 r;
const mat33& M(mMatrix);
@@ -204,9 +241,8 @@
return r;
}
-vec2 Transform::transform(int x, int y) const
-{
- return transform(vec2(x,y));
+vec2 Transform::transform(float x, float y) const {
+ return transform(vec2(x, y));
}
Rect Transform::makeBounds(int w, int h) const
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index af00342..807a5d9 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -19,7 +19,12 @@
#include <array>
#include <cstdint>
#include <optional>
+#include <string>
+#include <type_traits>
#include <variant>
+#include <vector>
+
+#include <utils/Flattenable.h>
namespace android {
@@ -29,13 +34,7 @@
// Product-specific information about the display or the directly connected device on the
// display chain. For example, if the display is transitively connected, this field may contain
// product information about the intermediate device.
-struct DeviceProductInfo {
- static constexpr size_t TEXT_BUFFER_SIZE = 20;
- static constexpr size_t RELATIVE_ADDRESS_SIZE = 4;
-
- using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>;
- static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff};
-
+struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> {
struct ModelYear {
uint32_t year;
};
@@ -48,21 +47,29 @@
};
// Display name.
- std::array<char, TEXT_BUFFER_SIZE> name;
+ std::string name;
// Manufacturer Plug and Play ID.
PnpId manufacturerPnpId;
// Manufacturer product ID.
- std::array<char, TEXT_BUFFER_SIZE> productId;
+ std::string productId;
using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+ static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>);
ManufactureOrModelDate manufactureOrModelDate;
- // Relative address in the display network. Unavailable address is indicated
- // by all elements equal to 255.
+ // Relative address in the display network. Empty vector indicates that the
+ // address is unavailable.
// For example, for HDMI connected device this will be the physical address.
- RelativeAddress relativeAddress;
+ std::vector<uint8_t> relativeAddress;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ void dump(std::string& result) const;
};
} // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 897060c..03e0a38 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -20,19 +20,23 @@
#include <type_traits>
#include <ui/DeviceProductInfo.h>
+#include <utils/Flattenable.h>
namespace android {
enum class DisplayConnectionType { Internal, External };
// Immutable information about physical display.
-struct DisplayInfo {
+struct DisplayInfo : LightFlattenable<DisplayInfo> {
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
-};
-static_assert(std::is_trivially_copyable_v<DisplayInfo>);
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+};
} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index c6bb598..2612e82 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -18,10 +18,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <array>
#include <ostream>
#include <string>
-#include <hardware/hardware.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
@@ -44,9 +44,9 @@
enum RotationFlags : uint32_t {
ROT_0 = 0,
- FLIP_H = HAL_TRANSFORM_FLIP_H,
- FLIP_V = HAL_TRANSFORM_FLIP_V,
- ROT_90 = HAL_TRANSFORM_ROT_90,
+ FLIP_H = 1, // HAL_TRANSFORM_FLIP_H
+ FLIP_V = 2, // HAL_TRANSFORM_FLIP_V
+ ROT_90 = 4, // HAL_TRANSFORM_ROT_90
ROT_180 = FLIP_H | FLIP_V,
ROT_270 = ROT_180 | ROT_90,
ROT_INVALID = 0x80
@@ -69,24 +69,31 @@
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
float ty() const;
- float sx() const;
- float sy() const;
+ float dsdx() const;
+ float dtdx() const;
+ float dtdy() const;
+ float dsdy() const;
+
+ float getScaleX() const;
+ float getScaleY() const;
// modify the transform
void reset();
void set(float tx, float ty);
void set(float a, float b, float c, float d);
status_t set(uint32_t flags, float w, float h);
+ void set(const std::array<float, 9>& matrix);
// transform data
Rect makeBounds(int w, int h) const;
- vec2 transform(int x, int y) const;
+ vec2 transform(float x, float y) const;
Region transform(const Region& reg) const;
Rect transform(const Rect& bounds,
bool roundOutwards = false) const;
FloatRect transform(const FloatRect& bounds) const;
Transform& operator = (const Transform& other);
Transform operator * (const Transform& rhs) const;
+ Transform operator * (float value) const;
// assumes the last row is < 0 , 0 , 1 >
vec2 transform(const vec2& v) const;
vec3 transform(const vec3& v) const;
diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
new file mode 100644
index 0000000..8e316d8
--- /dev/null
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <numeric>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+#include <utils/Flattenable.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+struct FlattenableHelpers {
+ // Helpers for reading and writing POD structures
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static constexpr size_t getFlattenedSize(const T&) {
+ return sizeof(T);
+ }
+
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static status_t flatten(void** buffer, size_t* size, const T& value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::write(*buffer, *size, value);
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static status_t unflatten(const void** buffer, size_t* size, T* value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::read(*buffer, *size, *value);
+ return OK;
+ }
+
+ // Helpers for reading and writing std::string
+ static size_t getFlattenedSize(const std::string& str) {
+ return sizeof(uint64_t) + str.length();
+ }
+
+ static status_t flatten(void** buffer, size_t* size, const std::string& str) {
+ if (*size < getFlattenedSize(str)) return NO_MEMORY;
+ flatten(buffer, size, (uint64_t)str.length());
+ memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
+ FlattenableUtils::advance(*buffer, *size, str.length());
+ return OK;
+ }
+
+ static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
+ uint64_t length;
+ RETURN_IF_ERROR(unflatten(buffer, size, &length));
+ if (*size < length) return NO_MEMORY;
+ str->assign(reinterpret_cast<const char*>(*buffer), length);
+ FlattenableUtils::advance(*buffer, *size, length);
+ return OK;
+ }
+
+ // Helpers for reading and writing LightFlattenable
+ template <class T>
+ static size_t getFlattenedSize(const LightFlattenable<T>& value) {
+ return value.getFlattenedSize();
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
+ RETURN_IF_ERROR(value.flatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
+ RETURN_IF_ERROR(value->unflatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
+ return OK;
+ }
+
+ // Helpers for reading and writing std::optional
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static size_t getFlattenedSize(const std::optional<T>& value) {
+ return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
+ if (value) {
+ RETURN_IF_ERROR(flatten(buffer, size, true));
+ RETURN_IF_ERROR(flatten(buffer, size, *value));
+ } else {
+ RETURN_IF_ERROR(flatten(buffer, size, false));
+ }
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
+ bool isPresent;
+ RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
+ if (isPresent) {
+ *value = T();
+ RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
+ } else {
+ value->reset();
+ }
+ return OK;
+ }
+
+ // Helpers for reading and writing std::vector
+ template <class T>
+ static size_t getFlattenedSize(const std::vector<T>& value) {
+ return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
+ [](size_t sum, const T& element) {
+ return sum + getFlattenedSize(element);
+ });
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
+ RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
+ for (const auto& element : value) {
+ RETURN_IF_ERROR(flatten(buffer, size, element));
+ }
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
+ uint64_t numElements;
+ RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
+ // We don't need an extra size check since each iteration of the loop does that
+ std::vector<T> elements;
+ for (size_t i = 0; i < numElements; i++) {
+ T element;
+ RETURN_IF_ERROR(unflatten(buffer, size, &element));
+ elements.push_back(element);
+ }
+ *value = std::move(elements);
+ return OK;
+ }
+};
+
+} // namespace android
+
+#undef RETURN_IF_ERROR
\ No newline at end of file
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h
similarity index 100%
rename from libs/ui/include/ui/ColorSpace.h
rename to libs/ui/include_types/ui/ColorSpace.h
diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h
index ddf70d5..7d2a6d3 120000
--- a/libs/ui/include_vndk/ui/ColorSpace.h
+++ b/libs/ui/include_vndk/ui/ColorSpace.h
@@ -1 +1 @@
-../../include/ui/ColorSpace.h
\ No newline at end of file
+../../include_types/ui/ColorSpace.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index b53342c..28ef77a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
}
cc_test {
+ name: "FlattenableHelpers_test",
+ shared_libs: ["libui"],
+ srcs: ["FlattenableHelpers_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "GraphicBufferAllocator_test",
header_libs: [
"libnativewindow_headers",
diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp
new file mode 100644
index 0000000..db32bc7
--- /dev/null
+++ b/libs/ui/tests/FlattenableHelpers_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2020 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 "FlattenableHelpersTest"
+
+#include <ui/FlattenableHelpers.h>
+
+#include <gtest/gtest.h>
+#include <utils/Flattenable.h>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android {
+
+namespace {
+
+struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> {
+ std::unique_ptr<int32_t> ptr;
+
+ bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const { return sizeof(int32_t); }
+
+ status_t flatten(void* buffer, size_t size) const {
+ FlattenableUtils::write(buffer, size, *ptr);
+ return OK;
+ }
+
+ status_t unflatten(void const* buffer, size_t size) {
+ int value;
+ FlattenableUtils::read(buffer, size, value);
+ ptr = std::make_unique<int32_t>(value);
+ return OK;
+ }
+};
+
+class FlattenableHelpersTest : public testing::Test {
+public:
+ template <class T>
+ void testWriteThenRead(const T& value, size_t bufferSize) {
+ std::vector<int8_t> buffer(bufferSize);
+ auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+ auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size = buffer.size();
+ T valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ EXPECT_EQ(value, valueRead);
+ }
+
+ template <class T>
+ void testTriviallyCopyable(const T& value) {
+ testWriteThenRead(value, sizeof(T));
+ }
+
+ template <class T>
+ void testWriteThenRead(const T& value) {
+ testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value));
+ }
+};
+
+TEST_F(FlattenableHelpersTest, TriviallyCopyable) {
+ testTriviallyCopyable(42);
+ testTriviallyCopyable(1LL << 63);
+ testTriviallyCopyable(false);
+ testTriviallyCopyable(true);
+ testTriviallyCopyable(std::optional<int>());
+ testTriviallyCopyable(std::optional<int>(4));
+}
+
+TEST_F(FlattenableHelpersTest, String) {
+ testWriteThenRead(std::string("Android"));
+ testWriteThenRead(std::string());
+}
+
+TEST_F(FlattenableHelpersTest, Vector) {
+ testWriteThenRead(std::vector<int>({1, 2, 3}));
+ testWriteThenRead(std::vector<int>());
+}
+
+TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ constexpr int kInternalValue = 16;
+ {
+ std::optional<TestLightFlattenable> value =
+ TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)};
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_TRUE(valueRead.has_value());
+ EXPECT_EQ(kInternalValue, *valueRead->ptr);
+}
+
+TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ {
+ std::optional<TestLightFlattenable> value;
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_FALSE(valueRead.has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
index b104a46..c6b826d 100644
--- a/libs/ui/tests/Region_test.cpp
+++ b/libs/ui/tests/Region_test.cpp
@@ -152,5 +152,20 @@
}
}
+TEST_F(RegionTest, EqualsToSelf) {
+ Region touchableRegion;
+ touchableRegion.orSelf(Rect(0, 0, 100, 100));
+
+ ASSERT_TRUE(touchableRegion.contains(50, 50));
+
+ // Compiler prevents us from directly calling 'touchableRegion = touchableRegion'
+ Region& referenceTouchableRegion = touchableRegion;
+ touchableRegion = referenceTouchableRegion;
+
+ ASSERT_FALSE(touchableRegion.isEmpty());
+
+ ASSERT_TRUE(touchableRegion.contains(50, 50));
+}
+
}; // namespace android
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index e95a080..1681fe2 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -14,6 +14,8 @@
cc_library_shared {
name: "libvibrator",
+ vendor_available: true,
+ double_loadable: true,
shared_libs: [
"libbinder",
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
new file mode 100644
index 0000000..749c568
--- /dev/null
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 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 <cstring>
+
+#include <math.h>
+
+#include <vibrator/ExternalVibrationUtils.h>
+
+namespace android::os {
+
+namespace {
+static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
+static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
+static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+
+float getHapticScaleGamma(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::VERY_LOW:
+ return 2.0f;
+ case HapticScale::LOW:
+ return 1.5f;
+ case HapticScale::HIGH:
+ return 0.5f;
+ case HapticScale::VERY_HIGH:
+ return 0.25f;
+ default:
+ return 1.0f;
+ }
+}
+
+float getHapticMaxAmplitudeRatio(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::VERY_LOW:
+ return HAPTIC_SCALE_VERY_LOW_RATIO;
+ case HapticScale::LOW:
+ return HAPTIC_SCALE_LOW_RATIO;
+ case HapticScale::NONE:
+ case HapticScale::HIGH:
+ case HapticScale::VERY_HIGH:
+ return 1.0f;
+ default:
+ return 0.0f;
+ }
+}
+
+} // namespace
+
+bool isValidHapticScale(HapticScale scale) {
+ switch (scale) {
+ case HapticScale::MUTE:
+ case HapticScale::VERY_LOW:
+ case HapticScale::LOW:
+ case HapticScale::NONE:
+ case HapticScale::HIGH:
+ case HapticScale::VERY_HIGH:
+ return true;
+ }
+ return false;
+}
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale) {
+ if (!isValidHapticScale(scale) || scale == HapticScale::NONE) {
+ return;
+ }
+ if (scale == HapticScale::MUTE) {
+ memset(buffer, 0, length * sizeof(float));
+ return;
+ }
+ float gamma = getHapticScaleGamma(scale);
+ float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
+ for (size_t i = 0; i < length; i++) {
+ float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+ buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+ * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+ }
+}
+
+} // namespace android::os
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
new file mode 100644
index 0000000..20045d0
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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_EXTERNAL_VIBRATION_UTILS_H
+#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+
+#include <android/os/IExternalVibratorService.h>
+
+namespace android::os {
+
+enum class HapticScale {
+ MUTE = IExternalVibratorService::SCALE_MUTE,
+ VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW,
+ LOW = IExternalVibratorService::SCALE_LOW,
+ NONE = IExternalVibratorService::SCALE_NONE,
+ HIGH = IExternalVibratorService::SCALE_HIGH,
+ VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH,
+};
+
+bool isValidHapticScale(HapticScale scale);
+
+void scaleHapticData(float* buffer, size_t length, HapticScale scale);
+
+} // namespace android::os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_UTILS_H
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 2fcee7b..37c19d4 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -16,6 +16,12 @@
name: "libbufferhub_headers",
export_include_dirs: ["include"],
vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor.
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
}
sourceFiles = [
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 81a9b2d..340d7bf 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -17,6 +17,12 @@
name: "libdvr_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
}
cc_library_headers {
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 23a4224..24ba830 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -2,6 +2,7 @@
name: "libpdx_headers",
export_include_dirs: ["private"],
vendor_available: true,
+ min_sdk_version: "29",
}
cc_library_static {
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
new file mode 100644
index 0000000..b36e0de
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -0,0 +1,62 @@
+cc_fuzz {
+ name: "libpdx_service_dispatcher_fuzzer",
+ clang: true,
+ srcs: [
+ "service_dispatcher_fuzzer.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libcutils"
+ ],
+}
+
+cc_fuzz {
+ name: "libpdx_message_fuzzer",
+ clang: true,
+ srcs: [
+ "message_fuzzer.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libcutils"
+ ],
+}
+
+cc_fuzz {
+ name: "libpdx_serialization_fuzzer",
+ clang: true,
+ srcs: [
+ "serialization_fuzzer.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libcutils"
+ ],
+}
diff --git a/libs/vr/libpdx/fuzz/helpers.h b/libs/vr/libpdx/fuzz/helpers.h
new file mode 100644
index 0000000..83ec409
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/helpers.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#ifndef LEV_FUZZERS_LIBPDX_HELPERS_H_
+#define LEV_FUZZERS_LIBPDX_HELPERS_H_
+
+#define UNUSED(expr) \
+ do { \
+ (void)(expr); \
+ } while (0)
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <pdx/client.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <pdx/service_endpoint.h>
+#include <sys/eventfd.h>
+#include <memory>
+#include <vector>
+
+using namespace android::pdx;
+
+// Vector of operations we can call in the dispatcher.
+static const std::vector<std::function<void(
+ const std::unique_ptr<ServiceDispatcher>&, FuzzedDataProvider*)>>
+ dispatcher_operations = {
+ [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+ FuzzedDataProvider*) -> void { dispatcher->EnterDispatchLoop(); },
+ [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+ FuzzedDataProvider*) -> void { dispatcher->ReceiveAndDispatch(); },
+ [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+ FuzzedDataProvider* fdp) -> void {
+ dispatcher->ReceiveAndDispatch(fdp->ConsumeIntegral<int>());
+ }};
+
+// Most of the fuzzing occurs within the endpoint, which is derived from an
+// abstract class. So we are returning garbage data for most functions besides
+// the ones we added or need to actually use.
+class FuzzEndpoint : public Endpoint {
+ public:
+ explicit FuzzEndpoint(FuzzedDataProvider* fdp) {
+ _fdp = fdp;
+ _epoll_fd = eventfd(0, 0);
+ }
+
+ ~FuzzEndpoint() { close(_epoll_fd); }
+
+ // Returns an fd that can be used with epoll() to wait for incoming messages
+ // from this endpoint.
+ int epoll_fd() const { return _epoll_fd; }
+
+ // Associates a Service instance with an endpoint by setting the service
+ // context pointer to the address of the Service. Only one Service may be
+ // associated with a given endpoint.
+ Status<void> SetService(Service* service) {
+ _service = service;
+ return Status<void>(0);
+ }
+
+ // Set the channel context for the given channel.
+ Status<void> SetChannel(int channel_id, Channel* channel) {
+ UNUSED(channel_id);
+ _channel = std::shared_ptr<Channel>(channel);
+ return Status<void>(0);
+ }
+
+ // Receives a message on the given endpoint file descriptor.
+ // This is called by the dispatcher to determine what operations
+ // to make, so we are fuzzing the response.
+ Status<void> MessageReceive(Message* message) {
+ // Create a randomized MessageInfo struct.
+ MessageInfo info;
+ eventfd_t wakeup_val = 0;
+ info.pid = _fdp->ConsumeIntegral<int>();
+ info.tid = _fdp->ConsumeIntegral<int>();
+ info.cid = _fdp->ConsumeIntegral<int>();
+ info.mid = _fdp->ConsumeIntegral<int>();
+ info.euid = _fdp->ConsumeIntegral<int>();
+ info.egid = _fdp->ConsumeIntegral<int>();
+ info.op = _fdp->ConsumeIntegral<int32_t>();
+ info.flags = _fdp->ConsumeIntegral<uint32_t>();
+ info.service = _service;
+ info.channel = _channel.get();
+ info.send_len = _fdp->ConsumeIntegral<size_t>();
+ info.recv_len = _fdp->ConsumeIntegral<size_t>();
+ info.fd_count = _fdp->ConsumeIntegral<size_t>();
+ if (_fdp->remaining_bytes() >= 32) {
+ std::vector<uint8_t> impulse_vec = _fdp->ConsumeBytes<uint8_t>(32);
+ memcpy(info.impulse, impulse_vec.data(), 32);
+ }
+
+ *message = Message(info);
+ eventfd_read(_epoll_fd, &wakeup_val);
+
+ return Status<void>();
+ }
+
+ // Returns a tag that uniquely identifies a specific underlying IPC
+ // transport.
+ uint32_t GetIpcTag() const { return 0; }
+
+ // Close a channel, signaling the client file object and freeing the channel
+ // id. Once closed, the client side of the channel always returns the error
+ // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ Status<void> CloseChannel(int channel_id) {
+ UNUSED(channel_id);
+ return Status<void>();
+ }
+
+ // Update the event bits for the given channel (given by id), using the
+ // given clear and set masks.
+ Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) {
+ UNUSED(channel_id);
+ UNUSED(clear_mask);
+ UNUSED(set_mask);
+ return Status<void>();
+ }
+
+ // Create a new channel and push it as a file descriptor to the process
+ // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ // sending process may change these later using fcntl()). The internal
+ // Channel instance associated with this channel is set to |channel|,
+ // which may be nullptr. The new channel id allocated for this channel is
+ // returned in |channel_id|, which may also be nullptr if not needed.
+ Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel, int* channel_id) {
+ UNUSED(message);
+ UNUSED(flags);
+ UNUSED(channel);
+ UNUSED(channel_id);
+ return Status<RemoteChannelHandle>();
+ }
+
+ // Check whether the |ref| is a reference to a channel to the service
+ // represented by the |endpoint|. If the channel reference in question is
+ // valid, the Channel object is returned in |channel| when non-nullptr and
+ // the channel ID is returned through the Status object.
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) {
+ UNUSED(message);
+ UNUSED(ref);
+ UNUSED(channel);
+ return Status<int>();
+ }
+
+ // Replies to the message with a return code.
+ Status<void> MessageReply(Message* message, int return_code) {
+ UNUSED(message);
+ UNUSED(return_code);
+ return Status<void>();
+ }
+
+ // Replies to the message with a file descriptor.
+ Status<void> MessageReplyFd(Message* message, unsigned int push_fd) {
+ UNUSED(message);
+ UNUSED(push_fd);
+ return Status<void>();
+ }
+
+ // Replies to the message with a local channel handle.
+ Status<void> MessageReplyChannelHandle(Message* message,
+ const LocalChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<void>();
+ }
+
+ // Replies to the message with a borrowed local channel handle.
+ Status<void> MessageReplyChannelHandle(Message* message,
+ const BorrowedChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<void>();
+ }
+
+ // Replies to the message with a remote channel handle.
+ Status<void> MessageReplyChannelHandle(Message* message,
+ const RemoteChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<void>();
+ }
+
+ // Reads message data into an array of memory buffers.
+ Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ UNUSED(message);
+ UNUSED(vector);
+ UNUSED(vector_length);
+ return Status<size_t>();
+ }
+
+ // Sends reply data for message.
+ Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ UNUSED(message);
+ UNUSED(vector);
+ UNUSED(vector_length);
+ return Status<size_t>();
+ }
+
+ // Records a file descriptor into the message buffer and returns the
+ // remapped reference to be sent to the remote process.
+ Status<FileReference> PushFileHandle(Message* message,
+ const LocalHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<FileReference>();
+ }
+
+ Status<FileReference> PushFileHandle(Message* message,
+ const BorrowedHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<FileReference>();
+ }
+
+ Status<FileReference> PushFileHandle(Message* message,
+ const RemoteHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<FileReference>();
+ }
+
+ Status<ChannelReference> PushChannelHandle(Message* message,
+ const LocalChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<ChannelReference>();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<ChannelReference>();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<ChannelReference>();
+ }
+
+ // Obtains a file descriptor/channel handle from a message for the given
+ // reference.
+ LocalHandle GetFileHandle(Message* message, FileReference ref) const {
+ UNUSED(message);
+ UNUSED(ref);
+ return LocalHandle();
+ }
+
+ LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const {
+ UNUSED(message);
+ UNUSED(ref);
+ return LocalChannelHandle();
+ }
+
+ // Transport-specific message state management.
+ void* AllocateMessageState() { return nullptr; }
+
+ void FreeMessageState(void* state) { UNUSED(state); }
+
+ // Cancels the endpoint, unblocking any receiver threads waiting for a
+ // message.
+ Status<void> Cancel() { return Status<void>(); }
+
+ private:
+ FuzzedDataProvider* _fdp;
+ std::shared_ptr<Channel> _channel;
+ Service* _service;
+ int _epoll_fd;
+};
+
+#endif // LEV_FUZZERS_LIBPDX_HELPERS_H_
diff --git a/libs/vr/libpdx/fuzz/message_fuzzer.cpp b/libs/vr/libpdx/fuzz/message_fuzzer.cpp
new file mode 100644
index 0000000..b627045
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/message_fuzzer.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <helpers.h>
+#include <pdx/client_channel.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <thread>
+
+using namespace android::pdx;
+
+// Fuzzer for Message object functions.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+ FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp);
+ std::shared_ptr<Service> service(
+ new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint)));
+ std::shared_ptr<Channel> channel(nullptr);
+
+ // Generate a random Message object to call functions in.
+ MessageInfo info;
+ info.pid = fdp.ConsumeIntegral<int>();
+ info.tid = fdp.ConsumeIntegral<int>();
+ info.cid = fdp.ConsumeIntegral<int>();
+ info.mid = fdp.ConsumeIntegral<int>();
+ info.euid = fdp.ConsumeIntegral<int>();
+ info.egid = fdp.ConsumeIntegral<int>();
+ info.op = fdp.ConsumeIntegral<int32_t>();
+ info.flags = fdp.ConsumeIntegral<uint32_t>();
+ info.service = service.get();
+ info.channel = channel.get();
+ info.send_len = fdp.ConsumeIntegral<size_t>();
+ info.recv_len = fdp.ConsumeIntegral<size_t>();
+ info.fd_count = fdp.ConsumeIntegral<size_t>();
+ if (fdp.remaining_bytes() >= 32) {
+ std::vector<uint8_t> impulse_vec = fdp.ConsumeBytes<uint8_t>(32);
+ memcpy(info.impulse, impulse_vec.data(), 32);
+ }
+
+ Message message = Message(info);
+
+ // A bunch of getters that probably won't do much, but might as well
+ // get coverage, while we are here.
+ message.GetProcessId();
+ message.GetThreadId();
+ message.GetEffectiveUserId();
+ message.GetEffectiveGroupId();
+ message.GetChannelId();
+ message.GetMessageId();
+ message.GetOp();
+ message.GetFlags();
+ message.GetSendLength();
+ message.GetReceiveLength();
+ message.GetFileDescriptorCount();
+ message.ImpulseEnd();
+ message.replied();
+ message.IsChannelExpired();
+ message.IsServiceExpired();
+ message.GetState();
+ message.GetState();
+
+ // Some misc. functions.
+ unsigned int fd = fdp.ConsumeIntegral<unsigned int>();
+ int clear_mask = fdp.ConsumeIntegral<int>();
+ int set_mask = fdp.ConsumeIntegral<int>();
+ Status<void> status = {};
+ message.ModifyChannelEvents(clear_mask, set_mask);
+
+ // Fuzz the handle functions.
+ LocalHandle l_handle = {};
+ BorrowedHandle b_handle = {};
+ RemoteHandle r_handle = {};
+ LocalChannelHandle lc_handle = {};
+ BorrowedChannelHandle bc_handle = {};
+ RemoteChannelHandle rc_handle = {};
+ FileReference f_ref = fdp.ConsumeIntegral<int32_t>();
+ ChannelReference c_ref = fdp.ConsumeIntegral<int32_t>();
+
+ // These don't actually modify any state in the Message or params.
+ // They can be called in any order.
+ message.PushFileHandle(b_handle);
+ message.PushFileHandle(r_handle);
+ message.PushChannelHandle(lc_handle);
+ message.PushChannelHandle(bc_handle);
+ message.PushChannelHandle(rc_handle);
+ message.GetFileHandle(f_ref, &l_handle);
+ message.GetChannelHandle(c_ref, &lc_handle);
+
+ // Can only reply once, pick at random.
+ switch (fdp.ConsumeIntegral<uint8_t>()) {
+ case 0:
+ message.ReplyFileDescriptor(fd);
+ break;
+ case 1:
+ message.Reply(status);
+ break;
+ case 2:
+ message.Reply(l_handle);
+ break;
+ case 3:
+ message.Reply(b_handle);
+ break;
+ case 4:
+ message.Reply(r_handle);
+ break;
+ case 5:
+ message.Reply(lc_handle);
+ break;
+ case 6:
+ message.Reply(bc_handle);
+ break;
+ case 7:
+ message.Reply(rc_handle);
+ }
+
+ // Fuzz the channel functions.
+ int flags = fdp.ConsumeIntegral<int>();
+ int channel_id = 0;
+ message.PushChannel(flags, channel, &channel_id);
+ message.CheckChannel(service.get(), c_ref, &channel);
+ message.CheckChannel(c_ref, &channel);
+ message.PushChannel(service.get(), flags, channel, &channel_id);
+ size_t iovec_size = sizeof(iovec);
+ struct iovec* iovecs = nullptr;
+
+ // Fuzz the read/write functions. Needs at least one iovec, plus one byte.
+ if (fdp.remaining_bytes() >= iovec_size + 1) {
+ std::vector<uint8_t> tmp_vec = fdp.ConsumeBytes<uint8_t>(iovec_size);
+ struct iovec* vector = reinterpret_cast<struct iovec*>(tmp_vec.data());
+ std::vector<uint8_t> tmp_buf =
+ fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes());
+ void* buf = reinterpret_cast<void*>(tmp_buf.data());
+ size_t buf_size = fdp.ConsumeIntegral<size_t>();
+
+ // Capping num_vecs to 1024 so it doesn't allocate too much memory.
+ size_t num_vecs = fdp.ConsumeIntegralInRange<size_t>(0, 1024);
+
+ if (num_vecs > 0)
+ iovecs = new struct iovec[num_vecs];
+ for (size_t i = 0; i < num_vecs; i++) {
+ iovecs[i] = *vector;
+ }
+
+ message.ReadAll(vector, buf_size);
+ message.WriteAll(buf, buf_size);
+ message.ReadVectorAll(vector, num_vecs);
+ message.WriteVectorAll(vector, num_vecs);
+ message.ReadVector(vector, buf_size);
+ message.WriteVector(vector, buf_size);
+ }
+
+ if (iovecs != nullptr)
+ delete[] iovecs;
+ return 0;
+}
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
new file mode 100644
index 0000000..afde5f7
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+struct FuzzType {
+ int a;
+ float b;
+ std::string c;
+
+ FuzzType() {}
+ FuzzType(int a, float b, const std::string& c) : a(a), b(b), c(c) {}
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(FuzzType, a, b, c);
+};
+
+// Fuzzer for Serialization operations, this is mostly just lifted from the
+// existing test cases to use fuzzed values as inputs.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload result;
+
+ // Currently, only fuzzing subset of types. In the future, may want
+ // to add more difficult to generate types like array, map, enum, etc...
+ bool b_val = fdp.ConsumeBool();
+ uint8_t u8_val = fdp.ConsumeIntegral<uint8_t>();
+ uint16_t u16_val = fdp.ConsumeIntegral<uint16_t>();
+ uint32_t u32_val = fdp.ConsumeIntegral<uint32_t>();
+ uint64_t u64_val = fdp.ConsumeIntegral<uint64_t>();
+ int8_t i8_val = fdp.ConsumeIntegral<int8_t>();
+ int16_t i16_val = fdp.ConsumeIntegral<uint16_t>();
+ int32_t i32_val = fdp.ConsumeIntegral<uint32_t>();
+ int64_t i64_val = fdp.ConsumeIntegral<uint64_t>();
+ float f_val = fdp.ConsumeFloatingPoint<float>();
+ double d_val = fdp.ConsumeFloatingPoint<double>();
+ std::string s_val = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ std::vector<uint8_t> vec_val =
+ fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes());
+ FuzzType t1_val{reinterpret_cast<int>(i32_val), f_val, s_val};
+
+ // Types need to be individually fuzzed because code path changes depending
+ // on which type is being serialized/deserialized.
+ Serialize(b_val, &result);
+ Deserialize(&b_val, &result);
+ Serialize(u8_val, &result);
+ Deserialize(&u8_val, &result);
+ Serialize(u16_val, &result);
+ Deserialize(&u16_val, &result);
+ Serialize(u32_val, &result);
+ Deserialize(&u32_val, &result);
+ Serialize(u64_val, &result);
+ Deserialize(&u64_val, &result);
+ Serialize(i8_val, &result);
+ Deserialize(&i8_val, &result);
+ Serialize(i16_val, &result);
+ Deserialize(&i16_val, &result);
+ Serialize(i32_val, &result);
+ Deserialize(&i32_val, &result);
+ Serialize(i64_val, &result);
+ Deserialize(&i64_val, &result);
+ Serialize(f_val, &result);
+ Deserialize(&f_val, &result);
+ Serialize(d_val, &result);
+ Deserialize(&d_val, &result);
+ Serialize(s_val, &result);
+ Deserialize(&s_val, &result);
+ Serialize(WrapString(s_val), &result);
+ Deserialize(&s_val, &result);
+ Serialize(vec_val, &result);
+ Deserialize(&vec_val, &result);
+ Serialize(t1_val, &result);
+ Deserialize(&t1_val, &result);
+
+ return 0;
+}
diff --git a/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp
new file mode 100644
index 0000000..3a3bfd9
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <helpers.h>
+#include <pdx/client_channel.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <thread>
+
+using namespace android::pdx;
+
+// Dispatch fuzzer entry point. This fuzzer creates a ServiceDispatcher
+// and creates an endpoint that returns fuzzed messages that are passed
+// to the ReceiveAndDispatch and DispatchLoop functions.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ eventfd_t wakeup_val = 1;
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+ // Endpoint is only used to be immediately wrapped as a unique_ptr,
+ // so it is ok to be using a raw ptr and new here without freeing.
+ FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp);
+ std::unique_ptr<ServiceDispatcher> dispatcher = ServiceDispatcher::Create();
+ std::shared_ptr<Channel> channel(nullptr);
+ std::shared_ptr<Client> client(nullptr);
+ std::shared_ptr<Service> service(
+ new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint)));
+
+ service->SetChannel(0, std::shared_ptr<Channel>(channel));
+ dispatcher->AddService(service);
+
+ // Dispatcher blocks, so needs to run in its own thread.
+ std::thread run_dispatcher([&]() {
+ uint8_t opt = 0;
+
+ // Right now the only operations block, so the while loop is pointless
+ // but leaving it in, just in case that ever changes.
+ while (fdp.remaining_bytes() > sizeof(MessageInfo)) {
+ opt = fdp.ConsumeIntegral<uint8_t>() % dispatcher_operations.size();
+ dispatcher_operations[opt](dispatcher, &fdp);
+ }
+ });
+
+ // Continuously wake up the epoll so the dispatcher can run.
+ while (fdp.remaining_bytes() > sizeof(MessageInfo)) {
+ eventfd_write(endpoint->epoll_fd(), wakeup_val);
+ }
+
+ // Cleanup the dispatcher and thread.
+ dispatcher->SetCanceled(true);
+ if (run_dispatcher.joinable())
+ run_dispatcher.join();
+ dispatcher->RemoveService(service);
+
+ return 0;
+}
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 0556ea1..1f1bcb3 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -4,12 +4,12 @@
// found in the LICENSE file.
//
// eglext_angle.h: ANGLE modifications to the eglext.h header file.
-// Currently we don't include this file directly, we patch eglext.h
-// to include it implicitly so it is visible throughout our code.
#ifndef INCLUDE_EGL_EGLEXT_ANGLE_
#define INCLUDE_EGL_EGLEXT_ANGLE_
+#include <EGL/eglext.h>
+
// clang-format off
#ifndef EGL_ANGLE_robust_resource_initialization
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 3997134..3c76c62 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -100,12 +100,8 @@
"libgraphicsenv",
"libnativewindow",
"libbacktrace",
+ "libbase",
],
- target: {
- vendor: {
- exclude_shared_libs: ["libgraphicsenv"],
- },
- },
}
cc_library_static {
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index 74c4d7d..74fb019 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -21,7 +21,7 @@
#include <errno.h>
#include <inttypes.h>
-#include <cutils/properties.h>
+#include <android-base/properties.h>
#include <log/log.h>
#include <chrono>
@@ -165,7 +165,8 @@
}
size_t BlobCache::getFlattenedSize() const {
- size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX);
+ auto buildId = base::GetProperty("ro.build.id", "");
+ size_t size = align4(sizeof(Header) + buildId.size());
for (const CacheEntry& e : mCacheEntries) {
std::shared_ptr<Blob> const& keyBlob = e.getKey();
std::shared_ptr<Blob> const& valueBlob = e.getValue();
@@ -185,9 +186,9 @@
header->mBlobCacheVersion = blobCacheVersion;
header->mDeviceVersion = blobCacheDeviceVersion;
header->mNumEntries = mCacheEntries.size();
- char buildId[PROPERTY_VALUE_MAX];
- header->mBuildIdLength = property_get("ro.build.id", buildId, "");
- memcpy(header->mBuildId, buildId, header->mBuildIdLength);
+ auto buildId = base::GetProperty("ro.build.id", "");
+ header->mBuildIdLength = buildId.size();
+ memcpy(header->mBuildId, buildId.c_str(), header->mBuildIdLength);
// Write cache entries
uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
@@ -238,12 +239,11 @@
ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
return -EINVAL;
}
- char buildId[PROPERTY_VALUE_MAX];
- int len = property_get("ro.build.id", buildId, "");
+ auto buildId = base::GetProperty("ro.build.id", "");
if (header->mBlobCacheVersion != blobCacheVersion ||
- header->mDeviceVersion != blobCacheDeviceVersion ||
- len != header->mBuildIdLength ||
- strncmp(buildId, header->mBuildId, len)) {
+ header->mDeviceVersion != blobCacheDeviceVersion ||
+ buildId.size() != header->mBuildIdLength ||
+ strncmp(buildId.c_str(), header->mBuildId, buildId.size())) {
// We treat version mismatches as an empty cache.
return 0;
}
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index cc42ac7..3284778 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -17,11 +17,13 @@
#include "FileBlobCache.h"
#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
-#include <log/log.h>
#include <sys/mman.h>
#include <sys/stat.h>
+#include <unistd.h>
+#include <log/log.h>
// Cache file header
static const char* cacheFileMagic = "EGL$";
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index d664e4d..1afc693 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,27 +17,23 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <EGL/Loader.h>
+#include "EGL/Loader.h"
+
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <utils/Timers.h>
+#include <vndksupport/linker.h>
#include <string>
-#include <dirent.h>
-#include <dlfcn.h>
-
-#include <android/dlext.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <utils/Timers.h>
-
-#ifndef __ANDROID_VNDK__
-#include <graphicsenv/GraphicsEnv.h>
-#endif
-#include <vndksupport/linker.h>
-
+#include "EGL/eglext_angle.h"
#include "egl_platform_entries.h"
#include "egl_trace.h"
#include "egldefs.h"
-#include <EGL/eglext_angle.h>
namespace android {
@@ -159,13 +155,11 @@
return true;
}
-#ifndef __ANDROID_VNDK__
// Return true if updated driver namespace is set.
ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (ns) {
return true;
}
-#endif
return false;
}
@@ -241,12 +235,12 @@
// i.e.:
// libGLES_${prop}.so, or:
// libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
- char prop[PROPERTY_VALUE_MAX + 1];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
- if (property_get(key, prop, nullptr) <= 0) {
+ auto prop = base::GetProperty(key, "");
+ if (prop.empty()) {
continue;
}
- hnd = attempt_to_load_system_driver(cnx, prop, true);
+ hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
if (hnd) {
break;
} else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
@@ -276,7 +270,7 @@
// will set cnx->useAngle appropriately.
// Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
// not just loading ANGLE as option.
- init_angle_backend(hnd->dso[0], cnx);
+ init_angle_backend(hnd->dso[2], cnx);
}
LOG_ALWAYS_FATAL_IF(!hnd,
@@ -370,7 +364,7 @@
f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
/*
- * GL_EXT_debug_label is special, we always report it as
+ * GL_EXT_debug_marker is special, we always report it as
* supported, it's handled by GLES_trace. If GLES_trace is not
* enabled, then these are no-ops.
*/
@@ -510,9 +504,9 @@
.library_namespace = ns,
};
void* so = nullptr;
- char prop[PROPERTY_VALUE_MAX + 1];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
- if (property_get(key, prop, nullptr) <= 0) {
+ auto prop = base::GetProperty(key, "");
+ if (prop.empty()) {
continue;
}
std::string name = std::string("lib") + kind + "_" + prop + ".so";
@@ -557,12 +551,8 @@
}
void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
- void* eglCreateDeviceANGLE = nullptr;
-
- ALOGV("dso: %p", dso);
- eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE");
- ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE);
- if (eglCreateDeviceANGLE) {
+ void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
+ if (pANGLEGetDisplayPlatform) {
ALOGV("ANGLE GLES library in use");
cnx->useAngle = true;
} else {
@@ -573,7 +563,7 @@
Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
ATRACE_CALL();
-#ifndef __ANDROID_VNDK__
+
android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns) {
return nullptr;
@@ -603,9 +593,6 @@
hnd->set(dso, GLESv2);
}
return hnd;
-#else
- return nullptr;
-#endif
}
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index d5d57d7..43f7a07 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -18,7 +18,7 @@
#include <EGL/egl.h>
-#include <cutils/properties.h>
+#include <android-base/properties.h>
#include <log/log.h>
@@ -58,9 +58,7 @@
} else {
LOG_ALWAYS_FATAL(error);
}
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.callstack", value, "0");
- if (atoi(value)) {
+ if (base::GetBoolProperty("debug.egl.callstack", false)) {
CallStack::log(LOG_TAG);
}
}
@@ -224,9 +222,7 @@
pthread_mutex_unlock(&sLogPrintMutex);
if (printLog) {
ALOGE("called unimplemented OpenGL ES API");
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.callstack", value, "0");
- if (atoi(value)) {
+ if (base::GetBoolProperty("debug.egl.callstack", false)) {
CallStack::log(LOG_TAG);
}
}
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 00caff2..97dc0f1 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -16,7 +16,6 @@
#if defined(__ANDROID__)
-#include <cutils/properties.h>
#include "Loader.h"
#include "egl_angle_platform.h"
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 6af7cd2..8c6f284 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -19,25 +19,22 @@
#include "egl_display.h"
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+
#include "../egl_impl.h"
-
-#include <EGL/eglext_angle.h>
-#include <private/EGL/display.h>
-
-#include <cutils/properties.h>
+#include "EGL/eglext_angle.h"
#include "Loader.h"
#include "egl_angle_platform.h"
#include "egl_cache.h"
#include "egl_object.h"
#include "egl_tls.h"
-
-#include <SurfaceFlingerProperties.h>
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <graphicsenv/GraphicsEnv.h>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
+#include "private/EGL/display.h"
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
@@ -73,7 +70,7 @@
}
bool needsAndroidPEglMitigation() {
- static const int32_t vndk_version = property_get_int32("ro.vndk.version", -1);
+ static const int32_t vndk_version = base::GetIntProperty("ro.vndk.version", -1);
return vndk_version <= 28;
}
@@ -151,10 +148,8 @@
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
- char prop[PROPERTY_VALUE_MAX];
- property_get("debug.angle.validation", prop, "0");
attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
- attrs.push_back(atoi(prop));
+ attrs.push_back(base::GetBoolProperty("debug.angle.validation", false));
attrs.push_back(EGL_NONE);
@@ -372,16 +367,8 @@
egl_cache_t::get()->initialize(this);
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.finish", value, "0");
- if (atoi(value)) {
- finishOnSwap = true;
- }
-
- property_get("debug.egl.traceGpuCompletion", value, "0");
- if (atoi(value)) {
- traceGpuCompletion = true;
- }
+ finishOnSwap = base::GetBoolProperty("debug.egl.finish", false);
+ traceGpuCompletion = base::GetBoolProperty("debug.egl.traceGpuCompletion", false);
// TODO: If device doesn't provide 1.4 or 1.5 then we'll be
// changing the behavior from the past where we always advertise
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index 44a1c0b..ea86c9a 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -18,9 +18,9 @@
#include <EGL/egl.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/dlext.h>
-#include <cutils/properties.h>
#include <dlfcn.h>
#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
@@ -157,9 +157,7 @@
if (debug_layers.empty()) {
// Only check system properties if Java settings are empty
- char prop[PROPERTY_VALUE_MAX];
- property_get("debug.gles.layers", prop, "");
- debug_layers = prop;
+ debug_layers = base::GetProperty("debug.gles.layers", "");
}
return debug_layers;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index ff4fe2d..fd426c2 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -309,6 +309,18 @@
gl_extensions = exts;
if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
gl_extensions.insert(0, "GL_EXT_debug_marker ");
+ // eglGetProcAddress could return function pointers to these
+ // functions while they actually don't work. Fix them now.
+ __eglMustCastToProperFunctionPointerType* f;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glInsertEventMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPushGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
+ f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version]
+ ->gl.glPopGroupMarkerEXT;
+ if (*f != gl_noop) *f = gl_noop;
}
// tokenize the supported extensions for the glGetStringi() wrapper
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index c976c60..1119e4a 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,36 +18,32 @@
#include "egl_platform_entries.h"
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/hardware_buffer.h>
#include <ctype.h>
+#include <cutils/compiler.h>
#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <private/android/AHardwareBufferHelpers.h>
#include <stdlib.h>
#include <string.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
-#include <android/hardware_buffer.h>
-#include <android-base/strings.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
#include <condition_variable>
#include <deque>
#include <mutex>
-#include <unordered_map>
#include <string>
#include <thread>
+#include <unordered_map>
#include "../egl_impl.h"
-
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "EGL/eglext_angle.h"
#include "egl_display.h"
-#include "egl_object.h"
#include "egl_layers.h"
+#include "egl_object.h"
#include "egl_tls.h"
#include "egl_trace.h"
@@ -96,7 +92,7 @@
"EGL_EXT_surface_CTA861_3_metadata "
;
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+// Allowed list of extensions exposed to applications if implemented in the vendor driver.
char const * const gExtensionString =
"EGL_KHR_image " // mandatory
"EGL_KHR_image_base " // mandatory
@@ -381,10 +377,7 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso) {
if (attrib_list) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.force_msaa", value, "false");
-
- if (!strcmp(value, "true")) {
+ if (base::GetBoolProperty("debug.egl.force_msaa", false)) {
size_t attribCount = 0;
EGLint attrib = attrib_list[0];
@@ -2251,15 +2244,8 @@
}
EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
- // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
- // this function cannot be implemented when this libEGL is built for
- // vendors.
-#ifndef __ANDROID_VNDK__
if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
}
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index aaecb62..8d118e0 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -18,7 +18,7 @@
#include <stdlib.h>
-#include <cutils/properties.h>
+#include <android-base/properties.h>
#include <log/log.h>
#include "CallStack.h"
#include "egl_platform_entries.h"
@@ -96,9 +96,7 @@
if (!quiet) {
ALOGE("%s:%d error %x (%s)",
caller, line, error, egl_strerror(error));
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.callstack", value, "0");
- if (atoi(value)) {
+ if (base::GetBoolProperty("debug.egl.callstack", false)) {
CallStack::log(LOG_TAG);
}
}
diff --git a/opengl/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp
index 65f89b1..8d4323f 100644
--- a/opengl/tests/gl2_jni/Android.bp
+++ b/opengl/tests/gl2_jni/Android.bp
@@ -17,6 +17,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp
index 5bec336..0cb129a 100644
--- a/opengl/tests/gl_jni/Android.bp
+++ b/opengl/tests/gl_jni/Android.bp
@@ -19,6 +19,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp
index cf899ac..66afb6a 100644
--- a/opengl/tests/gl_perfapp/Android.bp
+++ b/opengl/tests/gl_perfapp/Android.bp
@@ -17,6 +17,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp
index 2432566..1006d44 100644
--- a/opengl/tests/gldual/Android.bp
+++ b/opengl/tests/gldual/Android.bp
@@ -20,6 +20,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 41fcf1b..9efd38f 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -1,6 +1,18 @@
#!/bin/bash
set -u
set -e
+
+if [ -z "$ANDROID_BUILD_TOP" ] ; then
+ echo "ANDROID_BUILD_TOP is not set, did you run lunch?"
+ exit 1
+fi
+
+# Avoid spewing files in any location other than the intended one.
+if [ ! -x "$PWD/gen" ] ; then
+ echo "Run this script from its parent directory".
+ exit 1
+fi
+
rm -rf out generated
mkdir out
@@ -92,7 +104,7 @@
# Add UnsupportedAppUsage.java to known sources.
mkdir -p out/android/compat/annotation
-cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
+cp ${ANDROID_BUILD_TOP}/tools/platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
pushd out > /dev/null
mkdir classes
@@ -153,23 +165,23 @@
fi
}
-compareGenerated ../../../../base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp
-compareGenerated ../../../../base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java
+compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp
+compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java
for x in GL.java GL10.java GL10Ext.java GL11.java GL11Ext.java GL11ExtensionPack.java
do
- compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
done
for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
do
- compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
- compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C android_opengl_${x}.cpp
done
for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
do
- compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java
done
if [ $KEEP_GENERATED == "0" ] ; then
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index 9932556..951ecff 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -21,8 +21,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.SurfaceTexture;
import android.view.Surface;
-import android.view.SurfaceView;
import android.view.SurfaceHolder;
+import android.view.SurfaceView;
/**
* EGL 1.4
diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
index 93203fd..b2ea041 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
@@ -20,7 +20,7 @@
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.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/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
index 34cb3e1..6dffac5 100644
--- a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -20,7 +20,7 @@
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
index b3b0690..be8b3e3 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
@@ -20,7 +20,7 @@
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.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 e763b4e..d84a693 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 <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.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 c12efc3..9cab1d6 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -20,7 +20,7 @@
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
diff --git a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
index afcc3eb..32c9d7d 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
+++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
@@ -19,6 +19,7 @@
package com.google.android.gles_jni;
import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
@@ -26,6 +27,7 @@
import android.util.Log;
import java.nio.Buffer;
+
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL10Ext;
import javax.microedition.khronos.opengles.GL11;
@@ -55,6 +57,7 @@
private boolean have_OES_framebuffer_object;
private boolean have_OES_texture_cube_map;
+ @UnsupportedAppUsage
public GLImpl() {
}
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index ffb7b5e..5c7f344 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -2,4 +2,3 @@
class hal
user graphics
group automotive_evs
- disabled # will not automatically start with its class; must be explicitly started.
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 6eed24a..1bcaab4 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,6 +21,8 @@
"libbinder",
"libcutils",
"libgfxstats",
+ "libgpumem",
+ "libgpumemtracer",
"libgraphicsenv",
"liblog",
"libutils",
@@ -85,6 +87,10 @@
name: "gpuservice",
defaults: ["libgpuservice_binary"],
init_rc: ["gpuservice.rc"],
+ required: [
+ "bpfloader",
+ "gpu_mem.o",
+ ],
srcs: [":gpuservice_binary_sources"],
shared_libs: [
"libgpuservice",
diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk
new file mode 100644
index 0000000..482fc6d
--- /dev/null
+++ b/services/gpuservice/CleanSpec.mk
@@ -0,0 +1,52 @@
+# Copyright 2020 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Remove gpu_mem.o
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp)
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 7bb0e4a..18c819f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,13 +24,16 @@
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
+#include <gpumem/GpuMem.h>
#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
+#include <tracing/GpuMemTracer.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-
#include <vkjson.h>
+#include <thread>
+
namespace android {
using base::StringAppendF;
@@ -45,7 +48,16 @@
const char* const GpuService::SERVICE_NAME = "gpu";
-GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){};
+GpuService::GpuService()
+ : mGpuMem(std::make_shared<GpuMem>()),
+ mGpuStats(std::make_unique<GpuStats>()),
+ mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
+ std::thread asyncInitThread([this]() {
+ mGpuMem->initialize();
+ mGpuMemTracer->initialize(mGpuMem);
+ });
+ asyncInitThread.detach();
+};
void GpuService::setGpuStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
@@ -62,6 +74,14 @@
mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
}
+void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
+ developerDriverPath = driverPath;
+}
+
+std::string GpuService::getUpdatableDriverPath() {
+ return developerDriverPath;
+}
+
status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
ATRACE_CALL();
@@ -90,24 +110,31 @@
} else {
bool dumpAll = true;
bool dumpDriverInfo = false;
+ bool dumpMem = false;
bool dumpStats = false;
size_t numArgs = args.size();
if (numArgs) {
- dumpAll = false;
for (size_t index = 0; index < numArgs; ++index) {
if (args[index] == String16("--gpustats")) {
dumpStats = true;
} else if (args[index] == String16("--gpudriverinfo")) {
dumpDriverInfo = true;
+ } else if (args[index] == String16("--gpumem")) {
+ dumpMem = true;
}
}
+ dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
}
if (dumpAll || dumpDriverInfo) {
dumpGameDriverInfo(&result);
result.append("\n");
}
+ if (dumpAll || dumpMem) {
+ mGpuMem->dump(args, &result);
+ result.append("\n");
+ }
if (dumpAll || dumpStats) {
mGpuStats->dump(args, &result);
result.append("\n");
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index b3e34d5..43faa3e 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,7 +28,9 @@
namespace android {
+class GpuMem;
class GpuStats;
+class GpuMemTracer;
class GpuService : public BnGpuService, public PriorityDumper {
public:
@@ -50,6 +52,8 @@
int64_t driverLoadingTime) override;
void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value) override;
+ void setUpdatableDriverPath(const std::string& driverPath) override;
+ std::string getUpdatableDriverPath() override;
/*
* IBinder interface
@@ -72,7 +76,10 @@
/*
* Attributes
*/
+ std::shared_ptr<GpuMem> mGpuMem;
std::unique_ptr<GpuStats> mGpuStats;
+ std::unique_ptr<GpuMemTracer> mGpuMemTracer;
+ std::string developerDriverPath;
};
} // namespace android
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
new file mode 100644
index 0000000..b875814
--- /dev/null
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -0,0 +1,22 @@
+// Copyright 2020 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.
+
+bpf {
+ name: "gpu_mem.o",
+ srcs: ["gpu_mem.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
new file mode 100644
index 0000000..c75213b
--- /dev/null
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 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 <bpf_helpers.h>
+
+/*
+ * On Android the number of active processes using gpu is limited.
+ * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024
+ */
+#define GPU_MEM_TOTAL_MAP_SIZE 1024
+
+/*
+ * This map maintains the global and per process gpu memory total counters.
+ *
+ * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes.
+ * Use HASH type here since key is not int.
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ */
+DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE,
+ AID_GRAPHICS);
+
+/* This struct aligns with the fields offsets of the raw tracepoint format */
+struct gpu_mem_total_args {
+ uint64_t ignore;
+ /* Actual fields start at offset 8 */
+ uint32_t gpu_id;
+ uint32_t pid;
+ uint64_t size;
+};
+
+/*
+ * This program parses the gpu_mem/gpu_mem_total tracepoint's data into
+ * {KEY, VAL} pair used to update the corresponding bpf map.
+ *
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ * Upon seeing size 0, the corresponding KEY needs to be cleaned up.
+ */
+DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total)
+(struct gpu_mem_total_args* args) {
+ uint64_t key = 0;
+ uint64_t cur_val = 0;
+ uint64_t* prev_val = NULL;
+
+ /* The upper 32 bits are for gpu_id while the lower is the pid */
+ key = ((uint64_t)args->gpu_id << 32) | args->pid;
+ cur_val = args->size;
+
+ if (!cur_val) {
+ bpf_gpu_mem_total_map_delete_elem(&key);
+ return 0;
+ }
+
+ prev_val = bpf_gpu_mem_total_map_lookup_elem(&key);
+ if (prev_val) {
+ *prev_val = cur_val;
+ } else {
+ bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST);
+ }
+ return 0;
+}
+
+char _license[] SEC("license") = "Apache 2.0";
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
new file mode 100644
index 0000000..b2230b6
--- /dev/null
+++ b/services/gpuservice/gpumem/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumem",
+ srcs: [
+ "GpuMem.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "libbase",
+ "libbpf_android",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
new file mode 100644
index 0000000..245edb8
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMem"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpumem/GpuMem.h"
+
+#include <android-base/stringprintf.h>
+#include <libbpf.h>
+#include <libbpf_android.h>
+#include <log/log.h>
+#include <unistd.h>
+#include <utils/Trace.h>
+
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+using base::StringAppendF;
+
+GpuMem::~GpuMem() {
+ bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
+}
+
+void GpuMem::initialize() {
+ // Make sure bpf programs are loaded
+ bpf::waitForProgsLoaded();
+
+ int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+ if (fd < 0) {
+ ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+ return;
+ }
+
+ // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+ int count = 0;
+ while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+ if (++count > kGpuWaitTimeout) {
+ ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
+ kGpuMemTotalTracepoint);
+ return;
+ }
+ // Retry until GPU driver loaded or timeout.
+ sleep(1);
+ }
+
+ // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+ if (!map.isValid()) {
+ ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+ return;
+ }
+ setGpuMemTotalMap(map);
+
+ mInitialized.store(true);
+}
+
+void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMemTotalMap = std::move(map);
+}
+
+// Dump the snapshots of global and per process memory usage on all gpus
+void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
+ ATRACE_CALL();
+
+ if (!mInitialized.load() || !mGpuMemTotalMap.isValid()) {
+ result->append("Failed to initialize GPU memory eBPF\n");
+ return;
+ }
+
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) {
+ result->append("GPU memory total usage map is empty\n");
+ return;
+ }
+ uint64_t key = res.value();
+ // unordered_map<gpu_id, vector<pair<pid, size>>>
+ std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ dumpMap[gpu_id].emplace_back(pid, size);
+
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+
+ for (auto& gpu : dumpMap) {
+ if (gpu.second.empty()) continue;
+ StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
+
+ std::sort(gpu.second.begin(), gpu.second.end(),
+ [](auto& l, auto& r) { return l.first < r.first; });
+
+ int i = 0;
+ if (gpu.second[0].first != 0) {
+ StringAppendF(result, "Global total: N/A\n");
+ } else {
+ StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
+ i++;
+ }
+ for (; i < gpu.second.size(); i++) {
+ StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
+ gpu.second[i].second);
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
new file mode 100644
index 0000000..49a9f95
--- /dev/null
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class GpuMem {
+public:
+ GpuMem() = default;
+ ~GpuMem();
+
+ // initialize eBPF program and map
+ void initialize();
+ // dumpsys interface
+ void dump(const Vector<String16>& args, std::string* result);
+ bool isInitialized() { return mInitialized.load(); }
+
+ // Traverse the map and send each value read back to the callback function.
+ // Used for tracing.
+ template <typename lambda>
+ void traceGpuMemTotals(lambda tracerCallback) {
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) return;
+ uint64_t key = res.value();
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ tracerCallback(gpu_id, pid, size);
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+ }
+
+private:
+ // Friend class for testing.
+ friend class TestableGpuMem;
+
+ // set gpu memory total map
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+
+ // indicate whether ebpf has been initialized
+ std::atomic<bool> mInitialized = false;
+ // bpf map for GPU memory total data
+ android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+
+ // gpu memory tracepoint event category
+ static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
+ // gpu memory total tracepoint
+ static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total";
+ // pinned gpu memory total bpf c program path in bpf sysfs
+ static constexpr char kGpuMemTotalProgPath[] =
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total";
+ // pinned gpu memory total bpf map path in bpf sysfs
+ static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+ // 30 seconds timeout for trying to attach bpf program to tracepoint
+ static constexpr int kGpuWaitTimeout = 30;
+};
+
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 538506d..db81f5d 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -19,11 +19,16 @@
address: true,
},
srcs: [
+ "GpuMemTest.cpp",
"GpuStatsTest.cpp",
],
shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
"libcutils",
"libgfxstats",
+ "libgpumem",
"libgraphicsenv",
"liblog",
"libstatslog",
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
new file mode 100644
index 0000000..abaf30a
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <android-base/stringprintf.h>
+#include <bpf/BpfMap.h>
+#include <gmock/gmock.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+namespace {
+
+using base::StringPrintf;
+using testing::HasSubstr;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+constexpr uint64_t TEST_PROC_VAL_1 = 234;
+constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
+constexpr uint64_t TEST_PROC_VAL_2 = 345;
+
+class GpuMemTest : public testing::Test {
+public:
+ GpuMemTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~GpuMemTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ void SetUp() override {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_EQ(0, bpf::setrlimitForTest());
+
+ mGpuMem = std::make_unique<GpuMem>();
+ mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+ mTestableGpuMem.setInitialized();
+ errno = 0;
+ mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC);
+
+ EXPECT_EQ(0, errno);
+ EXPECT_LE(0, mTestMap.getMap().get());
+ EXPECT_TRUE(mTestMap.isValid());
+ }
+
+ std::string dumpsys() {
+ std::string result;
+ Vector<String16> args;
+ mGpuMem->dump(args, &result);
+ return result;
+ }
+
+ std::unique_ptr<GpuMem> mGpuMem;
+ TestableGpuMem mTestableGpuMem;
+ bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map");
+}
+
+TEST_F(GpuMemTest, bpfInitializationFailed) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
+}
+
+TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
+}
+
+TEST_F(GpuMemTest, globalMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr(StringPrintf("Global total: %" PRIu64 "\n", TEST_GLOBAL_VAL)));
+}
+
+TEST_F(GpuMemTest, missingGlobalMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr("Global total: N/A"));
+}
+
+TEST_F(GpuMemTest, procMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_1 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_1,
+ TEST_PROC_VAL_1)));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_2 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_2,
+ TEST_PROC_VAL_2)));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
new file mode 100644
index 0000000..6c8becb
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+
+namespace android {
+
+class TestableGpuMem {
+public:
+ TestableGpuMem() = default;
+ explicit TestableGpuMem(GpuMem *gpuMem) : mGpuMem(gpuMem) {}
+
+ void setInitialized() { mGpuMem->mInitialized.store(true); }
+
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMem->setGpuMemTotalMap(map);
+ }
+
+ std::string getGpuMemTraceGroup() { return mGpuMem->kGpuMemTraceGroup; }
+
+ std::string getGpuMemTotalTracepoint() { return mGpuMem->kGpuMemTotalTracepoint; }
+
+ std::string getGpuMemTotalProgPath() { return mGpuMem->kGpuMemTotalProgPath; }
+
+ std::string getGpuMemTotalMapPath() { return mGpuMem->kGpuMemTotalMapPath; }
+
+private:
+ GpuMem *mGpuMem;
+};
+
+} // namespace android
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
new file mode 100644
index 0000000..919fed3
--- /dev/null
+++ b/services/gpuservice/tracing/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumemtracer",
+ srcs: [
+ "GpuMemTracer.cpp",
+ ],
+ shared_libs: [
+ "libgpumem",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libperfetto_client_experimental",
+ ],
+ export_include_dirs: ["include"],
+ export_static_lib_headers: [
+ "libperfetto_client_experimental",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
new file mode 100644
index 0000000..c9bfa57
--- /dev/null
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMemTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "tracing/GpuMemTracer.h"
+
+#include <gpumem/GpuMem.h>
+#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
+#include <unistd.h>
+#include <utils/Timers.h>
+
+#include <algorithm>
+#include <thread>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
+
+namespace android {
+
+std::mutex GpuMemTracer::sTraceMutex;
+std::condition_variable GpuMemTracer::sCondition;
+bool GpuMemTracer::sTraceStarted;
+
+void GpuMemTracer::initialize(std::shared_ptr<GpuMem> gpuMem) {
+ if (!gpuMem->isInitialized()) {
+ ALOGE("Cannot initialize GpuMemTracer before GpuMem");
+ return;
+ }
+ mGpuMem = gpuMem;
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ // TODO(b/160016498): Find a better way to wait for traced
+ // Sleep for 30 seconds to make sure the data source is registered only
+ // after traced starts.
+ sleep(30);
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+ std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+ pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
+ tracerThread.detach();
+}
+
+void GpuMemTracer::registerDataSource() {
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(kGpuMemDataSource);
+ GpuMemDataSource::Register(dsd);
+}
+
+void GpuMemTracer::threadLoop() {
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ while (!sTraceStarted) {
+ sCondition.wait(lock);
+ }
+ }
+ traceInitialCounters();
+ {
+ std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = false;
+ }
+ }
+}
+
+void GpuMemTracer::traceInitialCounters() {
+ if (!mGpuMem->isInitialized()) {
+ // This should never happen.
+ ALOGE("Cannot trace without GpuMem initialization");
+ return;
+ }
+ mGpuMem->traceGpuMemTotals([](uint32_t gpuId, uint32_t pid, uint64_t size) {
+ GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp(systemTime());
+ auto* event = packet->set_gpu_mem_total_event();
+ event->set_gpu_id(gpuId);
+ event->set_pid(pid);
+ event->set_size(size);
+ });
+ });
+ // Flush the TraceContext. The last packet in the above loop will go
+ // missing without this flush.
+ GpuMemDataSource::Trace([](GpuMemDataSource::TraceContext ctx) { ctx.Flush(); });
+}
+
+} // namespace android
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
new file mode 100644
index 0000000..40deb4c
--- /dev/null
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <perfetto/tracing.h>
+
+#include <mutex>
+
+namespace android {
+
+class GpuMem;
+
+class GpuMemTracer {
+public:
+ class GpuMemDataSource : public perfetto::DataSource<GpuMemDataSource> {
+ virtual void OnSetup(const SetupArgs&) override{};
+ virtual void OnStart(const StartArgs&) override {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = true;
+ sCondition.notify_all();
+ }
+ virtual void OnStop(const StopArgs&) override{};
+ };
+
+ ~GpuMemTracer() = default;
+
+ // Sets up the perfetto tracing backend and data source.
+ void initialize(std::shared_ptr<GpuMem>);
+ // Registers the data source with the perfetto backend. Called as part of initialize()
+ // and should not be called manually outside of tests. Public to allow for substituting a
+ // perfetto::kInProcessBackend in tests.
+ void registerDataSource();
+
+ static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
+ static std::condition_variable sCondition;
+ static std::mutex sTraceMutex;
+ static bool sTraceStarted;
+
+private:
+ void traceInitialCounters();
+ void threadLoop();
+
+ std::shared_ptr<GpuMem> mGpuMem;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f67c9d0..d1a3e9a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -21,6 +21,7 @@
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
+ "-Wshadow",
],
}
@@ -99,6 +100,7 @@
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
+ "VibrationElement.cpp"
],
}
@@ -110,6 +112,7 @@
"libcutils",
"libinput",
"liblog",
+ "libui",
"libutils",
],
header_libs: [
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e68946d..e49667e 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -93,16 +93,15 @@
class BinderWindowHandle : public InputWindowHandle {
public:
- BinderWindowHandle(const InputWindowInfo& info) {
- mInfo = info;
- }
+ BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; }
bool updateInfo() override {
return true;
}
};
-void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos,
+binder::Status InputManager::setInputWindows(
+ const std::vector<InputWindowInfo>& infos,
const sp<ISetInputWindowsListener>& setInputWindowsListener) {
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
@@ -116,26 +115,40 @@
if (setInputWindowsListener) {
setInputWindowsListener->onSetInputWindowsFinished();
}
+ return binder::Status::ok();
}
// Used by tests only.
-void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::registerInputChannel(const InputChannel& channel) {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid != AID_SHELL && uid != AID_ROOT) {
ALOGE("Invalid attempt to register input channel over IPC"
"from non shell/root entity (PID: %d)", ipc->getCallingPid());
- return;
+ return binder::Status::ok();
}
- mDispatcher->registerInputChannel(channel);
+
+ mDispatcher->registerInputChannel(channel.dup());
+ return binder::Status::ok();
}
-void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
+binder::Status InputManager::unregisterInputChannel(const InputChannel& channel) {
mDispatcher->unregisterInputChannel(channel);
+ return binder::Status::ok();
}
-void InputManager::setMotionClassifierEnabled(bool enabled) {
- mClassifier->setMotionClassifierEnabled(enabled);
+status_t InputManager::dump(int fd, const Vector<String16>& args) {
+ std::string dump;
+
+ dump += " InputFlinger dump\n";
+
+ ::write(fd, dump.c_str(), dump.size());
+ return NO_ERROR;
+}
+
+binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
+ mDispatcher->setFocusedWindow(request);
+ return binder::Status::ok();
}
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 0158441..bf86a98 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -26,15 +26,19 @@
#include <InputDispatcherInterface.h>
#include <InputDispatcherPolicyInterface.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/os/ISetInputWindowsListener.h>
#include <input/Input.h>
#include <input/InputTransport.h>
-#include <input/IInputFlinger.h>
+#include <android/os/BnInputFlinger.h>
+#include <android/os/IInputFlinger.h>
#include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
namespace android {
class InputChannel;
@@ -43,17 +47,19 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager has two components.
+ * The input manager has three components.
*
* 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
- * policy, and posts messages to a queue managed by the InputDispatcherThread.
- * 2. The InputDispatcher class starts a thread that waits for new events on the
- * queue and asynchronously dispatches them to applications.
+ * policy, and posts messages to a queue managed by the InputClassifier.
+ * 2. The InputClassifier class starts a thread to communicate with the device-specific
+ * classifiers. It then waits on the queue of events from InputReader, applies a classification
+ * to them, and queues them for the InputDispatcher.
+ * 3. The InputDispatcher class starts a thread that waits for new events on the
+ * previous queue and asynchronously dispatches them to applications.
*
- * By design, the InputReader class and InputDispatcher class do not share any
- * internal state. Moreover, all communication is done one way from the InputReader
- * into the InputDispatcherThread and never the reverse. Both classes may interact with the
- * InputDispatchPolicy, however.
+ * By design, none of these classes share any internal state. Moreover, all communication is
+ * done one way from the InputReader to the InputDispatcher and never the reverse. All
+ * classes may interact with the InputDispatchPolicy, however.
*
* The InputManager class never makes any calls into Java itself. Instead, the
* InputDispatchPolicy is responsible for performing all external interactions with the
@@ -74,33 +80,37 @@
/* Gets the input reader. */
virtual sp<InputReaderInterface> getReader() = 0;
+ /* Gets the input classifier */
+ virtual sp<InputClassifierInterface> getClassifier() = 0;
+
/* Gets the input dispatcher. */
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:
- virtual ~InputManager();
+ ~InputManager() override;
public:
InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
- virtual status_t start();
- virtual status_t stop();
+ status_t start() override;
+ status_t stop() override;
- virtual sp<InputReaderInterface> getReader();
- virtual sp<InputClassifierInterface> getClassifier();
- virtual sp<InputDispatcherInterface> getDispatcher();
+ sp<InputReaderInterface> getReader() override;
+ sp<InputClassifierInterface> getClassifier() override;
+ sp<InputDispatcherInterface> getDispatcher() override;
- virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
- const sp<ISetInputWindowsListener>& setInputWindowsListener);
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setInputWindows(
+ const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- virtual void registerInputChannel(const sp<InputChannel>& channel);
- virtual void unregisterInputChannel(const sp<InputChannel>& channel);
-
- void setMotionClassifierEnabled(bool enabled);
+ binder::Status registerInputChannel(const InputChannel& channel) override;
+ binder::Status unregisterInputChannel(const InputChannel& channel) override;
+ binder::Status setFocusedWindow(const FocusRequest&) override;
private:
sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp
new file mode 100644
index 0000000..a69f5d0
--- /dev/null
+++ b/services/inputflinger/VibrationElement.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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 "VibrationElement.h"
+
+#include <android-base/stringprintf.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+// The sentinel to use the default amplitude
+static const int DEFAULT_AMPLITUDE = -1;
+
+// The vibration magnitude for the "DEFAULT_AMPLITUDE" magnitude constant.
+static const uint16_t DEFAULT_MAGNITUDE = 0xc000;
+
+void VibrationElement::dump(std::string& dump) const {
+ dump += StringPrintf("[duration=%lldms, channels=[", duration.count());
+
+ if (channels.size()) {
+ dump += std::to_string(channels[0]);
+ std::for_each(channels.begin() + 1, channels.end(), [&dump](int channel) {
+ dump += ", ";
+ dump += std::to_string(channel);
+ });
+ }
+ dump += "]]";
+}
+
+uint16_t VibrationElement::getChannel(int id) const {
+ if (id >= (int)channels.size()) {
+ return 0;
+ }
+
+ // android framework uses DEFAULT_AMPLITUDE to signal that the vibration
+ // should use some built-in default value, denoted here as DEFAULT_MAGNITUDE
+ if (channels[id] == DEFAULT_AMPLITUDE) {
+ return DEFAULT_MAGNITUDE;
+ }
+
+ // convert range [0,255] to [0,65535] (android framework to linux ff ranges)
+ return ((uint16_t)channels[id]) << 8;
+}
+
+bool VibrationElement::isOn() const {
+ return std::any_of(channels.begin(), channels.end(),
+ [](uint16_t channel) { return channel != 0; });
+}
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 7c5c9c5..4e4af7e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -28,8 +28,8 @@
static const int32_t INJECTOR_PID = 999;
static const int32_t INJECTOR_UID = 1001;
-static const int32_t INJECT_EVENT_TIMEOUT = 5000;
-static const int32_t DISPATCHING_TIMEOUT = 100000;
+static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
static nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -45,49 +45,45 @@
virtual ~FakeInputDispatcherPolicy() {}
private:
- virtual void notifyConfigurationChanged(nsecs_t) override {}
+ void notifyConfigurationChanged(nsecs_t) override {}
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&, const sp<IBinder>&,
- const std::string& name) override {
+ std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
+ const std::string& name) override {
ALOGE("The window is not responding : %s", name.c_str());
- return 0;
+ return 0s;
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
- uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
- KeyEvent*) override {
+ bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+ void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
- virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
- return false;
- }
+ bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
InputDispatcherConfiguration mConfig;
};
@@ -132,14 +128,17 @@
protected:
explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
: mDispatcher(dispatcher) {
- InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+ mServerChannel = std::move(serverChannel);
+ mClientChannel = std::move(clientChannel);
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {}
sp<InputDispatcher> mDispatcher;
- sp<InputChannel> mServerChannel, mClientChannel;
+ std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
};
@@ -161,8 +160,7 @@
virtual bool updateInfo() override {
mInfo.token = mServerChannel->getConnectionToken();
mInfo.name = "FakeWindowHandle";
- mInfo.layoutParamsFlags = 0;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.type = InputWindowInfo::Type::APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.frameLeft = mFrame.left;
mInfo.frameTop = mFrame.top;
@@ -178,7 +176,6 @@
mInfo.paused = false;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
mInfo.displayId = ADISPLAY_ID_DEFAULT;
return true;
@@ -202,13 +199,13 @@
const nsecs_t currentTime = now();
+ ui::Transform identityTransform;
MotionEvent event;
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- 1 /* xScale */, 1 /* yScale */,
- /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+ identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index a98f4b4..d29d8df 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -22,6 +22,7 @@
filegroup {
name: "libinputdispatcher_sources",
srcs: [
+ "AnrTracker.cpp",
"Connection.cpp",
"Entry.cpp",
"InjectionState.cpp",
@@ -67,4 +68,5 @@
export_header_lib_headers: [
"libinputdispatcher_headers",
],
+ logtags: ["EventLogTags.logtags"],
}
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
new file mode 100644
index 0000000..c3f611e
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 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 "AnrTracker.h"
+
+namespace android::inputdispatcher {
+
+template <typename T>
+static T max(const T& a, const T& b) {
+ return a < b ? b : a;
+}
+
+void AnrTracker::insert(nsecs_t timeoutTime, sp<IBinder> token) {
+ mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token)));
+}
+
+/**
+ * Erase a single entry only. If there are multiple duplicate entries
+ * (same time, same connection), then only remove one of them.
+ */
+void AnrTracker::erase(nsecs_t timeoutTime, const sp<IBinder>& token) {
+ auto pair = std::make_pair(timeoutTime, token);
+ auto it = mAnrTimeouts.find(pair);
+ if (it != mAnrTimeouts.end()) {
+ mAnrTimeouts.erase(it);
+ }
+}
+
+void AnrTracker::eraseToken(const sp<IBinder>& token) {
+ for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) {
+ if (it->second == token) {
+ it = mAnrTimeouts.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+bool AnrTracker::empty() const {
+ return mAnrTimeouts.empty();
+}
+
+// If empty() is false, return the time at which the next connection should cause an ANR
+// If empty() is true, return LONG_LONG_MAX
+nsecs_t AnrTracker::firstTimeout() const {
+ if (mAnrTimeouts.empty()) {
+ return std::numeric_limits<nsecs_t>::max();
+ }
+ return mAnrTimeouts.begin()->first;
+}
+
+const sp<IBinder>& AnrTracker::firstToken() const {
+ return mAnrTimeouts.begin()->second;
+}
+
+void AnrTracker::clear() {
+ mAnrTimeouts.clear();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
new file mode 100644
index 0000000..097dba5
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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 _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+
+#include <binder/IBinder.h>
+#include <utils/Timers.h>
+#include <set>
+
+namespace android::inputdispatcher {
+
+/**
+ * Keeps track of the times when each connection is going to ANR.
+ * Provides the ability to quickly find the connection that is going to cause ANR next.
+ */
+class AnrTracker {
+public:
+ void insert(nsecs_t timeoutTime, sp<IBinder> token);
+ void erase(nsecs_t timeoutTime, const sp<IBinder>& token);
+ void eraseToken(const sp<IBinder>& token);
+ void clear();
+
+ bool empty() const;
+ // If empty() is false, return the time at which the next connection should cause an ANR
+ // If empty() is true, return LONG_LONG_MAX
+ nsecs_t firstTimeout() const;
+ // Return the token of the next connection that should cause an ANR.
+ // Do not call this unless empty() is false, you will encounter undefined behaviour.
+ const sp<IBinder>& firstToken() const;
+
+private:
+ // Optimization: use a multiset to keep track of the event timeouts. When an event is sent
+ // to the InputConsumer, we add an entry to this structure. We look at the smallest value to
+ // determine if any of the connections is unresponsive, and to determine when we should wake
+ // next for the future ANR check.
+ // Using a multiset helps quickly look up the next timeout due.
+ //
+ // We must use a multi-set, because it is plausible (although highly unlikely) to have entries
+ // from the same connection and same timestamp, but different sequence numbers.
+ // We are not tracking sequence numbers, and just allow duplicates to exist.
+ std::multiset<std::pair<nsecs_t /*timeoutTime*/, sp<IBinder> /*connectionToken*/>> mAnrTimeouts;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index 188212b..cee9c39 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -20,14 +20,13 @@
namespace android::inputdispatcher {
-Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
+Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
const IdGenerator& idGenerator)
: status(STATUS_NORMAL),
inputChannel(inputChannel),
monitor(monitor),
inputPublisher(inputChannel),
- inputState(idGenerator),
- inputPublisherBlocked(false) {}
+ inputState(idGenerator) {}
Connection::~Connection() {}
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index bb3f2fe..c4262ad 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -42,14 +42,15 @@
};
Status status;
- sp<InputChannel> inputChannel; // never null
+ std::shared_ptr<InputChannel> inputChannel; // never null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
- // True if the socket is full and no further events can be published until
- // the application consumes some of the input.
- bool inputPublisherBlocked;
+ // True if this connection is responsive.
+ // If this connection is not responsive, avoid publishing more events to it until the
+ // application consumes some of the input.
+ bool responsive = true;
// Queue of events that need to be published to the connection.
std::deque<DispatchEntry*> outboundQueue;
@@ -58,7 +59,8 @@
// yet received a "finished" response from the application.
std::deque<DispatchEntry*> waitQueue;
- Connection(const sp<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator);
+ Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
+ const IdGenerator& idGenerator);
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 49630ad..26a1644 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -28,35 +28,6 @@
namespace android::inputdispatcher {
-static std::string motionActionToString(int32_t action) {
- // Convert MotionEvent action to string
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- return "DOWN";
- case AMOTION_EVENT_ACTION_MOVE:
- return "MOVE";
- case AMOTION_EVENT_ACTION_UP:
- return "UP";
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
- case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
- }
- return StringPrintf("%" PRId32, action);
-}
-
-static std::string keyActionToString(int32_t action) {
- // Convert KeyEvent action to string
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- return "DOWN";
- case AKEY_EVENT_ACTION_UP:
- return "UP";
- case AKEY_EVENT_ACTION_MULTIPLE:
- return "MULTIPLE";
- }
- return StringPrintf("%" PRId32, action);
-}
VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
@@ -99,6 +70,12 @@
releaseInjectionState();
}
+std::string EventEntry::getDescription() const {
+ std::string result;
+ appendDescription(result);
+ return result;
+}
+
void EventEntry::release() {
refCount -= 1;
if (refCount == 0) {
@@ -182,7 +159,7 @@
msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
"flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, displayId, keyActionToString(action).c_str(), flags,
+ deviceId, source, displayId, KeyEvent::actionToString(action), flags,
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
@@ -244,7 +221,7 @@
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, source, displayId, motionActionToString(action).c_str(),
+ deviceId, source, displayId, MotionEvent::actionToString(action),
actionButton, flags, metaState, buttonState,
motionClassificationToString(classification), edgeFlags, xPrecision,
yPrecision, xCursorPosition, yCursorPosition);
@@ -263,17 +240,13 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset,
- float yOffset, float globalScaleFactor, float windowXScale,
- float windowYScale)
+DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
+ float globalScaleFactor)
: seq(nextSeq()),
eventEntry(eventEntry),
targetFlags(targetFlags),
- xOffset(xOffset),
- yOffset(yOffset),
+ transform(transform),
globalScaleFactor(globalScaleFactor),
- windowXScale(windowXScale),
- windowYScale(windowYScale),
deliveryTime(0),
resolvedAction(0),
resolvedFlags(0) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ab481bd..e5b36f4 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -83,6 +83,8 @@
virtual void appendDescription(std::string& msg) const = 0;
+ std::string getDescription() const;
+
protected:
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
@@ -191,20 +193,21 @@
EventEntry* eventEntry; // the event to dispatch
int32_t targetFlags;
- float xOffset;
- float yOffset;
+ ui::Transform transform;
float globalScaleFactor;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
+ // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
+ // and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
+ // An ANR will be triggered if a response for this entry is not received by timeoutTime
+ nsecs_t timeoutTime;
// Set to the resolved ID, action and flags when the event is enqueued.
int32_t resolvedEventId;
int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset,
- float globalScaleFactor, float windowXScale, float windowYScale);
+ DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
+ float globalScaleFactor);
~DispatchEntry();
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
@@ -256,7 +259,7 @@
int32_t userActivityEventType;
uint32_t seq;
bool handled;
- sp<InputChannel> inputChannel;
+ std::shared_ptr<InputChannel> inputChannel;
sp<IBinder> oldToken;
sp<IBinder> newToken;
};
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
new file mode 100644
index 0000000..9c0f80e
--- /dev/null
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -0,0 +1,42 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# See system/core/logcat/event.logtags for the master copy of the tags.
+
+# 62000 - 62199 reserved for inputflinger
+
+62000 input_interaction (windows|4)
+62001 input_focus (window|3)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index baf2f2b..5a4143a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -45,28 +45,30 @@
#include "InputDispatcher.h"
-#include "Connection.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <statslog.h>
-#include <stddef.h>
-#include <time.h>
-#include <unistd.h>
-#include <queue>
-#include <sstream>
-
#include <android-base/chrono_utils.h>
#include <android-base/stringprintf.h>
#include <binder/Binder.h>
#include <input/InputDevice.h>
+#include <input/InputWindow.h>
#include <log/log.h>
+#include <log/log_event_list.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <powermanager/PowerManager.h>
+#include <statslog.h>
+#include <unistd.h>
#include <utils/Trace.h>
+#include <cerrno>
+#include <cinttypes>
+#include <climits>
+#include <cstddef>
+#include <ctime>
+#include <queue>
+#include <sstream>
+
+#include "Connection.h"
+
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
@@ -78,7 +80,7 @@
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
-constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
// Amount of time to allow for all pending events to be processed when an app switch
// key is on the way. This is used to preempt input dispatch and drop input events
@@ -89,21 +91,24 @@
// before considering it stale and dropping it.
constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
-// Amount of time to allow touch events to be streamed out to a connection before requiring
-// that the first event be finished. This value extends the ANR timeout by the specified
-// amount. For example, if streaming is allowed to get ahead by one second relative to the
-// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
-constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
// Log a warning when an event takes longer than this to process, even if an ANR does not occur.
constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
// Log a warning when an interception call takes longer than this to process.
constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms;
+// Additional key latency in case a connection is still processing some motion events.
+// This will help with the case when a user touched a button that opens a new window,
+// and gives us the chance to dispatch the key to this new window.
+constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms;
+
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
+// Event log tags. See EventLogTags.logtags for reference
+constexpr int LOGTAG_INPUT_INTERACTION = 62000;
+constexpr int LOGTAG_INPUT_FOCUS = 62001;
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
@@ -160,6 +165,10 @@
}
}
+static int64_t millis(std::chrono::nanoseconds t) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
+}
+
static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
const PointerProperties* pointerProperties) {
if (!isValidMotionAction(action, actionButton, pointerCount)) {
@@ -262,12 +271,11 @@
static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
EventEntry* eventEntry,
int32_t inputTargetFlags) {
- if (inputTarget.useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+ if (inputTarget.useDefaultPointerTransform()) {
+ const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, pointerInfo.xOffset,
- pointerInfo.yOffset, inputTarget.globalScaleFactor,
- pointerInfo.windowXScale, pointerInfo.windowYScale);
+ inputTargetFlags, transform,
+ inputTarget.globalScaleFactor);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -277,28 +285,24 @@
// Use the first pointer information to normalize all other pointers. This could be any pointer
// as long as all other pointers are normalized to the same value and the final DispatchEntry
- // uses the offset and scale for the normalized pointer.
- const PointerInfo& firstPointerInfo =
- inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+ // uses the transform for the normalized pointer.
+ const ui::Transform& firstPointerTransform =
+ inputTarget.pointerTransforms[inputTarget.pointerIds.firstMarkedBit()];
+ ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
// Iterate through all pointers in the event to normalize against the first.
for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
uint32_t pointerId = uint32_t(pointerProperties.id);
- const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
-
- // The scale factor is the ratio of the current pointers scale to the normalized scale.
- float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
- float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+ const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
- // First apply the current pointers offset to set the window at 0,0
- pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
- // Next scale the coordinates.
- pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
- // Lastly, offset the coordinates so they're in the normalized pointer's frame.
- pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
- -firstPointerInfo.yOffset);
+ // First, apply the current pointer's transform to update the coordinates into
+ // window space.
+ pointerCoords[pointerIndex].transform(currTransform);
+ // Next, apply the inverse transform of the normalized coordinates so the
+ // current coordinates are transformed into the normalized coordinate space.
+ pointerCoords[pointerIndex].transform(inverseFirstTransform);
}
MotionEntry* combinedMotionEntry =
@@ -320,14 +324,24 @@
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
- inputTargetFlags, firstPointerInfo.xOffset,
- firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
- firstPointerInfo.windowXScale,
- firstPointerInfo.windowYScale);
+ inputTargetFlags, firstPointerTransform,
+ inputTarget.globalScaleFactor);
combinedMotionEntry->release();
return dispatchEntry;
}
+static void addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+ float yOffset = 0) {
+ if (monitors.empty()) {
+ return;
+ }
+ outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+ for (const Monitor& monitor : monitors) {
+ outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+ }
+}
+
static std::array<uint8_t, 128> getRandomKey() {
std::array<uint8_t, 128> key;
if (RAND_bytes(key.data(), key.size()) != 1) {
@@ -392,8 +406,7 @@
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
- mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
- mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+ mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -413,7 +426,7 @@
while (!mConnectionsByFd.empty()) {
sp<Connection> connection = mConnectionsByFd.begin()->second;
- unregisterInputChannel(connection->inputChannel);
+ unregisterInputChannel(*connection->inputChannel);
}
}
@@ -453,6 +466,11 @@
nextWakeupTime = LONG_LONG_MIN;
}
+ // If we are still waiting for ack on some events,
+ // we might have to wake up earlier to check if an app is anr'ing.
+ const nsecs_t nextAnrCheck = processAnrsLocked();
+ nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
+
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
@@ -466,6 +484,55 @@
mLooper->pollOnce(timeoutMillis);
}
+/**
+ * Check if any of the connections' wait queues have events that are too old.
+ * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
+ * Return the time at which we should wake up next.
+ */
+nsecs_t InputDispatcher::processAnrsLocked() {
+ const nsecs_t currentTime = now();
+ nsecs_t nextAnrCheck = LONG_LONG_MAX;
+ // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
+ if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
+ if (currentTime >= *mNoFocusedWindowTimeoutTime) {
+ onAnrLocked(mAwaitedFocusedApplication);
+ mAwaitedFocusedApplication.clear();
+ return LONG_LONG_MIN;
+ } else {
+ // Keep waiting
+ const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
+ ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+ nextAnrCheck = *mNoFocusedWindowTimeoutTime;
+ }
+ }
+
+ // Check if any connection ANRs are due
+ nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
+ if (currentTime < nextAnrCheck) { // most likely scenario
+ return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck
+ }
+
+ // If we reached here, we have an unresponsive connection.
+ sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+ if (connection == nullptr) {
+ ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
+ return nextAnrCheck;
+ }
+ connection->responsive = false;
+ // Stop waking up for this unresponsive connection
+ mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+ onAnrLocked(connection);
+ return LONG_LONG_MIN;
+}
+
+nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+ sp<InputWindowHandle> window = getWindowHandleLocked(token);
+ if (window != nullptr) {
+ return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count();
+ }
+ return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count();
+}
+
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
@@ -529,9 +596,6 @@
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(*mPendingEvent);
}
-
- // Get ready to dispatch the event.
- resetANRTimeoutsLocked();
}
// Now we have an event to dispatch.
@@ -620,6 +684,66 @@
}
}
+/**
+ * Return true if the events preceding this incoming motion event should be dropped
+ * Return false otherwise (the default behaviour)
+ */
+bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
+ const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+ (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
+
+ // Optimize case where the current application is unresponsive and the user
+ // decides to touch a window in a different application.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the touch into the other window.
+ if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
+ int32_t displayId = motionEntry.displayId;
+ int32_t x = static_cast<int32_t>(
+ motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = static_cast<int32_t>(
+ motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ sp<InputWindowHandle> touchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, nullptr);
+ if (touchedWindowHandle != nullptr &&
+ touchedWindowHandle->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
+ // User touched a different application than the one we are waiting on.
+ ALOGI("Pruning input queue because user touched a different application while waiting "
+ "for %s",
+ mAwaitedFocusedApplication->getName().c_str());
+ return true;
+ }
+
+ // Alternatively, maybe there's a gesture monitor that could handle this event
+ std::vector<TouchedMonitor> gestureMonitors =
+ findTouchedGestureMonitorsLocked(displayId, {});
+ for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+ sp<Connection> connection =
+ getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+ if (connection != nullptr && connection->responsive) {
+ // This monitor could take more input. Drop all events preceding this
+ // event, so that gesture monitor could get a chance to receive the stream
+ ALOGW("Pruning the input queue because %s is unresponsive, but we have a "
+ "responsive gesture monitor that may handle the event",
+ mAwaitedFocusedApplication->getName().c_str());
+ return true;
+ }
+ }
+ }
+
+ // Prevent getting stuck: if we have a pending key event, and some motion events that have not
+ // yet been processed by some connections, the dispatcher will wait for these motion
+ // events to be processed before dispatching the key event. This is because these motion events
+ // may cause a new window to be launched, which the user might expect to receive focus.
+ // To prevent waiting forever for such events, just send the key to the currently focused window
+ if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) {
+ ALOGD("Received a new pointer down event, stop waiting for events to process and "
+ "just send the pending key event to the focused window.");
+ mKeyIsWaitingForEventsTimeout = now();
+ }
+ return false;
+}
+
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.empty();
mInboundQueue.push_back(entry);
@@ -649,36 +773,18 @@
}
case EventEntry::Type::MOTION: {
- // Optimize case where the current application is unresponsive and the user
- // decides to touch a window in a different application.
- // If the application takes too long to catch up then we drop all events preceding
- // the touch into the other window.
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN &&
- (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
- mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
- mInputTargetWaitApplicationToken != nullptr) {
- int32_t displayId = motionEntry->displayId;
- int32_t x =
- int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y =
- int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> touchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y);
- if (touchedWindowHandle != nullptr &&
- touchedWindowHandle->getApplicationToken() !=
- mInputTargetWaitApplicationToken) {
- // User touched a different application than the one we are waiting on.
- // Flag the event, and start pruning the input queue.
- mNextUnblockedEvent = motionEntry;
- needWake = true;
- }
+ if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
+ mNextUnblockedEvent = entry;
+ needWake = true;
}
break;
}
- case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::FOCUS: {
+ LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
+ break;
+ }
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
// nothing to do
break;
}
@@ -697,29 +803,33 @@
}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, bool addOutsideTargets,
+ int32_t y, TouchState* touchState,
+ bool addOutsideTargets,
bool addPortalWindows) {
+ if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
+ LOG_ALWAYS_FATAL(
+ "Must provide a valid touch state if adding portal windows or outside targets");
+ }
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
- int32_t flags = windowInfo->layoutParamsFlags;
+ auto flags = windowInfo->flags;
if (windowInfo->visible) {
- if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags &
- (InputWindowInfo::FLAG_NOT_FOCUSABLE |
- InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+ bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) &&
+ !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
int32_t portalToDisplayId = windowInfo->portalToDisplayId;
if (portalToDisplayId != ADISPLAY_ID_NONE &&
portalToDisplayId != displayId) {
if (addPortalWindows) {
// For the monitoring channels of the display.
- mTempTouchState.addPortalWindow(windowHandle);
+ touchState->addPortalWindow(windowHandle);
}
- return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+ return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
addOutsideTargets, addPortalWindows);
}
// Found window.
@@ -727,10 +837,10 @@
}
}
- if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
- BitSet32(0));
+ if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+ touchState->addOrUpdateWindow(windowHandle,
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+ BitSet32(0));
}
}
}
@@ -739,7 +849,7 @@
}
std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
std::vector<TouchedMonitor> touchedMonitors;
std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
@@ -753,18 +863,6 @@
return touchedMonitors;
}
-void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors,
- float xOffset, float yOffset) {
- if (monitors.empty()) {
- return;
- }
- outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
- for (const Monitor& monitor : monitors) {
- outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
- }
-}
-
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -888,7 +986,6 @@
void InputDispatcher::releasePendingEventLocked() {
if (mPendingEvent) {
- resetANRTimeoutsLocked();
releaseInboundEventLocked(mPendingEvent);
mPendingEvent = nullptr;
}
@@ -980,13 +1077,28 @@
}
void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+ if (mPendingEvent != nullptr) {
+ // Move the pending event to the front of the queue. This will give the chance
+ // for the pending event to get dispatched to the newly focused window
+ mInboundQueue.push_front(mPendingEvent);
+ mPendingEvent = nullptr;
+ }
+
FocusEntry* focusEntry =
new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
- enqueueInboundEventLocked(focusEntry);
+
+ // This event should go to the front of the queue, but behind all other focus events
+ // Find the last focus event, and insert right after it
+ std::deque<EventEntry*>::reverse_iterator it =
+ std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
+ [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; });
+
+ // Maintain the order of focus events. Insert the entry after all other focus events.
+ mInboundQueue.insert(it.base(), focusEntry);
}
void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
- sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
@@ -994,7 +1106,9 @@
target.inputChannel = channel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
entry->dispatchInProgress = true;
-
+ std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
+ channel->getName();
+ android_log_event_list(LOGTAG_INPUT_FOCUS) << message << LOG_ID_EVENTS;
dispatchEventLocked(currentTime, entry, {target});
}
@@ -1152,14 +1266,16 @@
}
setInjectionResult(entry, injectionResult);
+ if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
+ ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
+ return true;
+ }
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
- CancelationOptions::Mode mode(isPointerEvent
- ? CancelationOptions::CANCEL_POINTER_EVENTS
- : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
- CancelationOptions options(mode, "input event injection failed");
- synthesizeCancelationEventsForMonitorsLocked(options);
- }
+ CancelationOptions::Mode mode(isPointerEvent
+ ? CancelationOptions::CANCEL_POINTER_EVENTS
+ : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+ CancelationOptions options(mode, "input event injection failed");
+ synthesizeCancelationEventsForMonitorsLocked(options);
return true;
}
@@ -1167,9 +1283,10 @@
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
if (isPointerEvent) {
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
- if (stateIndex >= 0) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+ std::unordered_map<int32_t, TouchState>::iterator it =
+ mTouchStatesByDisplay.find(entry->displayId);
+ if (it != mTouchStatesByDisplay.end()) {
+ const TouchState& state = it->second;
if (!state.portalWindows.empty()) {
// The event has gone through these portal windows, so we add monitoring targets of
// the corresponding displays as well.
@@ -1229,6 +1346,8 @@
ALOGD("dispatchEventToCurrentInputTargets");
#endif
+ updateInteractionTokensLocked(*eventEntry, inputTargets);
+
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(*eventEntry);
@@ -1248,118 +1367,29 @@
}
}
-int32_t InputDispatcher::handleTargetsNotReadyLocked(
- nsecs_t currentTime, const EventEntry& entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
- if (applicationHandle == nullptr && windowHandle == nullptr) {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
- if (DEBUG_FOCUS) {
- ALOGD("Waiting for system to become ready for input. Reason: %s", reason);
- }
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationToken.clear();
- }
- } else {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
- if (DEBUG_FOCUS) {
- ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
- }
- nsecs_t timeout;
- if (windowHandle != nullptr) {
- timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else if (applicationHandle != nullptr) {
- timeout =
- applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else {
- timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
- }
-
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = currentTime + timeout;
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationToken.clear();
-
- if (windowHandle != nullptr) {
- mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
- }
- if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
- mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
- }
- }
- }
-
- if (mInputTargetWaitTimeoutExpired) {
- return INPUT_EVENT_INJECTION_TIMED_OUT;
- }
-
- if (currentTime >= mInputTargetWaitTimeoutTime) {
- onANRLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
- mInputTargetWaitStartTime, reason);
-
- // Force poll loop to wake up immediately on next iteration once we get the
- // ANR response back from the policy.
- *nextWakeupTime = LONG_LONG_MIN;
- return INPUT_EVENT_INJECTION_PENDING;
- } else {
- // Force poll loop to wake up when timeout is due.
- if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
- *nextWakeupTime = mInputTargetWaitTimeoutTime;
- }
- return INPUT_EVENT_INJECTION_PENDING;
+void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+ // We will not be breaking any connections here, even if the policy wants us to abort dispatch.
+ // If the policy decides to close the app, we will get a channel removal event via
+ // unregisterInputChannel, and will clean up the connection that way. We are already not
+ // sending new pointers to the connection when it blocked, but focused events will continue to
+ // pile up.
+ ALOGW("Canceling events for %s because it is unresponsive",
+ connection->inputChannel->getName().c_str());
+ if (connection->status == Connection::STATUS_NORMAL) {
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+ "application not responding");
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
-void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) {
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
- state.removeWindowByToken(token);
- }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
- nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) {
- if (newTimeout > 0) {
- // Extend the timeout.
- mInputTargetWaitTimeoutTime = now() + newTimeout;
- } else {
- // Give up.
- mInputTargetWaitTimeoutExpired = true;
-
- // Input state will not be realistic. Mark it out of sync.
- sp<Connection> connection = getConnectionLocked(inputConnectionToken);
- if (connection != nullptr) {
- removeWindowByTokenLocked(inputConnectionToken);
-
- if (connection->status == Connection::STATUS_NORMAL) {
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
- "application not responding");
- synthesizeCancelationEventsForConnectionLocked(connection, options);
- }
- }
- }
-}
-
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) {
- if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
- return currentTime - mInputTargetWaitStartTime;
- }
- return 0;
-}
-
-void InputDispatcher::resetANRTimeoutsLocked() {
+void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
if (DEBUG_FOCUS) {
ALOGD("Resetting ANR timeouts.");
}
// Reset input target wait timeout.
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
- mInputTargetWaitApplicationToken.clear();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
+ mAwaitedFocusedApplication.clear();
}
/**
@@ -1390,11 +1420,40 @@
return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
}
+bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
+ const char* focusedWindowName) {
+ if (mAnrTracker.empty()) {
+ // already processed all events that we waited for
+ mKeyIsWaitingForEventsTimeout = std::nullopt;
+ return false;
+ }
+
+ if (!mKeyIsWaitingForEventsTimeout.has_value()) {
+ // Start the timer
+ ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
+ "focus to change",
+ focusedWindowName);
+ mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
+ return true;
+ }
+
+ // We still have pending events, and already started the timer
+ if (currentTime < *mKeyIsWaitingForEventsTimeout) {
+ return true; // Still waiting
+ }
+
+ // Waited too long, and some connection still hasn't processed all motions
+ // Just send the key to the focused window
+ ALOGW("Dispatching key to %s even though there are other unprocessed events",
+ focusedWindowName);
+ mKeyIsWaitingForEventsTimeout = std::nullopt;
+ return false;
+}
+
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime) {
- int32_t injectionResult;
std::string reason;
int32_t displayId = getTargetDisplayId(entry);
@@ -1405,56 +1464,105 @@
// If there is no currently focused window and no focused application
// then drop the event.
- if (focusedWindowHandle == nullptr) {
- if (focusedApplicationHandle != nullptr) {
- injectionResult =
- handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
- nullptr, nextWakeupTime,
- "Waiting because no window has focus but there is "
- "a focused application that may eventually add a "
- "window when it finishes starting up.");
- goto Unresponsive;
- }
-
- ALOGI("Dropping event because there is no focused window or focused application in display "
- "%" PRId32 ".",
- displayId);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
+ if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
+ ALOGI("Dropping %s event because there is no focused window or focused application in "
+ "display %" PRId32 ".",
+ EventEntry::typeToString(entry.type), displayId);
+ return INPUT_EVENT_INJECTION_FAILED;
}
+ // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
+ // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
+ // start interacting with another application via touch (app switch). This code can be removed
+ // if the "no focused window ANR" is moved to the policy. Input doesn't know whether
+ // an app is expected to have a focused window.
+ if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
+ if (!mNoFocusedWindowTimeoutTime.has_value()) {
+ // We just discovered that there's no focused window. Start the ANR timer
+ std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT);
+ mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
+ mAwaitedFocusedApplication = focusedApplicationHandle;
+ ALOGW("Waiting because no window has focus but %s may eventually add a "
+ "window when it finishes starting up. Will wait for %" PRId64 "ms",
+ mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
+ *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
+ return INPUT_EVENT_INJECTION_PENDING;
+ } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
+ // Already raised ANR. Drop the event
+ ALOGE("Dropping %s event because there is no focused window",
+ EventEntry::typeToString(entry.type));
+ return INPUT_EVENT_INJECTION_FAILED;
+ } else {
+ // Still waiting for the focused window
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+ }
+
+ // we have a valid, non-null focused window
+ resetNoFocusedWindowTimeoutLocked();
+
// Check permissions.
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- goto Failed;
+ return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
}
- // Check whether the window is ready for more input.
- reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
- if (!reason.empty()) {
- injectionResult =
- handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
- focusedWindowHandle, nextWakeupTime, reason.c_str());
- goto Unresponsive;
+ if (focusedWindowHandle->getInfo()->paused) {
+ ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+
+ // If the event is a key event, then we must wait for all previous events to
+ // complete before delivering it because previous events may have the
+ // side-effect of transferring focus to a different window and we want to
+ // ensure that the following keys are sent to the new window.
+ //
+ // Suppose the user touches a button in a window then immediately presses "A".
+ // If the button causes a pop-up window to appear then we want to ensure that
+ // the "A" key is delivered to the new pop-up window. This is because users
+ // often anticipate pending UI changes when typing on a keyboard.
+ // To obtain this behavior, we must serialize key events with respect to all
+ // prior input events.
+ if (entry.type == EventEntry::Type::KEY) {
+ if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
+ *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
}
// Success! Output targets.
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
addWindowTargetLocked(focusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
BitSet32(0), inputTargets);
// Done.
-Failed:
-Unresponsive:
- nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
- if (DEBUG_FOCUS) {
- ALOGD("findFocusedWindow finished: injectionResult=%d, "
- "timeSpentWaitingForApplication=%0.1fms",
- injectionResult, timeSpentWaitingForApplication / 1000000.0);
- }
- return injectionResult;
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+/**
+ * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
+ * that are currently unresponsive.
+ */
+std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
+ const std::vector<TouchedMonitor>& monitors) const {
+ std::vector<TouchedMonitor> responsiveMonitors;
+ std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
+ [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
+ sp<Connection> connection = getConnectionLocked(
+ monitor.monitor.inputChannel->getConnectionToken());
+ if (connection == nullptr) {
+ ALOGE("Could not find connection for monitor %s",
+ monitor.monitor.inputChannel->getName().c_str());
+ return false;
+ }
+ if (!connection->responsive) {
+ ALOGW("Unresponsive monitor %s will not get the new gesture",
+ connection->inputChannel->getName().c_str());
+ return false;
+ }
+ return true;
+ });
+ return responsiveMonitors;
}
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
@@ -1478,22 +1586,25 @@
// Update the touch state as needed based on the properties of the touch event.
int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- sp<InputWindowHandle> newHoverWindowHandle;
+ sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+ sp<InputWindowHandle> newTouchedWindowHandle;
- // Copy current touch state into mTempTouchState.
- // This state is always reset at the end of this function, so if we don't find state
- // for the specified display then our initial state will be empty.
+ // Copy current touch state into tempTouchState.
+ // This state will be used to update mTouchStatesByDisplay at the end of this function.
+ // If no state for the specified display exists, then our initial state will be empty.
const TouchState* oldState = nullptr;
- ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (oldStateIndex >= 0) {
- oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
- mTempTouchState.copyFrom(*oldState);
+ TouchState tempTouchState;
+ std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (oldStateIt != mTouchStatesByDisplay.end()) {
+ oldState = &(oldStateIt->second);
+ tempTouchState.copyFrom(*oldState);
}
- bool isSplit = mTempTouchState.split;
- bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
- (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source ||
- mTempTouchState.displayId != displayId);
+ bool isSplit = tempTouchState.split;
+ bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
+ (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
+ tempTouchState.displayId != displayId);
bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
@@ -1503,30 +1614,26 @@
bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because a pointer for a different device is already down "
- "in display %" PRId32,
- displayId);
- }
+ if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
+ ALOGI("Dropping event because a pointer for a different device is already down "
+ "in display %" PRId32,
+ displayId);
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
}
- mTempTouchState.reset();
- mTempTouchState.down = down;
- mTempTouchState.deviceId = entry.deviceId;
- mTempTouchState.source = entry.source;
- mTempTouchState.displayId = displayId;
+ tempTouchState.reset();
+ tempTouchState.down = down;
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ tempTouchState.displayId = displayId;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
- if (DEBUG_FOCUS) {
- ALOGI("Dropping move event because a pointer for a different device is already active "
- "in display %" PRId32,
- displayId);
- }
+ ALOGI("Dropping move event because a pointer for a different device is already active "
+ "in display %" PRId32,
+ displayId);
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
switchedDevice = false;
@@ -1549,12 +1656,12 @@
y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
- true /*addPortalWindows*/);
+ newTouchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+ isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
std::vector<TouchedMonitor> newGestureMonitors = isDown
- ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+ ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
: std::vector<TouchedMonitor>{};
// Figure out whether splitting will be allowed for this window.
@@ -1571,9 +1678,32 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
// Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
+ newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
+ if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
+ ALOGI("Not sending touch event to %s because it is paused",
+ newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle = nullptr;
+ }
+
+ if (newTouchedWindowHandle != nullptr) {
+ sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken());
+ if (connection == nullptr) {
+ ALOGI("Could not find connection for %s",
+ newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle = nullptr;
+ } else if (!connection->responsive) {
+ // don't send the new touch to an unresponsive window
+ ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64,
+ newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+ newTouchedWindowHandle = nullptr;
+ }
+ }
+
+ // Also don't send the new touch event to unresponsive gesture monitors
+ newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
+
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
"(%d, %d) in display %" PRId32 ".",
@@ -1595,10 +1725,10 @@
}
// Update hover state.
- if (isHoverAction) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ newHoverWindowHandle = nullptr;
+ } else if (isHoverAction) {
newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
}
// Update the temporary touch state.
@@ -1607,15 +1737,15 @@
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
pointerIds.markBit(pointerId);
}
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
- mTempTouchState.addGestureMonitors(newGestureMonitors);
+ tempTouchState.addGestureMonitors(newGestureMonitors);
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!mTempTouchState.down) {
+ if (!tempTouchState.down) {
if (DEBUG_FOCUS) {
ALOGD("Dropping event because the pointer is not down or we previously "
"dropped the pointer down event in display %" PRId32,
@@ -1627,14 +1757,13 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
- mTempTouchState.isSlippery()) {
+ tempTouchState.isSlippery()) {
int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> oldTouchedWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
- sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y);
+ tempTouchState.getFirstForegroundWindowHandle();
+ newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -1643,9 +1772,9 @@
newTouchedWindowHandle->getName().c_str(), displayId);
}
// Make a slippery exit from the old window.
- mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+ BitSet32(0));
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -1665,32 +1794,37 @@
if (isSplit) {
pointerIds.markBit(entry.pointerProperties[0].id);
}
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
}
}
if (newHoverWindowHandle != mLastHoverWindowHandle) {
- // Let the previous window know that the hover sequence is over.
- if (mLastHoverWindowHandle != nullptr) {
+ // Let the previous window know that the hover sequence is over, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (mLastHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ mLastHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
#endif
- mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
- // Let the new window know that the hover sequence is starting.
- if (newHoverWindowHandle != nullptr) {
+ // Let the new window know that the hover sequence is starting, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (newHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ newHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
#endif
- mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+ BitSet32(0));
}
}
@@ -1698,7 +1832,7 @@
// is at least one touched foreground window.
{
bool haveForegroundWindow = false;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
@@ -1708,13 +1842,11 @@
}
}
}
- bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+ bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
if (!haveForegroundWindow && !hasGestureMonitor) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because there is no touched foreground window in display "
- "%" PRId32 " or gesture monitor to receive it.",
- displayId);
- }
+ ALOGI("Dropping event because there is no touched foreground window in display "
+ "%" PRId32 " or gesture monitor to receive it.",
+ displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1727,38 +1859,22 @@
// set the policy flag that we will not reveal coordinate information to this window.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle) {
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
- mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(inputWindowHandle,
+ InputTarget::FLAG_ZERO_COORDS,
+ BitSet32(0));
}
}
}
}
}
- // Ensure all touched foreground windows are ready for new input.
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- // Check whether the window is ready for more input.
- std::string reason =
- checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
- entry, "touched");
- if (!reason.empty()) {
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr,
- touchedWindow.windowHandle,
- nextWakeupTime, reason.c_str());
- goto Unresponsive;
- }
- }
- }
-
// If this is the first pointer going down and the touched window has a wallpaper
// then also add the touched wallpaper windows so they are locked in for the duration
// of the touch gesture.
@@ -1767,15 +1883,15 @@
// to View.onGenericMotionEvent to enable wallpapers to handle these events.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
const std::vector<sp<InputWindowHandle>> windowHandles =
getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* info = windowHandle->getInfo();
if (info->displayId == displayId &&
- windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
- mTempTouchState
+ windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) {
+ tempTouchState
.addOrUpdateWindow(windowHandle,
InputTarget::FLAG_WINDOW_IS_OBSCURED |
InputTarget::
@@ -1790,19 +1906,19 @@
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
- for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+ for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
touchedMonitor.yOffset, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
- mTempTouchState.filterNonAsIsTouchWindows();
+ tempTouchState.filterNonAsIsTouchWindows();
Failed:
// Check injection permission once and for all.
@@ -1814,98 +1930,81 @@
}
}
+ if (injectionPermission != INJECTION_PERMISSION_GRANTED) {
+ return injectionResult;
+ }
+
// Update final pieces of touch state if the injector had permission.
- if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
- if (!wrongDevice) {
- if (switchedDevice) {
+ if (!wrongDevice) {
+ if (switchedDevice) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Conflicting pointer actions: Switched to a different device.");
+ }
+ *outConflictingPointerActions = true;
+ }
+
+ if (isHoverAction) {
+ // Started hovering, therefore no longer down.
+ if (oldState && oldState->down) {
if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Switched to a different device.");
+ ALOGD("Conflicting pointer actions: Hover received while pointer was "
+ "down.");
}
*outConflictingPointerActions = true;
}
+ tempTouchState.reset();
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ tempTouchState.displayId = displayId;
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+ maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // All pointers up or canceled.
+ tempTouchState.reset();
+ } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ // First pointer went down.
+ if (oldState && oldState->down) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Conflicting pointer actions: Down received while already down.");
+ }
+ *outConflictingPointerActions = true;
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ // One pointer went up.
+ if (isSplit) {
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
- if (isHoverAction) {
- // Started hovering, therefore no longer down.
- if (oldState && oldState->down) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Hover received while pointer was "
- "down.");
- }
- *outConflictingPointerActions = true;
- }
- mTempTouchState.reset();
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mTempTouchState.deviceId = entry.deviceId;
- mTempTouchState.source = entry.source;
- mTempTouchState.displayId = displayId;
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
- maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
- // All pointers up or canceled.
- mTempTouchState.reset();
- } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- // First pointer went down.
- if (oldState && oldState->down) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Down received while already down.");
- }
- *outConflictingPointerActions = true;
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- // One pointer went up.
- if (isSplit) {
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
- for (size_t i = 0; i < mTempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = mTempTouchState.windows[i];
- if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
- touchedWindow.pointerIds.clearBit(pointerId);
- if (touchedWindow.pointerIds.isEmpty()) {
- mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i);
- continue;
- }
+ for (size_t i = 0; i < tempTouchState.windows.size();) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+ touchedWindow.pointerIds.clearBit(pointerId);
+ if (touchedWindow.pointerIds.isEmpty()) {
+ tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+ continue;
}
- i += 1;
}
+ i += 1;
}
}
+ }
- // Save changes unless the action was scroll in which case the temporary touch
- // state was only valid for this one action.
- if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
- if (mTempTouchState.displayId >= 0) {
- if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
- } else {
- mTouchStatesByDisplay.add(displayId, mTempTouchState);
- }
- } else if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
- }
+ // Save changes unless the action was scroll in which case the temporary touch
+ // state was only valid for this one action.
+ if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
+ if (tempTouchState.displayId >= 0) {
+ mTouchStatesByDisplay[displayId] = tempTouchState;
+ } else {
+ mTouchStatesByDisplay.erase(displayId);
}
+ }
- // Update hover state.
- mLastHoverWindowHandle = newHoverWindowHandle;
- }
- } else {
- if (DEBUG_FOCUS) {
- ALOGD("Not updating touch focus because injection was denied.");
- }
+ // Update hover state.
+ mLastHoverWindowHandle = newHoverWindowHandle;
}
-Unresponsive:
- // Reset temporary touch state to ensure we release unnecessary references to input channels.
- mTempTouchState.reset();
-
- nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
- if (DEBUG_FOCUS) {
- ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
- "timeSpentWaitingForApplication=%0.1fms",
- injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
- }
return injectionResult;
}
@@ -1923,7 +2022,8 @@
if (it == inputTargets.end()) {
InputTarget inputTarget;
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ std::shared_ptr<InputChannel> inputChannel =
+ getInputChannelLocked(windowHandle->getToken());
if (inputChannel == nullptr) {
ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
return;
@@ -1938,8 +2038,7 @@
ALOG_ASSERT(it->flags == targetFlags);
ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
- it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ it->addPointers(pointerIds, windowInfo->transform);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1962,7 +2061,9 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
+ ui::Transform t;
+ t.set(xOffset, yOffset);
+ target.setDefaultPointerTransform(t);
inputTargets.push_back(target);
}
@@ -2001,16 +2102,11 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
- } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) {
- // In general, if ownerPid is the same we don't want to generate occlusion
- // events. This line is now necessary since we are including all Surfaces
- // in occlusion calculation, so if we didn't check PID like this SurfaceView
- // would occlude their parents. On the other hand before we started including
- // all surfaces in occlusion calculation and had this line, we would count
- // windows with an input channel from the same PID as occluding, and so we
- // preserve this behavior with the getToken() == null check.
+ } else if (info->ownerPid == otherInfo->ownerPid) {
+ // If ownerPid is the same we don't generate occlusion events as there
+ // is no in-process security boundary.
return false;
- } else if (otherInfo->isTrustedOverlay()) {
+ } else if (otherInfo->trustedOverlay) {
return false;
} else if (otherInfo->displayId != info->displayId) {
return false;
@@ -2027,7 +2123,7 @@
break; // All future windows are below us. Exit early.
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) &&
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->frameContainsPoint(x, y)) {
return true;
}
@@ -2043,7 +2139,6 @@
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
-
const InputWindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->overlaps(windowInfo)) {
@@ -2053,106 +2148,17 @@
return false;
}
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
- nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
- const EventEntry& eventEntry, const char* targetType) {
- // If the window is paused then keep waiting.
- if (windowHandle->getInfo()->paused) {
- return StringPrintf("Waiting because the %s window is paused.", targetType);
- }
-
- // If the window's connection is not registered then keep waiting.
- sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
- if (connection == nullptr) {
- return StringPrintf("Waiting because the %s window's input channel is not "
- "registered with the input dispatcher. The window may be in the "
- "process of being removed.",
- targetType);
- }
-
- // If the connection is dead then keep waiting.
- if (connection->status != Connection::STATUS_NORMAL) {
- return StringPrintf("Waiting because the %s window's input connection is %s."
- "The window may be in the process of being removed.",
- targetType, connection->getStatusLabel());
- }
-
- // If the connection is backed up then keep waiting.
- if (connection->inputPublisherBlocked) {
- return StringPrintf("Waiting because the %s window's input channel is full. "
- "Outbound queue length: %zu. Wait queue length: %zu.",
- targetType, connection->outboundQueue.size(),
- connection->waitQueue.size());
- }
-
- // Ensure that the dispatch queues aren't too far backed up for this event.
- if (eventEntry.type == EventEntry::Type::KEY) {
- // If the event is a key event, then we must wait for all previous events to
- // complete before delivering it because previous events may have the
- // side-effect of transferring focus to a different window and we want to
- // ensure that the following keys are sent to the new window.
- //
- // Suppose the user touches a button in a window then immediately presses "A".
- // If the button causes a pop-up window to appear then we want to ensure that
- // the "A" key is delivered to the new pop-up window. This is because users
- // often anticipate pending UI changes when typing on a keyboard.
- // To obtain this behavior, we must serialize key events with respect to all
- // prior input events.
- if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) {
- return StringPrintf("Waiting to send key event because the %s window has not "
- "finished processing all of the input events that were previously "
- "delivered to it. Outbound queue length: %zu. Wait queue length: "
- "%zu.",
- targetType, connection->outboundQueue.size(),
- connection->waitQueue.size());
- }
- } else {
- // Touch events can always be sent to a window immediately because the user intended
- // to touch whatever was visible at the time. Even if focus changes or a new
- // window appears moments later, the touch event was meant to be delivered to
- // whatever window happened to be on screen at the time.
- //
- // Generic motion events, such as trackball or joystick events are a little trickier.
- // Like key events, generic motion events are delivered to the focused window.
- // Unlike key events, generic motion events don't tend to transfer focus to other
- // windows and it is not important for them to be serialized. So we prefer to deliver
- // generic motion events as soon as possible to improve efficiency and reduce lag
- // through batching.
- //
- // The one case where we pause input event delivery is when the wait queue is piling
- // up with lots of events because the application is not responding.
- // This condition ensures that ANRs are detected reliably.
- if (!connection->waitQueue.empty() &&
- currentTime >=
- connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
- return StringPrintf("Waiting to send non-key event because the %s window has not "
- "finished processing certain input events that were delivered to "
- "it over "
- "%0.1fms ago. Wait queue length: %zu. Wait queue head age: "
- "%0.1fms.",
- targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
- connection->waitQueue.size(),
- (currentTime - connection->waitQueue.front()->deliveryTime) *
- 0.000001f);
- }
- }
- return "";
-}
-
std::string InputDispatcher::getApplicationWindowLabel(
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
if (applicationHandle != nullptr) {
if (windowHandle != nullptr) {
- std::string label(applicationHandle->getName());
- label += " - ";
- label += windowHandle->getName();
- return label;
+ return applicationHandle->getName() + " - " + windowHandle->getName();
} else {
return applicationHandle->getName();
}
} else if (windowHandle != nullptr) {
- return windowHandle->getName();
+ return windowHandle->getInfo()->applicationInfo.name + " - " + windowHandle->getName();
} else {
return "<unknown application or window>";
}
@@ -2168,7 +2174,7 @@
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
if (focusedWindowHandle != nullptr) {
const InputWindowInfo* info = focusedWindowHandle->getInfo();
- if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
+ if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
#endif
@@ -2432,6 +2438,75 @@
traceOutboundQueueLength(connection);
}
+/**
+ * This function is purely for debugging. It helps us understand where the user interaction
+ * was taking place. For example, if user is touching launcher, we will see a log that user
+ * started interacting with launcher. In that example, the event would go to the wallpaper as well.
+ * We will see both launcher and wallpaper in that list.
+ * Once the interaction with a particular set of connections starts, no new logs will be printed
+ * until the set of interacted connections changes.
+ *
+ * The following items are skipped, to reduce the logspam:
+ * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged
+ * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions).
+ * This includes situations like the soft BACK button key. When the user releases (lifts up the
+ * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP.
+ * Both of those ACTION_UP events would not be logged
+ * Monitors (both gesture and global): any gesture monitors or global monitors receiving events
+ * will not be logged. This is omitted to reduce the amount of data printed.
+ * If you see <none>, it's likely that one of the gesture monitors pilfered the event, and therefore
+ * gesture monitor is the only connection receiving the remainder of the gesture.
+ */
+void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) {
+ // Skip ACTION_UP events, and all events other than keys and motions
+ if (entry.type == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
+ return;
+ }
+ } else if (entry.type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+ if (motionEntry.action == AMOTION_EVENT_ACTION_UP ||
+ motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
+ return;
+ }
+ } else {
+ return; // Not a key or a motion
+ }
+
+ std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens;
+ std::vector<sp<Connection>> newConnections;
+ for (const InputTarget& target : targets) {
+ if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ continue; // Skip windows that receive ACTION_OUTSIDE
+ }
+
+ sp<IBinder> token = target.inputChannel->getConnectionToken();
+ sp<Connection> connection = getConnectionLocked(token);
+ if (connection == nullptr || connection->monitor) {
+ continue; // We only need to keep track of the non-monitor connections.
+ }
+ newConnectionTokens.insert(std::move(token));
+ newConnections.emplace_back(connection);
+ }
+ if (newConnectionTokens == mInteractionConnectionTokens) {
+ return; // no change
+ }
+ mInteractionConnectionTokens = newConnectionTokens;
+
+ std::string windowList;
+ for (const sp<Connection>& connection : newConnections) {
+ windowList += connection->getWindowName() + ", ";
+ }
+ std::string message = "Interaction with windows: " + windowList;
+ if (windowList.empty()) {
+ message += "<none>";
+ }
+ android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS;
+}
+
void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
const sp<IBinder>& newToken) {
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
@@ -2474,6 +2549,9 @@
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
+ const nsecs_t timeout =
+ getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
+ dispatchEntry->timeoutTime = currentTime + timeout;
// Publish the event.
status_t status;
@@ -2504,15 +2582,9 @@
const PointerCoords* usingCoords = motionEntry->pointerCoords;
// Set the X and Y offset and X and Y scale depending on the input source.
- float xOffset = 0.0f, yOffset = 0.0f;
- float xScale = 1.0f, yScale = 1.0f;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
- xScale = dispatchEntry->windowXScale;
- yScale = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * xScale;
- yOffset = dispatchEntry->yOffset * yScale;
if (globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
@@ -2547,8 +2619,9 @@
dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState,
motionEntry->buttonState,
- motionEntry->classification, xScale, yScale,
- xOffset, yOffset, motionEntry->xPrecision,
+ motionEntry->classification,
+ dispatchEntry->transform,
+ motionEntry->xPrecision,
motionEntry->yPrecision,
motionEntry->xCursorPosition,
motionEntry->yCursorPosition,
@@ -2593,7 +2666,6 @@
"waiting for the application to catch up",
connection->getInputChannelName().c_str());
#endif
- connection->inputPublisherBlocked = true;
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -2610,6 +2682,10 @@
dispatchEntry));
traceOutboundQueueLength(connection);
connection->waitQueue.push_back(dispatchEntry);
+ if (connection->responsive) {
+ mAnrTracker.insert(dispatchEntry->timeoutTime,
+ connection->inputChannel->getConnectionToken());
+ }
traceWaitQueueLength(connection);
}
}
@@ -2644,8 +2720,6 @@
connection->getInputChannelName().c_str(), seq, toString(handled));
#endif
- connection->inputPublisherBlocked = false;
-
if (connection->status == Connection::STATUS_BROKEN ||
connection->status == Connection::STATUS_ZOMBIE) {
return;
@@ -2748,7 +2822,10 @@
// Monitor channels are never explicitly unregistered.
// We do it automatically when the remote endpoint is closed so don't warn
// about them.
- notify = !connection->monitor;
+ const bool stillHaveWindowHandle =
+ d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
+ nullptr;
+ notify = !connection->monitor && stillHaveWindowHandle;
if (notify) {
ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. "
"events=0x%x",
@@ -2757,7 +2834,7 @@
}
// Unregister the channel.
- d->unregisterInputChannelLocked(connection->inputChannel, notify);
+ d->unregisterInputChannelLocked(*connection->inputChannel, notify);
return 0; // remove the callback
} // release lock
}
@@ -2787,7 +2864,7 @@
}
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
- const sp<InputChannel>& channel, const CancelationOptions& options) {
+ const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) {
sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
if (connection == nullptr) {
return;
@@ -2822,8 +2899,7 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.setDefaultPointerTransform(windowInfo->transform);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
@@ -2888,8 +2964,7 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.setDefaultPointerTransform(windowInfo->transform);
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
@@ -3053,7 +3128,7 @@
if (newKeyCode != AKEYCODE_UNKNOWN) {
std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
- mReplacedKeys.add(replacement, newKeyCode);
+ mReplacedKeys[replacement] = newKeyCode;
keyCode = newKeyCode;
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
@@ -3063,10 +3138,10 @@
// even if the modifier was released between the down and the up events.
std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
- ssize_t index = mReplacedKeys.indexOfKey(replacement);
- if (index >= 0) {
- keyCode = mReplacedKeys.valueAt(index);
- mReplacedKeys.removeItemsAt(index);
+ auto replacementIt = mReplacedKeys.find(replacement);
+ if (replacementIt != mReplacedKeys.end()) {
+ keyCode = replacementIt->second;
+ mReplacedKeys.erase(replacementIt);
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
}
@@ -3200,13 +3275,13 @@
mLock.unlock();
MotionEvent event;
+ ui::Transform transform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
- args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
- 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
- args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime, args->eventTime, args->pointerCount,
- args->pointerProperties, args->pointerCoords);
+ args->metaState, args->buttonState, args->classification, transform,
+ args->xPrecision, args->yPrecision, args->xCursorPosition,
+ args->yCursorPosition, args->downTime, args->eventTime,
+ args->pointerCount, args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -3273,14 +3348,14 @@
int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
int32_t injectorUid, int32_t syncMode,
- int32_t timeoutMillis, uint32_t policyFlags) {
+ std::chrono::milliseconds timeout, uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
- event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
+ "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
+ event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
#endif
- nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
+ nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
policyFlags |= POLICY_FLAG_INJECTED;
if (hasInjectionPermission(injectorPid, injectorUid)) {
@@ -3467,8 +3542,7 @@
} // release lock
#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Finished with result %d. "
- "injectorPid=%d, injectorUid=%d",
+ ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
injectionResult, injectorPid, injectorUid);
#endif
@@ -3570,6 +3644,10 @@
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const {
+ if (windowHandleToken == nullptr) {
+ return nullptr;
+ }
+
for (auto& it : mWindowHandlesByDisplay) {
const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -3585,7 +3663,8 @@
for (auto& it : mWindowHandlesByDisplay) {
const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
for (const sp<InputWindowHandle>& handle : windowHandles) {
- if (handle->getToken() == windowHandle->getToken()) {
+ if (handle->getId() == windowHandle->getId() &&
+ handle->getToken() == windowHandle->getToken()) {
if (windowHandle->getInfo()->displayId != it.first) {
ALOGE("Found window %s in display %" PRId32
", but it should belong to display %" PRId32,
@@ -3599,7 +3678,8 @@
return false;
}
-sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
+ const sp<IBinder>& token) const {
size_t count = mInputChannelsByToken.count(token);
if (count == 0) {
return nullptr;
@@ -3634,10 +3714,9 @@
if ((getInputChannelLocked(handle->getToken()) == nullptr &&
info->portalToDisplayId == ADISPLAY_ID_NONE)) {
const bool noInputChannel =
- info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
- const bool canReceiveInput =
- !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
- !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+ info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+ const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) ||
+ !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE);
if (canReceiveInput && !noInputChannel) {
ALOGV("Window handle %s has no registered input channel",
handle->getName().c_str());
@@ -3651,7 +3730,8 @@
continue;
}
- if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+ if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) &&
+ (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
oldHandle->updateFrom(handle);
newHandles.push_back(oldHandle);
@@ -3724,7 +3804,7 @@
ALOGD("Focus left window: %s in display %" PRId32,
oldFocusedWindowHandle->getName().c_str(), displayId);
}
- sp<InputChannel> focusedInputChannel =
+ std::shared_ptr<InputChannel> focusedInputChannel =
getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (focusedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
@@ -3748,9 +3828,10 @@
}
}
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex >= 0) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ std::unordered_map<int32_t, TouchState>::iterator stateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (stateIt != mTouchStatesByDisplay.end()) {
+ TouchState& state = stateIt->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
@@ -3758,7 +3839,7 @@
ALOGD("Touched window was removed: %s in display %" PRId32,
touchedWindow.windowHandle->getName().c_str(), displayId);
}
- sp<InputChannel> touchedInputChannel =
+ std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -3797,15 +3878,17 @@
sp<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
+ if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
+ inputApplicationHandle != oldFocusedApplicationHandle) {
+ resetNoFocusedWindowTimeoutLocked();
+ }
+
if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
if (oldFocusedApplicationHandle != inputApplicationHandle) {
- if (oldFocusedApplicationHandle != nullptr) {
- resetANRTimeoutsLocked();
- }
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
- resetANRTimeoutsLocked();
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
@@ -3835,7 +3918,7 @@
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
if (oldFocusedWindowHandle != nullptr) {
- sp<InputChannel> inputChannel =
+ std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (inputChannel != nullptr) {
CancelationOptions
@@ -3857,9 +3940,8 @@
if (!mFocusedWindowHandlesByDisplay.empty()) {
ALOGE("But another display has a focused window:");
for (auto& it : mFocusedWindowHandlesByDisplay) {
- const int32_t displayId = it.first;
const sp<InputWindowHandle>& windowHandle = it.second;
- ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId,
+ ALOGE("Display #%" PRId32 " has focused window: '%s'\n", it.first,
windowHandle->getName().c_str());
}
}
@@ -3886,7 +3968,7 @@
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
- resetANRTimeoutsLocked();
+ resetNoFocusedWindowTimeoutLocked();
}
if (mDispatchEnabled && !enabled) {
@@ -3965,8 +4047,8 @@
}
bool found = false;
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+ for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+ TouchState& state = pair.second;
for (size_t i = 0; i < state.windows.size(); i++) {
const TouchedWindow& touchedWindow = state.windows[i];
if (touchedWindow.windowHandle == fromWindowHandle) {
@@ -4026,8 +4108,9 @@
resetKeyRepeatLocked();
releasePendingEventLocked();
drainInboundQueueLocked();
- resetANRTimeoutsLocked();
+ resetNoFocusedWindowTimeoutLocked();
+ mAnrTracker.clear();
mTouchStatesByDisplay.clear();
mLastHoverWindowHandle.clear();
mReplacedKeys.clear();
@@ -4056,12 +4139,11 @@
for (auto& it : mFocusedApplicationHandlesByDisplay) {
const int32_t displayId = it.first;
const sp<InputApplicationHandle>& applicationHandle = it.second;
+ const int64_t timeoutMillis = millis(
+ applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT));
dump += StringPrintf(INDENT2 "displayId=%" PRId32
- ", name='%s', dispatchingTimeout=%0.3fms\n",
- displayId, applicationHandle->getName().c_str(),
- applicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
- 1000000.0);
+ ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
+ displayId, applicationHandle->getName().c_str(), timeoutMillis);
}
} else {
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -4079,10 +4161,10 @@
dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
}
- if (!mTouchStatesByDisplay.isEmpty()) {
+ if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
- for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(i);
+ for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+ const TouchState& state = pair.second;
dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
state.displayId, toString(state.down), toString(state.split),
state.deviceId, state.source);
@@ -4124,9 +4206,9 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"portalToDisplayId=%d, paused=%s, hasFocus=%s, "
"hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
- "flags=0x%08x, type=0x%08x, "
+ "flags=%s, type=0x%08x, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "windowScale=(%f,%f), touchableRegion=",
+ "touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
windowInfo->portalToDisplayId,
toString(windowInfo->paused),
@@ -4134,16 +4216,19 @@
toString(windowInfo->hasWallpaper),
toString(windowInfo->visible),
toString(windowInfo->canReceiveKeys),
- windowInfo->layoutParamsFlags,
- windowInfo->layoutParamsType, windowInfo->frameLeft,
- windowInfo->frameTop, windowInfo->frameRight,
- windowInfo->frameBottom, windowInfo->globalScaleFactor,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ windowInfo->flags.string().c_str(),
+ static_cast<int32_t>(windowInfo->type),
+ windowInfo->frameLeft, windowInfo->frameTop,
+ windowInfo->frameRight, windowInfo->frameBottom,
+ windowInfo->globalScaleFactor);
dumpRegion(dump, windowInfo->touchableRegion);
- dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
- dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ dump += StringPrintf(", inputFeatures=%s",
+ windowInfo->inputFeatures.string().c_str());
+ dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+ "ms\n",
windowInfo->ownerPid, windowInfo->ownerUid,
- windowInfo->dispatchingTimeout / 1000000.0);
+ millis(windowInfo->dispatchingTimeout));
+ windowInfo->transform.dump(dump, INDENT4 "transform=");
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -4176,7 +4261,7 @@
for (EventEntry* entry : mRecentQueue) {
dump += INDENT2;
entry->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
dump += INDENT "RecentQueue: <empty>\n";
@@ -4187,8 +4272,8 @@
dump += INDENT "PendingEvent:\n";
dump += INDENT2;
mPendingEvent->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n",
- (currentTime - mPendingEvent->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%" PRId64 "ms\n",
+ ns2ms(currentTime - mPendingEvent->eventTime));
} else {
dump += INDENT "PendingEvent: <none>\n";
}
@@ -4199,18 +4284,18 @@
for (EventEntry* entry : mInboundQueue) {
dump += INDENT2;
entry->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
dump += INDENT "InboundQueue: <empty>\n";
}
- if (!mReplacedKeys.isEmpty()) {
+ if (!mReplacedKeys.empty()) {
dump += INDENT "ReplacedKeys:\n";
- for (size_t i = 0; i < mReplacedKeys.size(); i++) {
- const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
- int32_t newKeyCode = mReplacedKeys.valueAt(i);
- dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+ for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
+ const KeyReplacement& replacement = pair.first;
+ int32_t newKeyCode = pair.second;
+ dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
replacement.keyCode, replacement.deviceId, newKeyCode);
}
} else {
@@ -4222,11 +4307,10 @@
for (const auto& pair : mConnectionsByFd) {
const sp<Connection>& connection = pair.second;
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
- "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
+ "status=%s, monitor=%s, responsive=%s\n",
pair.first, connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(), connection->getStatusLabel(),
- toString(connection->monitor),
- toString(connection->inputPublisherBlocked));
+ toString(connection->monitor), toString(connection->responsive));
if (!connection->outboundQueue.empty()) {
dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
@@ -4234,9 +4318,10 @@
for (DispatchEntry* entry : connection->outboundQueue) {
dump.append(INDENT4);
entry->eventEntry->appendDescription(dump);
- dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
+ dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
+ "ms\n",
entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+ ns2ms(currentTime - entry->eventEntry->eventTime));
}
} else {
dump += INDENT3 "OutboundQueue: <empty>\n";
@@ -4249,10 +4334,10 @@
dump += INDENT4;
entry->eventEntry->appendDescription(dump);
dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%0.1fms, wait=%0.1fms\n",
+ "age=%" PRId64 "ms, wait=%" PRId64 "ms seq=%" PRIu32 "\n",
entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f,
- (currentTime - entry->deliveryTime) * 0.000001f);
+ ns2ms(currentTime - entry->eventEntry->eventTime),
+ ns2ms(currentTime - entry->deliveryTime), entry->seq);
}
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
@@ -4263,29 +4348,29 @@
}
if (isAppSwitchPendingLocked()) {
- dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
- (mAppSwitchDueTime - now()) / 1000000.0);
+ dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
+ ns2ms(mAppSwitchDueTime - now()));
} else {
dump += INDENT "AppSwitch: not pending\n";
}
dump += INDENT "Configuration:\n";
- dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
- dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
- mConfig.keyRepeatTimeout * 0.000001f);
+ dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
+ dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
+ ns2ms(mConfig.keyRepeatTimeout));
}
void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
const Monitor& monitor = monitors[i];
- const sp<InputChannel>& channel = monitor.inputChannel;
+ const std::shared_ptr<InputChannel>& channel = monitor.inputChannel;
dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str());
dump += "\n";
}
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+status_t InputDispatcher::registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
#endif
@@ -4313,7 +4398,7 @@
return OK;
}
-status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
+status_t InputDispatcher::registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
int32_t displayId, bool isGestureMonitor) {
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -4345,9 +4430,9 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+status_t InputDispatcher::unregisterInputChannel(const InputChannel& inputChannel) {
#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
+ ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel.getName().c_str());
#endif
{ // acquire lock
@@ -4365,24 +4450,23 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
+status_t InputDispatcher::unregisterInputChannelLocked(const InputChannel& inputChannel,
bool notify) {
- sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+ sp<Connection> connection = getConnectionLocked(inputChannel.getConnectionToken());
if (connection == nullptr) {
ALOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel->getName().c_str());
+ inputChannel.getName().c_str());
return BAD_VALUE;
}
- [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection);
- ALOG_ASSERT(removed);
- mInputChannelsByToken.erase(inputChannel->getConnectionToken());
+ removeConnectionLocked(connection);
+ mInputChannelsByToken.erase(inputChannel.getConnectionToken());
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
}
- mLooper->removeFd(inputChannel->getFd());
+ mLooper->removeFd(inputChannel.getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -4391,19 +4475,19 @@
return OK;
}
-void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
+void InputDispatcher::removeMonitorChannelLocked(const InputChannel& inputChannel) {
removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
}
void InputDispatcher::removeMonitorChannelLocked(
- const sp<InputChannel>& inputChannel,
+ const InputChannel& inputChannel,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) {
std::vector<Monitor>& monitors = it->second;
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
- if (monitors[i].inputChannel == inputChannel) {
+ if (*monitors[i].inputChannel == inputChannel) {
monitors.erase(monitors.begin() + i);
break;
}
@@ -4427,13 +4511,14 @@
}
int32_t displayId = foundDisplayId.value();
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex < 0) {
+ std::unordered_map<int32_t, TouchState>::iterator stateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (stateIt == mTouchStatesByDisplay.end()) {
ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
return BAD_VALUE;
}
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ TouchState& state = stateIt->second;
std::optional<int32_t> foundDeviceId;
for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
@@ -4453,7 +4538,8 @@
options.deviceId = deviceId;
options.displayId = displayId;
for (const TouchedWindow& window : state.windows) {
- sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
+ std::shared_ptr<InputChannel> channel =
+ getInputChannelLocked(window.windowHandle->getToken());
if (channel != nullptr) {
synthesizeCancelationEventsForInputChannelLocked(channel, options);
}
@@ -4477,7 +4563,7 @@
return std::nullopt;
}
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
if (inputConnectionToken == nullptr) {
return nullptr;
}
@@ -4492,6 +4578,11 @@
return nullptr;
}
+void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+ mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+ removeByValue(mConnectionsByFd, connection);
+}
+
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
const sp<Connection>& connection, uint32_t seq,
bool handled) {
@@ -4526,41 +4617,81 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onANRLocked(nsecs_t currentTime,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
- nsecs_t waitStartTime, const char* reason) {
- float dispatchLatency = (currentTime - eventTime) * 0.000001f;
- float waitDuration = (currentTime - waitStartTime) * 0.000001f;
- ALOGI("Application is not responding: %s. "
- "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
- waitDuration, reason);
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+ // Since we are allowing the policy to extend the timeout, maybe the waitQueue
+ // is already healthy again. Don't raise ANR in this situation
+ if (connection->waitQueue.empty()) {
+ ALOGI("Not raising ANR because the connection %s has recovered",
+ connection->inputChannel->getName().c_str());
+ return;
+ }
+ /**
+ * The "oldestEntry" is the entry that was first sent to the application. That entry, however,
+ * may not be the one that caused the timeout to occur. One possibility is that window timeout
+ * has changed. This could cause newer entries to time out before the already dispatched
+ * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app
+ * processes the events linearly. So providing information about the oldest entry seems to be
+ * most useful.
+ */
+ DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
+ std::string reason =
+ android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
+ connection->inputChannel->getName().c_str(),
+ ns2ms(currentWait),
+ oldestEntry->eventEntry->getDescription().c_str());
+ updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ reason);
+
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = nullptr;
+ commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->reason = std::move(reason);
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
+ std::string reason = android::base::StringPrintf("%s does not have a focused window",
+ application->getName().c_str());
+
+ updateLastAnrStateLocked(application, reason);
+
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = application;
+ commandEntry->inputChannel = nullptr;
+ commandEntry->reason = std::move(reason);
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
+ updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+ updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel,
+ const std::string& reason) {
// Capture a record of the InputDispatcher state at the time of the ANR.
time_t t = time(nullptr);
struct tm tm;
localtime_r(&t, &tm);
char timestr[64];
strftime(timestr, sizeof(timestr), "%F %T", &tm);
- mLastANRState.clear();
- mLastANRState += INDENT "ANR:\n";
- mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
- mLastANRState +=
- StringPrintf(INDENT2 "Window: %s\n",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
- mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
- mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
- mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
- dumpDispatchStateLocked(mLastANRState);
-
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyANRLockedInterruptible);
- commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputChannel =
- windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
- commandEntry->reason = reason;
- postCommandLocked(std::move(commandEntry));
+ mLastAnrState.clear();
+ mLastAnrState += INDENT "ANR:\n";
+ mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);
+ mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str());
+ mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str());
+ dumpDispatchStateLocked(mLastAnrState);
}
void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
@@ -4591,23 +4722,60 @@
mLock.lock();
}
-void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
sp<IBinder> token =
commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
- nsecs_t newTimeout =
- mPolicy->notifyANR(commandEntry->inputApplicationHandle, token, commandEntry->reason);
+ const std::chrono::nanoseconds timeoutExtension =
+ mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
mLock.lock();
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token);
+ if (timeoutExtension > 0s) {
+ extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
+ } else {
+ // stop waking up for events in this connection, it is already not responding
+ sp<Connection> connection = getConnectionLocked(token);
+ if (connection == nullptr) {
+ return;
+ }
+ cancelEventsForAnrLocked(connection);
+ }
+}
+
+void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken,
+ std::chrono::nanoseconds timeoutExtension) {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
+ // Maybe ANR happened because there's no focused window?
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
+ mAwaitedFocusedApplication = application;
+ } else {
+ // It's also possible that the connection already disappeared. No action necessary.
+ }
+ return;
+ }
+
+ ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
+ connection->inputChannel->getName().c_str(), millis(timeoutExtension));
+
+ connection->responsive = true;
+ const nsecs_t newTimeout = now() + timeoutExtension.count();
+ for (DispatchEntry* entry : connection->waitQueue) {
+ if (newTimeout >= entry->timeoutTime) {
+ // Already removed old entries when connection was marked unresponsive
+ entry->timeoutTime = newTimeout;
+ mAnrTracker.insert(entry->timeoutTime, connectionToken);
+ }
+ }
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
-
KeyEvent event = createKeyEvent(*entry);
mLock.unlock();
@@ -4641,6 +4809,20 @@
mLock.lock();
}
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+static bool isConnectionResponsive(const Connection& connection) {
+ const nsecs_t currentTime = now();
+ for (const DispatchEntry* entry : connection.waitQueue) {
+ if (entry->timeoutTime < currentTime) {
+ return false;
+ }
+ }
+ return true;
+}
+
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
const nsecs_t finishTime = commandEntry->eventTime;
@@ -4653,15 +4835,12 @@
return;
}
DispatchEntry* dispatchEntry = *dispatchEntryIt;
-
- nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+ const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
- std::string msg =
- StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
- connection->getWindowName().c_str(), eventDuration * 0.000001f);
- dispatchEntry->eventEntry->appendDescription(msg);
- ALOGI("%s", msg.c_str());
+ ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+ ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
}
+ reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
bool restartEvent;
if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
@@ -4677,13 +4856,18 @@
}
// Dequeue the event and start the next cycle.
- // Note that because the lock might have been released, it is possible that the
+ // Because the lock might have been released, it is possible that the
// contents of the wait queue to have been drained, so we need to double-check
// a few things.
dispatchEntryIt = connection->findWaitQueueEntry(seq);
if (dispatchEntryIt != connection->waitQueue.end()) {
dispatchEntry = *dispatchEntryIt;
connection->waitQueue.erase(dispatchEntryIt);
+ mAnrTracker.erase(dispatchEntry->timeoutTime,
+ connection->inputChannel->getConnectionToken());
+ if (!connection->responsive) {
+ connection->responsive = isConnectionResponsive(*connection);
+ }
traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.push_front(dispatchEntry);
@@ -4896,9 +5080,8 @@
return event;
}
-void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
- int32_t injectionResult,
- nsecs_t timeSpentWaitingForApplication) {
+void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
+ const Connection& connection, bool handled) {
// TODO Write some statistics about how long we spend waiting.
}
@@ -4958,9 +5141,9 @@
dump += "Input Dispatcher State:\n";
dumpDispatchStateLocked(dump);
- if (!mLastANRState.empty()) {
+ if (!mLastAnrState.empty()) {
dump += "\nInput Dispatcher State at time of last ANR:\n";
- dump += mLastANRState;
+ dump += mLastAnrState;
}
}
@@ -4989,4 +5172,19 @@
return result == std::cv_status::no_timeout;
}
+/**
+ * Sets focus to the window identified by the token. This must be called
+ * after updating any input window handles.
+ *
+ * Params:
+ * request.token - input channel token used to identify the window that should gain focus.
+ * request.focusedToken - the token that the caller expects currently to be focused. If the
+ * specified token does not match the currently focused window, this request will be dropped.
+ * If the specified focused token matches the currently focused window, the call will succeed.
+ * Set this to "null" if this call should succeed no matter what the currently focused token is.
+ * request.timestamp - SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm)
+ * when requesting the focus change. This determines which request gets
+ * precedence if there is a focus change request from another source such as pointer down.
+ */
+void InputDispatcher::setFocusedWindow(const FocusRequest&) {}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 2b9cbce..b4d1079 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
+#include "AnrTracker.h"
#include "CancelationOptions.h"
#include "Entry.h"
#include "InjectionState.h"
@@ -48,6 +49,7 @@
#include <deque>
#include <optional>
#include <unordered_map>
+#include <unordered_set>
#include <InputListener.h>
#include <InputReporterInterface.h>
@@ -103,7 +105,8 @@
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+ int32_t injectorUid, int32_t syncMode,
+ std::chrono::milliseconds timeout,
uint32_t policyFlags) override;
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
@@ -121,10 +124,12 @@
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override;
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
- bool isGestureMonitor) override;
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+ virtual status_t registerInputChannel(
+ const std::shared_ptr<InputChannel>& inputChannel) override;
+ virtual void setFocusedWindow(const FocusRequest&) override;
+ virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) override;
+ virtual status_t unregisterInputChannel(const InputChannel& inputChannel) override;
virtual status_t pilferPointers(const sp<IBinder>& token) override;
private:
@@ -190,26 +195,30 @@
EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+ TouchState* touchState,
bool addOutsideTargets = false,
bool addPortalWindows = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+ sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+ REQUIRES(mLock);
+
+ void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+
struct IBinderHash {
std::size_t operator()(const sp<IBinder>& b) const {
return std::hash<IBinder*>{}(b.get());
}
};
- std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken
- GUARDED_BY(mLock);
+ std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash>
+ mInputChannelsByToken GUARDED_BY(mLock);
// Finds the display ID of the gesture monitor identified by the provided token.
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
- sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) REQUIRES(mLock);
-
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -250,12 +259,14 @@
bool operator==(const KeyReplacement& rhs) const {
return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
}
- bool operator<(const KeyReplacement& rhs) const {
- return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+ };
+ struct KeyReplacementHash {
+ size_t operator()(const KeyReplacement& key) const {
+ return std::hash<int32_t>()(key.keyCode) ^ (std::hash<int32_t>()(key.deviceId) << 1);
}
};
// Maps the key code replaced, device id tuple to the key code it was replaced with
- KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+ std::unordered_map<KeyReplacement, int32_t, KeyReplacementHash> mReplacedKeys GUARDED_BY(mLock);
// Process certain Meta + Key combinations
void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
int32_t& metaState);
@@ -265,6 +276,9 @@
bool runCommandsLockedInterruptible() REQUIRES(mLock);
void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+ nsecs_t processAnrsLocked() REQUIRES(mLock);
+ nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
// Input filter processing.
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
@@ -289,7 +303,8 @@
REQUIRES(mLock);
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
- sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+ std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
+ REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
/*
@@ -303,8 +318,7 @@
std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
GUARDED_BY(mLock);
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
- TouchState mTempTouchState GUARDED_BY(mLock);
+ std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
// Focused applications.
std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
@@ -314,7 +328,12 @@
int32_t mFocusedDisplayId GUARDED_BY(mLock);
// Dispatcher state at time of last ANR.
- std::string mLastANRState GUARDED_BY(mLock);
+ std::string mLastAnrState GUARDED_BY(mLock);
+
+ // The connection tokens of the channels that the user last interacted, for debugging
+ std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock);
+ void updateInteractionTokensLocked(const EventEntry& entry,
+ const std::vector<InputTarget>& targets) REQUIRES(mLock);
// Dispatch inbound events.
bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
@@ -331,36 +350,53 @@
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
- // Keeping track of ANR timeouts.
- enum InputTargetWaitCause {
- INPUT_TARGET_WAIT_CAUSE_NONE,
- INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
- INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
- };
+ /**
+ * This field is set if there is no focused window, and we have an event that requires
+ * a focused window to be dispatched (for example, a KeyEvent).
+ * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before
+ * dropping the event and raising an ANR for that application.
+ * This is useful if an application is slow to add a focused window.
+ */
+ std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
- InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
- bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
- sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+ bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
+
+ /**
+ * Time to stop waiting for the events to be processed while trying to dispatch a key.
+ * When this time expires, we just send the pending key event to the currently focused window,
+ * without waiting on other events to be processed first.
+ */
+ std::optional<nsecs_t> mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock);
+ bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName)
+ REQUIRES(mLock);
+
+ /**
+ * The focused application at the time when no focused window was present.
+ * Used to raise an ANR when we have no focused window.
+ */
+ sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+
+ // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
+ // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
+ // If a connection is not responsive, then the entries should not be added to the AnrTracker.
+ // Once a connection becomes unresponsive, its entries are removed from AnrTracker to
+ // prevent unneeded wakeups.
+ AnrTracker mAnrTracker GUARDED_BY(mLock);
+ void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken,
+ std::chrono::nanoseconds timeoutExtension) REQUIRES(mLock);
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
- // Finding targets for input events.
- int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason)
- REQUIRES(mLock);
-
- void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<IBinder>& inputConnectionToken)
- REQUIRES(mLock);
+ void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
- void resetANRTimeoutsLocked() REQUIRES(mLock);
+ // If a focused application changes, we should stop counting down the "no focused window" time,
+ // because we will have no way of knowing when the previous application actually added a window.
+ // This also means that we will miss cases like pulling down notification shade when the
+ // focused application does not have a focused window (no ANR will be raised if notification
+ // shade is pulled down while we are counting down the timeout).
+ void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry& entry);
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
@@ -371,11 +407,10 @@
nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) REQUIRES(mLock);
std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
REQUIRES(mLock);
- void addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
- float yOffset = 0);
+ std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
+ const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
@@ -394,11 +429,6 @@
std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle);
- std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle,
- const EventEntry& eventEntry,
- const char* targetType) REQUIRES(mLock);
-
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -432,8 +462,8 @@
void synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options)
+ void synthesizeCancelationEventsForInputChannelLocked(
+ const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options)
REQUIRES(mLock);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
const CancelationOptions& options)
@@ -454,11 +484,11 @@
void logDispatchStateLocked() REQUIRES(mLock);
// Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ void removeMonitorChannelLocked(const InputChannel& inputChannel) REQUIRES(mLock);
void removeMonitorChannelLocked(
- const sp<InputChannel>& inputChannel,
+ const InputChannel& inputChannel,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+ status_t unregisterInputChannelLocked(const InputChannel& inputChannel, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -468,16 +498,21 @@
REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
- nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+ void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
+ void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
+ REQUIRES(mLock);
+ void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ const std::string& reason) REQUIRES(mLock);
+ void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
+ REQUIRES(mLock);
// Outbound policy interactions.
void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
REQUIRES(mLock);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
REQUIRES(mLock);
void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
@@ -496,8 +531,8 @@
LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
void reportTouchEventForStatistics(const MotionEntry& entry);
- void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
- int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
+ void reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
+ const Connection& connection, bool handled);
void traceInboundQueueLengthLocked() REQUIRES(mLock);
void traceOutboundQueueLength(const sp<Connection>& connection);
void traceWaitQueueLength(const sp<Connection>& connection);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 0588374..f6958d4 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,12 +42,11 @@
return StringPrintf("%" PRId32, dispatchMode);
}
-void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
- float windowXScale, float windowYScale) {
+void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
// and non splittable windows since we will just use all the pointers from the input event.
if (newPointerIds.isEmpty()) {
- setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ setDefaultPointerTransform(transform);
return;
}
@@ -57,47 +56,39 @@
pointerIds |= newPointerIds;
while (!newPointerIds.isEmpty()) {
int32_t pointerId = newPointerIds.clearFirstMarkedBit();
- pointerInfos[pointerId].xOffset = xOffset;
- pointerInfos[pointerId].yOffset = yOffset;
- pointerInfos[pointerId].windowXScale = windowXScale;
- pointerInfos[pointerId].windowYScale = windowYScale;
+ pointerTransforms[pointerId] = transform;
}
}
-void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale) {
+void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
pointerIds.clear();
- pointerInfos[0].xOffset = xOffset;
- pointerInfos[0].yOffset = yOffset;
- pointerInfos[0].windowXScale = windowXScale;
- pointerInfos[0].windowYScale = windowYScale;
+ pointerTransforms[0] = transform;
}
-bool InputTarget::useDefaultPointerInfo() const {
+bool InputTarget::useDefaultPointerTransform() const {
return pointerIds.isEmpty();
}
-const PointerInfo& InputTarget::getDefaultPointerInfo() const {
- return pointerInfos[0];
+const ui::Transform& InputTarget::getDefaultPointerTransform() const {
+ return pointerTransforms[0];
}
std::string InputTarget::getPointerInfoString() const {
- if (useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = getDefaultPointerInfo();
- return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
- pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
- pointerInfo.windowYScale);
+ std::string out;
+ if (useDefaultPointerTransform()) {
+ const ui::Transform& transform = getDefaultPointerTransform();
+ transform.dump(out, "default");
+ return out;
}
- std::string out;
for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
if (!pointerIds.hasBit(i)) {
continue;
}
- out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
- "windowScaleFactor=(%.1f, %.1f)",
- i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
- pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+
+ out += "\n";
+ const std::string name = "pointerId " + std::to_string(i) + ":";
+ pointerTransforms[i].dump(out, name.c_str());
}
return out;
}
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 499a75f..debf805 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -18,28 +18,13 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
#include <input/InputTransport.h>
+#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
namespace android::inputdispatcher {
/*
- * Information about each pointer for an InputTarget. This includes offset and scale so
- * all pointers can be normalized to a single offset and scale.
- *
- * These values are ignored for KeyEvents
- */
-struct PointerInfo {
- // The x and y offset to add to a MotionEvent as it is delivered.
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
- // Scaling factor to apply to MotionEvent as it is delivered.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
-};
-
-/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -106,7 +91,7 @@
};
// The input channel to be targeted.
- sp<InputChannel> inputChannel;
+ std::shared_ptr<InputChannel> inputChannel;
// Flags for the input target.
int32_t flags = 0;
@@ -119,13 +104,11 @@
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
// The data is stored by the pointerId. Use the bit position of pointerIds to look up
- // PointerInfo per pointerId.
- PointerInfo pointerInfos[MAX_POINTERS];
+ // Transform per pointerId.
+ ui::Transform pointerTransforms[MAX_POINTERS];
- void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
- float windowYScale);
- void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale);
+ void addPointers(BitSet32 pointerIds, const ui::Transform& transform);
+ void setDefaultPointerTransform(const ui::Transform& transform);
/**
* Returns whether the default pointer information should be used. This will be true when the
@@ -133,13 +116,13 @@
* and non splittable windows since we want all pointers for the EventEntry to go to this
* target.
*/
- bool useDefaultPointerInfo() const;
+ bool useDefaultPointerTransform() const;
/**
- * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * Returns the default Transform object. This should be used when useDefaultPointerTransform is
* true.
*/
- const PointerInfo& getDefaultPointerInfo() const;
+ const ui::Transform& getDefaultPointerTransform() const;
std::string getPointerInfoString() const;
};
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index 289b084..b347674 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,7 @@
namespace android::inputdispatcher {
// --- Monitor ---
-Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
// --- TouchedMonitor ---
TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index b67c9eb..fc0b020 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -22,9 +22,9 @@
namespace android::inputdispatcher {
struct Monitor {
- sp<InputChannel> inputChannel; // never null
+ std::shared_ptr<InputChannel> inputChannel; // never null
- explicit Monitor(const sp<InputChannel>& inputChannel);
+ explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel);
};
// For tracking the offsets we need to apply when adding gesture monitor targets.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2baceba..81b3cf0 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -137,8 +137,7 @@
for (const TouchedWindow& window : windows) {
if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
if (haveSlipperyForegroundWindow ||
- !(window.windowHandle->getInfo()->layoutParamsFlags &
- InputWindowInfo::FLAG_SLIPPERY)) {
+ !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
return false;
}
haveSlipperyForegroundWindow = true;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 09dc92c..f97c880 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,15 +18,15 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
#include <InputListener.h>
-#include <input/ISetInputWindowsListener.h>
+#include <android/FocusRequest.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <input/InputApplication.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <unordered_map>
namespace android {
-class InputApplicationHandle;
-class InputChannel;
-class InputWindowHandle;
-
/*
* Constants used to report the outcome of input event injection.
*/
@@ -90,8 +90,8 @@
* This method may be called on any thread (usually by the input manager).
*/
virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
- uint32_t policyFlags) = 0;
+ int32_t injectorUid, int32_t syncMode,
+ std::chrono::milliseconds timeout, uint32_t policyFlags) = 0;
/*
* Check whether InputEvent actually happened by checking the signature of the event.
@@ -149,11 +149,16 @@
*/
virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+ /**
+ * Sets focus on the specified window.
+ */
+ virtual void setFocusedWindow(const FocusRequest&) = 0;
+
/* Registers input channels that may be used as targets for input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) = 0;
/* Registers input channels to be used to monitor input events.
*
@@ -163,14 +168,14 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
- bool gestureMonitor) = 0;
+ virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
+ int32_t displayId, bool gestureMonitor) = 0;
/* Unregister input channels that will no longer receive input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t unregisterInputChannel(const InputChannel& inputChannel) = 0;
/* Allows an input monitor steal the current pointer stream away from normal input windows.
*
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 4214488..c886bee 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -21,11 +21,11 @@
#include <binder/IBinder.h>
#include <input/Input.h>
+#include <input/InputApplication.h>
#include <utils/RefBase.h>
namespace android {
-class InputApplicationHandle;
/*
* Input dispatcher policy interface.
@@ -47,8 +47,9 @@
/* Notifies the system that an application is not responding.
* Returns a new timeout to continue waiting, or 0 to abort dispatch. */
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) = 0;
+ virtual std::chrono::nanoseconds notifyAnr(
+ const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token,
+ const std::string& reason) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index cbe0190..3496a24 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -21,6 +21,7 @@
"InputHost.cpp",
],
+ header_libs: ["jni_headers"],
shared_libs: [
"libbinder",
"libcrypto",
@@ -42,6 +43,7 @@
//-fvisibility=hidden
],
+ export_header_lib_headers: ["jni_headers"],
export_include_dirs: ["."],
}
@@ -57,9 +59,11 @@
shared_libs: [
"libbinder",
"libinputflingerhost",
- "libutils"
+ "libutils",
+ "libinput"
],
static_libs: [
"libarect",
+ "libui-types",
],
}
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 683c05d..ff69800 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -217,8 +217,10 @@
idi.product = id->productId;
idi.version = id->version;
- std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+ std::string configFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(idi,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
if (configFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
idi.name.c_str());
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 973b4f9..2a2cea5 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -22,13 +22,17 @@
#include "InputHost.h"
+#include <android/os/BnInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+#include <binder/Binder.h>
#include <cutils/compiler.h>
-#include <input/IInputFlinger.h>
-#include <input/ISetInputWindowsListener.h>
-#include <utils/String8.h>
#include <utils/String16.h>
+#include <utils/String8.h>
#include <utils/StrongPointer.h>
+using android::os::BnInputFlinger;
+using android::os::ISetInputWindowsListener;
+
namespace android {
class InputFlinger : public BnInputFlinger {
@@ -40,10 +44,13 @@
InputFlinger() ANDROID_API;
virtual status_t dump(int fd, const Vector<String16>& args);
- void setInputWindows(const std::vector<InputWindowInfo>&,
- const sp<ISetInputWindowsListener>&) {}
- void registerInputChannel(const sp<InputChannel>&) {}
- void unregisterInputChannel(const sp<InputChannel>&) {}
+ binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
+ const sp<ISetInputWindowsListener>&) {
+ return binder::Status::ok();
+ }
+ binder::Status registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
+ binder::Status unregisterInputChannel(const InputChannel&) { return binder::Status::ok(); }
+ binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); }
private:
virtual ~InputFlinger();
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index f8d0150..8317b05 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -22,7 +22,6 @@
#include <input/Input.h>
#include <input/TouchVideoFrame.h>
#include <utils/RefBase.h>
-#include <utils/Vector.h>
namespace android {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index f8d5351..5a832e7 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -17,23 +17,24 @@
#ifndef _UI_INPUT_READER_BASE_H
#define _UI_INPUT_READER_BASE_H
-#include "PointerControllerInterface.h"
-
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
+#include <stddef.h>
+#include <unistd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
-#include <stddef.h>
-#include <unistd.h>
#include <optional>
#include <set>
#include <unordered_map>
#include <vector>
+#include "PointerControllerInterface.h"
+#include "VibrationElement.h"
+
// Maximum supported size of a vibration pattern.
// Must be at least 2.
#define MAX_VIBRATE_PATTERN_SIZE 100
@@ -41,6 +42,7 @@
// Maximum allowable delay value in a vibration pattern before
// which the delay will be truncated.
#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+#define MAX_VIBRATE_PATTERN_DELAY_MSECS (1000000 * 1000LL)
namespace android {
@@ -104,8 +106,8 @@
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
/* Controls the vibrator of a particular input device. */
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
- ssize_t repeat, int32_t token) = 0;
+ virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
+ ssize_t repeat, int32_t token) = 0;
virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
/* Return true if the device can send input events to the specified display. */
@@ -335,7 +337,8 @@
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
/* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
+ virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(
+ int32_t deviceId) = 0;
/* Notifies the input reader policy that some input devices have changed
* and provides information about all current input devices.
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 194c665..85d7247 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -33,7 +33,7 @@
* The pointer controller is responsible for providing synchronization and for tracking
* display orientation changes if needed.
*/
-class PointerControllerInterface : public virtual RefBase {
+class PointerControllerInterface {
protected:
PointerControllerInterface() { }
virtual ~PointerControllerInterface() { }
@@ -59,11 +59,11 @@
/* Gets the absolute location of the pointer. */
virtual void getPosition(float* outX, float* outY) const = 0;
- enum Transition {
+ enum class Transition {
// Fade/unfade immediately.
- TRANSITION_IMMEDIATE,
+ IMMEDIATE,
// Fade/unfade gradually.
- TRANSITION_GRADUAL,
+ GRADUAL,
};
/* Fades the pointer out now. */
@@ -75,11 +75,11 @@
* wants to ensure that the pointer becomes visible again. */
virtual void unfade(Transition transition) = 0;
- enum Presentation {
+ enum class Presentation {
// Show the mouse pointer.
- PRESENTATION_POINTER,
+ POINTER,
// Show spots and a spot anchor in place of the mouse pointer.
- PRESENTATION_SPOT,
+ SPOT,
};
/* Sets the mode of the pointer controller. */
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
new file mode 100644
index 0000000..8a134ee
--- /dev/null
+++ b/services/inputflinger/include/VibrationElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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 _VIBRATION_ELEMENT_H
+#define _VIBRATION_ELEMENT_H
+
+#include <chrono>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace android {
+
+/*
+ * Describes a rumble effect
+ */
+struct VibrationElement {
+ std::chrono::milliseconds duration;
+ std::vector<int> channels;
+
+ void dump(std::string& dump) const;
+ uint16_t getChannel(int id) const;
+ bool isOn() const;
+};
+
+} // namespace android
+
+#endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 83a610f..0ccada9 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -81,4 +81,7 @@
export_header_lib_headers: [
"libinputreader_headers",
],
+ static_libs: [
+ "libc++fs"
+ ],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 264d287..cef2b4c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -34,30 +34,19 @@
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
-
-#include "EventHub.h"
-
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
+#include <input/KeyCharacterMap.h>
+#include <input/KeyLayoutMap.h>
+#include <input/VirtualKeyMap.h>
#include <openssl/sha.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Timers.h>
-#include <utils/threads.h>
-#include <input/KeyCharacterMap.h>
-#include <input/KeyLayoutMap.h>
-#include <input/VirtualKeyMap.h>
+#include <filesystem>
-/* this macro is used to tell if "bit" is set in "array"
- * it selects a byte from the array, and does a boolean AND
- * operation with a byte that only has the relevant bit set.
- * eg. to check for the 12th bit, we do (array[1] & 1<<4)
- */
-#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8)))
-
-/* this macro computes the number of bytes needed to represent a bit array of the specified size */
-#define sizeof_bit_array(bits) (((bits) + 7) / 8)
+#include "EventHub.h"
#define INDENT " "
#define INDENT2 " "
@@ -94,8 +83,8 @@
/**
* Return true if name matches "v4l-touch*"
*/
-static bool isV4lTouchNode(const char* name) {
- return strstr(name, "v4l-touch") == name;
+static bool isV4lTouchNode(std::string name) {
+ return name.find("v4l-touch") != std::string::npos;
}
/**
@@ -193,15 +182,7 @@
ffEffectId(-1),
controllerNumber(0),
enabled(true),
- isVirtual(fd < 0) {
- memset(keyBitmask, 0, sizeof(keyBitmask));
- memset(absBitmask, 0, sizeof(absBitmask));
- memset(relBitmask, 0, sizeof(relBitmask));
- memset(swBitmask, 0, sizeof(swBitmask));
- memset(ledBitmask, 0, sizeof(ledBitmask));
- memset(ffBitmask, 0, sizeof(ffBitmask));
- memset(propBitmask, 0, sizeof(propBitmask));
-}
+ isVirtual(fd < 0) {}
EventHub::Device::~Device() {
close();
@@ -391,7 +372,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+ if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
struct input_absinfo info;
if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -418,9 +399,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(axis, device->relBitmask);
- }
+ return device != nullptr ? device->relBitmask.test(axis) : false;
}
return false;
}
@@ -430,9 +409,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
- return test_bit(property, device->propBitmask);
- }
+ return device != nullptr ? device->propBitmask.test(property) : false;
}
return false;
}
@@ -442,11 +419,9 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
- return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) {
+ if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
+ return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
}
}
@@ -457,16 +432,14 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
std::vector<int32_t> scanCodes;
device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
if (scanCodes.size() != 0) {
- uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
- memset(keyState, 0, sizeof(keyState));
- if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
+ if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) {
for (size_t i = 0; i < scanCodes.size(); i++) {
int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
+ if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) {
return AKEY_STATE_DOWN;
}
}
@@ -482,11 +455,9 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) {
- uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
- memset(swState, 0, sizeof(swState));
- if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
- return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) {
+ if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) {
+ return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
}
}
@@ -500,7 +471,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
+ if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) {
struct input_absinfo info;
if (ioctl(device->fd, EVIOCGABS(axis), &info)) {
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis,
@@ -520,7 +491,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
std::vector<int32_t> scanCodes;
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
@@ -531,7 +502,7 @@
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], device->keyBitmask)) {
+ if (device->keyBitmask.test(scanCodes[sc])) {
outFlags[codeIndex] = 1;
break;
}
@@ -549,7 +520,7 @@
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
- if (device) {
+ if (device != nullptr) {
// Check the key character map first.
sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm != nullptr) {
@@ -588,7 +559,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->keyMap.haveKeyLayout()) {
+ if (device != nullptr && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
if (err == NO_ERROR) {
return NO_ERROR;
@@ -607,10 +578,8 @@
bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && scanCode >= 0 && scanCode <= KEY_MAX) {
- if (test_bit(scanCode, device->keyBitmask)) {
- return true;
- }
+ if (device != nullptr && scanCode >= 0 && scanCode <= KEY_MAX) {
+ return device->keyBitmask.test(scanCode);
}
return false;
}
@@ -619,10 +588,8 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
int32_t sc;
- if (device && mapLed(device, led, &sc) == NO_ERROR) {
- if (test_bit(sc, device->ledBitmask)) {
- return true;
- }
+ if (device != nullptr && mapLed(device, led, &sc) == NO_ERROR) {
+ return device->ledBitmask.test(sc);
}
return false;
}
@@ -635,7 +602,7 @@
void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) {
int32_t sc;
- if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
+ if (device != nullptr && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
struct input_event ev;
ev.time.tv_sec = 0;
ev.time.tv_usec = 0;
@@ -656,7 +623,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->virtualKeyMap) {
+ if (device != nullptr && device->virtualKeyMap) {
const std::vector<VirtualKeyDefinition> virtualKeys =
device->virtualKeyMap->getVirtualKeys();
outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end());
@@ -666,7 +633,7 @@
sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
+ if (device != nullptr) {
return device->getKeyCharacterMap();
}
return nullptr;
@@ -675,7 +642,7 @@
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device) {
+ if (device != nullptr) {
if (map != device->overlayKeyMap) {
device->overlayKeyMap = map;
device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
@@ -735,17 +702,18 @@
identifier.descriptor.c_str());
}
-void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
+void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd()) {
+ if (device != nullptr && device->hasValidFd()) {
ff_effect effect;
memset(&effect, 0, sizeof(effect));
effect.type = FF_RUMBLE;
effect.id = device->ffEffectId;
- effect.u.rumble.strong_magnitude = 0xc000;
- effect.u.rumble.weak_magnitude = 0xc000;
- effect.replay.length = (duration + 999999LL) / 1000000LL;
+ // evdev FF_RUMBLE effect only supports two channels of vibration.
+ effect.u.rumble.strong_magnitude = element.getChannel(0);
+ effect.u.rumble.weak_magnitude = element.getChannel(1);
+ effect.replay.length = element.duration.count();
effect.replay.delay = 0;
if (ioctl(device->fd, EVIOCSFF, &effect)) {
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
@@ -772,7 +740,7 @@
void EventHub::cancelVibrate(int32_t deviceId) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device && device->hasValidFd()) {
+ if (device != nullptr && device->hasValidFd()) {
if (device->ffEffectPlaying) {
device->ffEffectPlaying = false;
@@ -810,7 +778,7 @@
return index >= 0 ? mDevices.valueAt(index) : NULL;
}
-EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
+EventHub::Device* EventHub::getDeviceByPathLocked(const std::string& devicePath) const {
for (size_t i = 0; i < mDevices.size(); i++) {
Device* device = mDevices.valueAt(i);
if (device->path == devicePath) {
@@ -933,11 +901,11 @@
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
- char buffer[16];
+ char wakeReadBuffer[16];
ssize_t nRead;
do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+ nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
@@ -1089,7 +1057,7 @@
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (!device || !device->videoDevice) {
+ if (device == nullptr || !device->videoDevice) {
return {};
}
return device->videoDevice->consumeFrames();
@@ -1126,17 +1094,6 @@
// ----------------------------------------------------------------------------
-static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
- const uint8_t* end = array + endIndex;
- array += startIndex;
- while (array != end) {
- if (*(array++) != 0) {
- return true;
- }
- }
- return false;
-}
-
static const int32_t GAMEPAD_KEYCODES[] = {
AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, //
AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, //
@@ -1215,14 +1172,14 @@
}
}
-status_t EventHub::openDeviceLocked(const char* devicePath) {
+status_t EventHub::openDeviceLocked(const std::string& devicePath) {
char buffer[80];
- ALOGV("Opening device: %s", devicePath);
+ ALOGV("Opening device: %s", devicePath.c_str());
- int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+ int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
- ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
return -1;
}
@@ -1230,7 +1187,7 @@
// Get device name.
if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
- ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
+ ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name = buffer;
@@ -1240,7 +1197,7 @@
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const std::string& item = mExcludedDevices[i];
if (identifier.name == item) {
- ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
+ ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
return -1;
}
@@ -1249,7 +1206,7 @@
// Get device driver version.
int driverVersion;
if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
- ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
return -1;
}
@@ -1257,7 +1214,7 @@
// Get device identifier.
struct input_id inputId;
if (ioctl(fd, EVIOCGID, &inputId)) {
- ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
return -1;
}
@@ -1289,7 +1246,7 @@
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, devicePath, identifier);
- ALOGV("add device %d: %s\n", deviceId, devicePath);
+ ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
@@ -1306,31 +1263,27 @@
loadConfigurationLocked(device);
// Figure out the kinds of events the device reports.
- ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
- ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
- ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
- ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
- ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
- ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
+ device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
+ device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys =
- containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
- containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX + 1));
- bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
- sizeof_bit_array(BTN_MOUSE)) ||
- containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
- sizeof_bit_array(BTN_DIGI));
+ device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
+ bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
+ device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
// See if this is a cursor device such as a trackball or mouse.
- if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) &&
- test_bit(REL_Y, device->relBitmask)) {
+ if (device->keyBitmask.test(BTN_MOUSE) && device->relBitmask.test(REL_X) &&
+ device->relBitmask.test(REL_Y)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
@@ -1345,22 +1298,20 @@
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_POSITION_X, device->absBitmask) &&
- test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
+ if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
- if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
+ if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) &&
- test_bit(ABS_Y, device->absBitmask)) {
+ } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
+ device->absBitmask.test(ABS_Y)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
// Is this a BT stylus?
- } else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
- test_bit(BTN_TOUCH, device->keyBitmask)) &&
- !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) {
+ } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
+ !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
// Keyboard will try to claim some of the buttons but we really want to reserve those so we
// can fuse it with the touch screen data, so just take them back. Note this means an
@@ -1374,7 +1325,7 @@
if (haveGamepadButtons) {
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
- if (test_bit(i, device->absBitmask) &&
+ if (device->absBitmask.test(i) &&
(getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
device->classes = assumedClasses;
break;
@@ -1384,14 +1335,14 @@
// Check whether this device has switches.
for (int i = 0; i <= SW_MAX; i++) {
- if (test_bit(i, device->swBitmask)) {
+ if (device->swBitmask.test(i)) {
device->classes |= INPUT_DEVICE_CLASS_SWITCH;
break;
}
}
// Check whether this device supports the vibrator.
- if (test_bit(FF_RUMBLE, device->ffBitmask)) {
+ if (device->ffBitmask.test(FF_RUMBLE)) {
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
}
@@ -1446,7 +1397,7 @@
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
- ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,
+ ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
device->identifier.name.c_str());
delete device;
return -1;
@@ -1492,7 +1443,7 @@
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
- deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes,
+ deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(), device->classes,
device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(),
device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId));
@@ -1613,8 +1564,10 @@
}
void EventHub::loadConfigurationLocked(Device* device) {
- device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
- device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
+ device->configurationFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(device->identifier,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
if (device->configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
device->identifier.name.c_str());
@@ -1701,7 +1654,7 @@
const size_t N = scanCodes.size();
for (size_t i = 0; i < N && i <= KEY_MAX; i++) {
int32_t sc = scanCodes[i];
- if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
+ if (sc >= 0 && sc <= KEY_MAX && device->keyBitmask.test(sc)) {
return true;
}
}
@@ -1716,7 +1669,7 @@
int32_t scanCode;
if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) {
- if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) {
+ if (scanCode >= 0 && scanCode <= LED_MAX && device->ledBitmask.test(scanCode)) {
*outScanCode = scanCode;
return NO_ERROR;
}
@@ -1724,13 +1677,13 @@
return NAME_NOT_FOUND;
}
-void EventHub::closeDeviceByPathLocked(const char* devicePath) {
+void EventHub::closeDeviceByPathLocked(const std::string& devicePath) {
Device* device = getDeviceByPathLocked(devicePath);
if (device) {
closeDeviceLocked(device);
return;
}
- ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
+ ALOGV("Remove device: %s not found, device may already have been removed.", devicePath.c_str());
}
/**
@@ -1835,16 +1788,16 @@
event = (struct inotify_event*)(event_buf + event_pos);
if (event->len) {
if (event->wd == mInputWd) {
- std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+ std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
- openDeviceLocked(filename.c_str());
+ openDeviceLocked(filename);
} else {
ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
- closeDeviceByPathLocked(filename.c_str());
+ closeDeviceByPathLocked(filename);
}
} else if (event->wd == mVideoWd) {
if (isV4lTouchNode(event->name)) {
- std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+ std::string filename = std::string(VIDEO_DEVICE_PATH) + "/" + event->name;
if (event->mask & IN_CREATE) {
openVideoDeviceLocked(filename);
} else {
@@ -1863,24 +1816,10 @@
return 0;
}
-status_t EventHub::scanDirLocked(const char* dirname) {
- char devname[PATH_MAX];
- char* filename;
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname);
- if (dir == nullptr) return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = '/';
- while ((de = readdir(dir))) {
- if (de->d_name[0] == '.' &&
- (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
- continue;
- strcpy(filename, de->d_name);
- openDeviceLocked(devname);
+status_t EventHub::scanDirLocked(const std::string& dirname) {
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ openDeviceLocked(entry.path());
}
- closedir(dir);
return 0;
}
@@ -1888,22 +1827,12 @@
* Look for all dirname/v4l-touch* devices, and open them.
*/
status_t EventHub::scanVideoDirLocked(const std::string& dirname) {
- DIR* dir;
- struct dirent* de;
- dir = opendir(dirname.c_str());
- if (!dir) {
- ALOGE("Could not open video directory %s", dirname.c_str());
- return BAD_VALUE;
- }
-
- while ((de = readdir(dir))) {
- const char* name = de->d_name;
- if (isV4lTouchNode(name)) {
- ALOGI("Found touch video device %s", name);
- openVideoDeviceLocked(dirname + "/" + name);
+ for (const auto& entry : std::filesystem::directory_iterator(dirname)) {
+ if (isV4lTouchNode(entry.path())) {
+ ALOGI("Found touch video device %s", entry.path().c_str());
+ openVideoDeviceLocked(entry.path());
}
}
- closedir(dir);
return OK;
}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 4b19e5e..b4eaf7f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -424,10 +424,10 @@
return result;
}
-void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void InputDevice::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) {
- for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
- mapper.vibrate(pattern, patternSize, repeat, token);
+ for_each_mapper([pattern, repeat, token](InputMapper& mapper) {
+ mapper.vibrate(pattern, repeat, token);
});
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 657a134..16fe865 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -390,8 +390,9 @@
}
}
-sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) {
- sp<PointerControllerInterface> controller = mPointerController.promote();
+std::shared_ptr<PointerControllerInterface> InputReader::getPointerControllerLocked(
+ int32_t deviceId) {
+ std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
if (controller == nullptr) {
controller = mPolicy->obtainPointerController(deviceId);
mPointerController = controller;
@@ -401,7 +402,7 @@
}
void InputReader::updatePointerDisplayLocked() {
- sp<PointerControllerInterface> controller = mPointerController.promote();
+ std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
if (controller == nullptr) {
return;
}
@@ -424,9 +425,9 @@
}
void InputReader::fadePointerLocked() {
- sp<PointerControllerInterface> controller = mPointerController.promote();
+ std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
if (controller != nullptr) {
- controller->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ controller->fade(PointerControllerInterface::Transition::GRADUAL);
}
}
@@ -558,12 +559,12 @@
}
}
-void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+void InputReader::vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
ssize_t repeat, int32_t token) {
AutoMutex _l(mLock);
InputDevice* device = findInputDevice(deviceId);
if (device) {
- device->vibrate(pattern, patternSize, repeat, token);
+ device->vibrate(pattern, repeat, token);
}
}
@@ -725,7 +726,8 @@
mReader->fadePointerLocked();
}
-sp<PointerControllerInterface> InputReader::ContextImpl::getPointerController(int32_t deviceId) {
+std::shared_ptr<PointerControllerInterface> InputReader::ContextImpl::getPointerController(
+ int32_t deviceId) {
// lock is already held by the input loop
return mReader->getPointerControllerLocked(deviceId);
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index c17f3a1..c5dfcfd 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -17,6 +17,8 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
+#include <bitset>
+#include <climits>
#include <vector>
#include <input/Input.h>
@@ -25,6 +27,8 @@
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
+#include <linux/input.h>
+#include <sys/epoll.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
@@ -33,15 +37,8 @@
#include <utils/Mutex.h>
#include <utils/PropertyMap.h>
-#include <linux/input.h>
-#include <sys/epoll.h>
-
#include "TouchVideoDevice.h"
-
-/* Convenience constants. */
-
-#define BTN_FIRST 0x100 // first button code
-#define BTN_LAST 0x15f // last button code
+#include "VibrationElement.h"
namespace android {
@@ -231,7 +228,7 @@
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
/* Control the vibrator. */
- virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
+ virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
virtual void cancelVibrate(int32_t deviceId) = 0;
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
@@ -256,6 +253,76 @@
virtual status_t disableDevice(int32_t deviceId) = 0;
};
+template <std::size_t BITS>
+class BitArray {
+ /* Array element type and vector of element type. */
+ using Element = std::uint32_t;
+ /* Number of bits in each BitArray element. */
+ static constexpr size_t WIDTH = sizeof(Element) * CHAR_BIT;
+ /* Number of elements to represent a bit array of the specified size of bits. */
+ static constexpr size_t COUNT = (BITS + WIDTH - 1) / WIDTH;
+
+public:
+ /* BUFFER type declaration for BitArray */
+ using Buffer = std::array<Element, COUNT>;
+ /* To tell if a bit is set in array, it selects an element from the array, and test
+ * if the relevant bit set.
+ * Note the parameter "bit" is an index to the bit, 0 <= bit < BITS.
+ */
+ inline bool test(size_t bit) const {
+ return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false;
+ }
+ /* Returns total number of bytes needed for the array */
+ inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; }
+ /* Returns true if array contains any non-zero bit from the range defined by start and end
+ * bit index [startIndex, endIndex).
+ */
+ bool any(size_t startIndex, size_t endIndex) {
+ if (startIndex >= endIndex || startIndex > BITS || endIndex > BITS + 1) {
+ ALOGE("Invalid start/end index. start = %zu, end = %zu, total bits = %zu", startIndex,
+ endIndex, BITS);
+ return false;
+ }
+ size_t se = startIndex / WIDTH; // Start of element
+ size_t ee = endIndex / WIDTH; // End of element
+ size_t si = startIndex % WIDTH; // Start index in start element
+ size_t ei = endIndex % WIDTH; // End index in end element
+ // Need to check first unaligned bitset for any non zero bit
+ if (si > 0) {
+ size_t nBits = se == ee ? ei - si : WIDTH - si;
+ // Generate the mask of interested bit range
+ Element mask = ((1 << nBits) - 1) << si;
+ if (mData[se++].to_ulong() & mask) {
+ return true;
+ }
+ }
+ // Check whole bitset for any bit set
+ for (; se < ee; se++) {
+ if (mData[se].any()) {
+ return true;
+ }
+ }
+ // Need to check last unaligned bitset for any non zero bit
+ if (ei > 0 && se <= ee) {
+ // Generate the mask of interested bit range
+ Element mask = (1 << ei) - 1;
+ if (mData[se].to_ulong() & mask) {
+ return true;
+ }
+ }
+ return false;
+ }
+ /* Load bit array values from buffer */
+ void loadFromBuffer(const Buffer& buffer) {
+ for (size_t i = 0; i < COUNT; i++) {
+ mData[i] = std::bitset<WIDTH>(buffer[i]);
+ }
+ }
+
+private:
+ std::array<std::bitset<WIDTH>, COUNT> mData;
+};
+
class EventHub : public EventHubInterface {
public:
EventHub();
@@ -307,7 +374,7 @@
virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
const sp<KeyCharacterMap>& map) override;
- virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
+ virtual void vibrate(int32_t deviceId, const VibrationElement& effect) override;
virtual void cancelVibrate(int32_t deviceId) override;
virtual void requestReopenDevices() override;
@@ -332,13 +399,15 @@
uint32_t classes;
- uint8_t keyBitmask[(KEY_MAX + 1) / 8];
- uint8_t absBitmask[(ABS_MAX + 1) / 8];
- uint8_t relBitmask[(REL_MAX + 1) / 8];
- uint8_t swBitmask[(SW_MAX + 1) / 8];
- uint8_t ledBitmask[(LED_MAX + 1) / 8];
- uint8_t ffBitmask[(FF_MAX + 1) / 8];
- uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
+ BitArray<KEY_MAX> keyBitmask;
+ BitArray<KEY_MAX> keyState;
+ BitArray<ABS_MAX> absBitmask;
+ BitArray<REL_MAX> relBitmask;
+ BitArray<SW_MAX> swBitmask;
+ BitArray<SW_MAX> swState;
+ BitArray<LED_MAX> ledBitmask;
+ BitArray<FF_MAX> ffBitmask;
+ BitArray<INPUT_PROP_MAX> propBitmask;
std::string configurationFile;
PropertyMap* configuration;
@@ -371,15 +440,30 @@
}
return keyMap.keyCharacterMap;
}
+
+ template <std::size_t N>
+ status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray) {
+ if (!hasValidFd()) {
+ return BAD_VALUE;
+ }
+ if ((_IOC_SIZE(ioctlCode) == 0)) {
+ ioctlCode |= _IOC(0, 0, 0, bitArray.bytes());
+ }
+
+ typename BitArray<N>::Buffer buffer;
+ status_t ret = ioctl(fd, ioctlCode, buffer.data());
+ bitArray.loadFromBuffer(buffer);
+ return ret;
+ }
};
- status_t openDeviceLocked(const char* devicePath);
+ status_t openDeviceLocked(const std::string& devicePath);
void openVideoDeviceLocked(const std::string& devicePath);
void createVirtualKeyboardLocked();
void addDeviceLocked(Device* device);
void assignDescriptorLocked(InputDeviceIdentifier& identifier);
- void closeDeviceByPathLocked(const char* devicePath);
+ void closeDeviceByPathLocked(const std::string& devicePath);
void closeVideoDeviceByPathLocked(const std::string& devicePath);
void closeDeviceLocked(Device* device);
void closeAllDevicesLocked();
@@ -396,14 +480,14 @@
status_t unregisterDeviceFromEpollLocked(Device* device);
void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
- status_t scanDirLocked(const char* dirname);
+ status_t scanDirLocked(const std::string& dirname);
status_t scanVideoDirLocked(const std::string& dirname);
void scanDevicesLocked();
status_t readNotifyLocked();
Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
Device* getDeviceLocked(int32_t deviceId) const;
- Device* getDeviceByPathLocked(const char* devicePath) const;
+ Device* getDeviceByPathLocked(const std::string& devicePath) const;
/**
* Look through all available fd's (both for input devices and for video devices),
* and return the device pointer.
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 71313fc..6cb86bd 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -81,7 +81,7 @@
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags);
- void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token);
void cancelVibrate(int32_t token);
void cancelTouch(nsecs_t when);
@@ -262,7 +262,9 @@
inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
return mEventHub->setKeyboardLayoutOverlay(mId, map);
}
- inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); }
+ inline void vibrate(const VibrationElement& element) {
+ return mEventHub->vibrate(mId, element);
+ }
inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); }
inline bool hasAbsoluteAxis(int32_t code) const {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 693ec30..9cb2052 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -17,19 +17,20 @@
#ifndef _UI_INPUTREADER_INPUT_READER_H
#define _UI_INPUTREADER_INPUT_READER_H
+#include <PointerControllerInterface.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
#include "EventHub.h"
#include "InputListener.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
#include "InputThread.h"
-#include <PointerControllerInterface.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-
-#include <unordered_map>
-#include <vector>
-
namespace android {
class InputDevice;
@@ -77,7 +78,7 @@
virtual void requestRefreshConfiguration(uint32_t changes) override;
- virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
+ virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
ssize_t repeat, int32_t token) override;
virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
@@ -104,7 +105,8 @@
virtual void disableVirtualKeysUntil(nsecs_t time) override;
virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
virtual void fadePointer() override;
- virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) override;
+ virtual std::shared_ptr<PointerControllerInterface> getPointerController(
+ int32_t deviceId) override;
virtual void requestTimeoutAtTime(nsecs_t when) override;
virtual int32_t bumpGeneration() override;
virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
@@ -160,8 +162,8 @@
void dispatchExternalStylusState(const StylusState& state);
// The PointerController that is shared among all the input devices that need it.
- wp<PointerControllerInterface> mPointerController;
- sp<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
+ std::weak_ptr<PointerControllerInterface> mPointerController;
+ std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
void updatePointerDisplayLocked();
void fadePointerLocked();
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 85701e4..ffb8d8c 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -46,7 +46,7 @@
virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0;
virtual void fadePointer() = 0;
- virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
+ virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
virtual void requestTimeoutAtTime(nsecs_t when) = 0;
virtual int32_t bumpGeneration() = 0;
diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
index 5a32443..7de9b83 100644
--- a/services/inputflinger/reader/include/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -102,7 +102,7 @@
* How many buffers to keep for the internal queue. When the internal buffer
* exceeds this capacity, oldest frames will be dropped.
*/
- static constexpr size_t MAX_QUEUE_SIZE = 10;
+ static constexpr size_t MAX_QUEUE_SIZE = 20;
std::vector<TouchVideoFrame> mFrames;
/**
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 887ab53..d1f20c7 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "CursorInputMapper.h"
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
+#include "PointerControllerInterface.h"
#include "TouchCursorInputMapperCommon.h"
namespace android {
@@ -154,7 +157,7 @@
mParameters.mode = Parameters::MODE_POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
// Keep PointerController around in order to preserve the pointer position.
- mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
} else {
ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
}
@@ -183,7 +186,7 @@
mOrientation = DISPLAY_ORIENTATION_0;
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
}
@@ -316,7 +319,7 @@
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mSource == AINPUT_SOURCE_MOUSE) {
if (moved || scrolled || buttonsChanged) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (moved) {
mPointerController->move(deltaX, deltaY);
@@ -326,7 +329,7 @@
mPointerController->setButtonState(currentButtonState);
}
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index f65ac39..05bbb26 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -106,7 +106,7 @@
int32_t mOrientation;
- sp<PointerControllerInterface> mPointerController;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
int32_t mButtonState;
nsecs_t mDownTime;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index a8fe39a..1db829f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -56,7 +56,7 @@
return false;
}
-void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void InputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) {}
void InputMapper::cancelVibrate(int32_t token) {}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 949c7ea..d9fc5cc 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -22,6 +22,7 @@
#include "InputListener.h"
#include "InputReaderContext.h"
#include "StylusState.h"
+#include "VibrationElement.h"
namespace android {
@@ -62,7 +63,8 @@
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
+ virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
+ int32_t token);
virtual void cancelVibrate(int32_t token);
virtual void cancelTouch(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 030a846..bf81496 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -32,8 +32,8 @@
void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- for (size_t i = 0; i < mAxes.size(); i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
addMotionRange(axis.axisInfo.axis, axis, info);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
@@ -72,9 +72,7 @@
dump += INDENT2 "Joystick Input Mapper:\n";
dump += INDENT3 "Axes:\n";
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (const auto& [rawAxis, axis] : mAxes) {
const char* label = getAxisLabel(axis.axisInfo.axis);
if (label) {
dump += StringPrintf(INDENT4 "%s", label);
@@ -100,7 +98,7 @@
axis.scale, axis.offset, axis.highScale, axis.highOffset);
dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, "
"rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
- mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+ rawAxis, axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
axis.rawAxisInfo.resolution);
}
@@ -123,43 +121,14 @@
if (rawAxisInfo.valid) {
// Map axis.
AxisInfo axisInfo;
- bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+ const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
+
if (!explicitlyMapped) {
// Axis is not explicitly mapped, will choose a generic axis later.
axisInfo.mode = AxisInfo::MODE_NORMAL;
axisInfo.axis = -1;
}
-
- // Apply flat override.
- int32_t rawFlat =
- axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
-
- // Calculate scaling factors and limits.
- Axis axis;
- if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
- float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
- float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale,
- 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else if (isCenteredAxis(axisInfo.axis)) {
- float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale,
- offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- } else {
- float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
- axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale,
- 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
- rawAxisInfo.resolution * scale);
- }
-
- // To eliminate noise while the joystick is at rest, filter out small variations
- // in axis values up front.
- axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f;
-
- mAxes.add(abs, axis);
+ mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
}
}
@@ -174,9 +143,8 @@
// Assign generic axis ids to remaining axes.
int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) {
+ Axis& axis = it->second;
if (axis.axisInfo.axis < 0) {
while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
haveAxis(nextGenericAxisId)) {
@@ -189,19 +157,57 @@
} else {
ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
"have already been assigned to other axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i--);
- numAxes -= 1;
+ getDeviceName().c_str(), it->first);
+ it = mAxes.erase(it);
+ continue;
}
}
+ it++;
}
}
}
+JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
+ const RawAbsoluteAxisInfo& rawAxisInfo,
+ bool explicitlyMapped) {
+ // Apply flat override.
+ int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+ float scale = std::numeric_limits<float>::signaling_NaN();
+ float highScale = std::numeric_limits<float>::signaling_NaN();
+ float highOffset = 0;
+ float offset = 0;
+ float min = 0;
+ // Calculate scaling factors and limits.
+ if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+ scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+ highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+ } else if (isCenteredAxis(axisInfo.axis)) {
+ scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+ highOffset = offset;
+ highScale = scale;
+ min = -1.0f;
+ } else {
+ scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
+ highScale = scale;
+ }
+
+ constexpr float max = 1.0;
+ const float flat = rawFlat * scale;
+ const float fuzz = rawAxisInfo.fuzz * scale;
+ const float resolution = rawAxisInfo.resolution * scale;
+
+ // To eliminate noise while the joystick is at rest, filter out small variations
+ // in axis values up front.
+ const float filter = fuzz ? fuzz : flat * 0.25f;
+ return Axis(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, highScale, highOffset, min,
+ max, flat, fuzz, resolution, filter);
+}
+
bool JoystickInputMapper::haveAxis(int32_t axisId) {
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (const std::pair<int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
if (axis.axisInfo.axis == axisId ||
(axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) {
return true;
@@ -211,14 +217,14 @@
}
void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) {
- size_t i = mAxes.size();
- while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) {
- if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) {
+ while (mAxes.size() > PointerCoords::MAX_AXES) {
+ auto it = mAxes.begin();
+ if (ignoreExplicitlyMappedAxes && it->second.explicitlyMapped) {
continue;
}
ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
- getDeviceName().c_str(), mAxes.keyAt(i));
- mAxes.removeItemsAt(i);
+ getDeviceName().c_str(), it->first);
+ mAxes.erase(it);
}
}
@@ -243,9 +249,8 @@
void JoystickInputMapper::reset(nsecs_t when) {
// Recenter all axes.
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
axis.resetValue();
}
@@ -255,9 +260,9 @@
void JoystickInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_ABS: {
- ssize_t index = mAxes.indexOfKey(rawEvent->code);
- if (index >= 0) {
- Axis& axis = mAxes.editValueAt(index);
+ auto it = mAxes.find(rawEvent->code);
+ if (it != mAxes.end()) {
+ Axis& axis = it->second;
float newValue, highNewValue;
switch (axis.axisInfo.mode) {
case AxisInfo::MODE_INVERT:
@@ -317,9 +322,8 @@
PointerCoords pointerCoords;
pointerCoords.clear();
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- const Axis& axis = mAxes.valueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ const Axis& axis = pair.second;
setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis,
@@ -357,9 +361,8 @@
bool JoystickInputMapper::filterAxes(bool force) {
bool atLeastOneSignificantChange = force;
- size_t numAxes = mAxes.size();
- for (size_t i = 0; i < numAxes; i++) {
- Axis& axis = mAxes.editValueAt(i);
+ for (std::pair<const int32_t, Axis>& pair : mAxes) {
+ Axis& axis = pair.second;
if (force ||
hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min,
axis.max)) {
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 823a096..0cf60a2 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -36,6 +36,26 @@
private:
struct Axis {
+ explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+ bool explicitlyMapped, float scale, float offset, float highScale,
+ float highOffset, float min, float max, float flat, float fuzz,
+ float resolution, float filter)
+ : rawAxisInfo(rawAxisInfo),
+ axisInfo(axisInfo),
+ explicitlyMapped(explicitlyMapped),
+ scale(scale),
+ offset(offset),
+ highScale(highScale),
+ highOffset(highOffset),
+ min(min),
+ max(max),
+ flat(flat),
+ fuzz(fuzz),
+ resolution(resolution),
+ filter(filter) {
+ resetValue();
+ }
+
RawAbsoluteAxisInfo rawAxisInfo;
AxisInfo axisInfo;
@@ -58,26 +78,6 @@
float highCurrentValue; // current value of high split
float highNewValue; // most recent value of high split
- void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
- bool explicitlyMapped, float scale, float offset, float highScale,
- float highOffset, float min, float max, float flat, float fuzz,
- float resolution) {
- this->rawAxisInfo = rawAxisInfo;
- this->axisInfo = axisInfo;
- this->explicitlyMapped = explicitlyMapped;
- this->scale = scale;
- this->offset = offset;
- this->highScale = highScale;
- this->highOffset = highOffset;
- this->min = min;
- this->max = max;
- this->flat = flat;
- this->fuzz = fuzz;
- this->resolution = resolution;
- this->filter = 0;
- resetValue();
- }
-
void resetValue() {
this->currentValue = 0;
this->newValue = 0;
@@ -86,8 +86,11 @@
}
};
+ static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo,
+ bool explicitlyMapped);
+
// Axes indexed by raw ABS_* axis index.
- KeyedVector<int32_t, Axis> mAxes;
+ std::unordered_map<int32_t, Axis> mAxes;
void sync(nsecs_t when, bool force);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 7be4a58..bd4232d 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "KeyboardInputMapper.h"
@@ -138,7 +140,7 @@
// No associated display defined, try to find default display if orientationAware.
if (mParameters.orientationAware) {
- return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ return config->getDisplayViewportByType(ViewportType::INTERNAL);
}
return std::nullopt;
@@ -233,7 +235,7 @@
}
bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
- return scanCode < BTN_MOUSE || scanCode >= KEY_OK ||
+ return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
(scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
(scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 43bd9f1..0440f49 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -236,6 +236,20 @@
mMultiTouchMotionAccumulator.process(rawEvent);
}
+std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
+ const MultiTouchMotionAccumulator::Slot& inSlot) {
+ if (mHavePointerIds) {
+ int32_t trackingId = inSlot.getTrackingId();
+ for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
+ int32_t n = idBits.clearFirstMarkedBit();
+ if (mPointerTrackingIdMap[n] == trackingId) {
+ return std::make_optional(n);
+ }
+ }
+ }
+ return std::nullopt;
+}
+
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
size_t outCount = 0;
@@ -250,10 +264,9 @@
}
if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
- if (!mCurrentMotionAborted) {
- ALOGI("Canceling touch gesture from device %s because the palm event was detected",
- getDeviceName().c_str());
- cancelTouch(when);
+ std::optional<int32_t> id = getActiveBitId(*inSlot);
+ if (id) {
+ outState->rawPointerData.canceledIdBits.markBit(id.value());
}
continue;
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 89ef41d..ea6f207 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -92,17 +92,19 @@
class MultiTouchInputMapper : public TouchInputMapper {
public:
explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
- virtual ~MultiTouchInputMapper();
+ ~MultiTouchInputMapper() override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
protected:
- virtual void syncTouch(nsecs_t when, RawState* outState);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
+ void syncTouch(nsecs_t when, RawState* outState) override;
+ void configureRawPointerAxes() override;
+ bool hasStylus() const override;
private:
+ // If the slot is in use, return the bit id. Return std::nullopt otherwise.
+ std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot);
MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
// Specifies the pointer id bits that are in use, and their associated tracking id.
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 9885889..594ff42 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "RotaryEncoderInputMapper.h"
@@ -66,7 +68,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index f5befb3..9cb3f67 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -25,15 +25,15 @@
class SingleTouchInputMapper : public TouchInputMapper {
public:
explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
- virtual ~SingleTouchInputMapper();
+ ~SingleTouchInputMapper() override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
protected:
- virtual void syncTouch(nsecs_t when, RawState* outState);
- virtual void configureRawPointerAxes();
- virtual bool hasStylus() const;
+ void syncTouch(nsecs_t when, RawState* outState) override;
+ void configureRawPointerAxes() override;
+ bool hasStylus() const override;
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 2a3e263..a86443d 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,12 +17,13 @@
#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+#include <input/DisplayViewport.h>
+#include <stdint.h>
+
#include "EventHub.h"
#include "InputListener.h"
#include "InputReaderContext.h"
-#include <stdint.h>
-
namespace android {
// --- Static Definitions ---
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 99a572a..15d5288 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+// clang-format off
#include "../Macros.h"
+// clang-format on
#include "TouchInputMapper.h"
@@ -102,6 +104,7 @@
pointerCount = other.pointerCount;
hoveringIdBits = other.hoveringIdBits;
touchingIdBits = other.touchingIdBits;
+ canceledIdBits = other.canceledIdBits;
for (uint32_t i = 0; i < pointerCount; i++) {
pointers[i] = other.pointers[i];
@@ -138,6 +141,7 @@
pointerCount = 0;
hoveringIdBits.clear();
touchingIdBits.clear();
+ canceledIdBits.clear();
}
void CookedPointerData::copyFrom(const CookedPointerData& other) {
@@ -159,7 +163,7 @@
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
: InputMapper(deviceContext),
mSource(0),
- mDeviceMode(DEVICE_MODE_DISABLED),
+ mDeviceMode(DeviceMode::DISABLED),
mRawSurfaceWidth(-1),
mRawSurfaceHeight(-1),
mSurfaceLeft(0),
@@ -179,7 +183,7 @@
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- if (mDeviceMode != DEVICE_MODE_DISABLED) {
+ if (mDeviceMode != DeviceMode::DISABLED) {
info->addMotionRange(mOrientedRanges.x);
info->addMotionRange(mOrientedRanges.y);
info->addMotionRange(mOrientedRanges.pressure);
@@ -218,7 +222,7 @@
info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
0.0f);
}
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
@@ -311,7 +315,7 @@
dump += INDENT3 "External Stylus State:\n";
dumpStylusState(dump, mExternalStylusState);
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n");
dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale);
dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale);
@@ -323,15 +327,15 @@
const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
switch (deviceMode) {
- case DEVICE_MODE_DISABLED:
+ case DeviceMode::DISABLED:
return "disabled";
- case DEVICE_MODE_DIRECT:
+ case DeviceMode::DIRECT:
return "direct";
- case DEVICE_MODE_UNSCALED:
+ case DeviceMode::UNSCALED:
return "unscaled";
- case DEVICE_MODE_NAVIGATION:
+ case DeviceMode::NAVIGATION:
return "navigation";
- case DEVICE_MODE_POINTER:
+ case DeviceMode::POINTER:
return "pointer";
}
return "unknown";
@@ -406,16 +410,16 @@
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
- ? Parameters::GESTURE_MODE_SINGLE_TOUCH
- : Parameters::GESTURE_MODE_MULTI_TOUCH;
+ ? Parameters::GestureMode::SINGLE_TOUCH
+ : Parameters::GestureMode::MULTI_TOUCH;
String8 gestureModeString;
if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
gestureModeString)) {
if (gestureModeString == "single-touch") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
+ mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
} else if (gestureModeString == "multi-touch") {
- mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH;
+ mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
} else if (gestureModeString != "default") {
ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
}
@@ -423,18 +427,18 @@
if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
// The device is a touch screen.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
} else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
// The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
} else if (getDeviceContext().hasRelativeAxis(REL_X) ||
getDeviceContext().hasRelativeAxis(REL_Y)) {
// The device is a cursor device with a touch pad attached.
// By default don't use the touch pad to move the pointer.
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
} else {
// The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
}
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
@@ -443,29 +447,29 @@
if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)) {
if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
} else if (deviceTypeString == "touchPad") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
} else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
} else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
} else if (deviceTypeString != "default") {
ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
}
}
- mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
+ mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
mParameters.orientationAware);
mParameters.hasAssociatedDisplay = false;
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware ||
- mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN ||
- mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+ mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
+ mParameters.deviceType == Parameters::DeviceType::POINTER) {
mParameters.hasAssociatedDisplay = true;
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+ if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
String8 uniqueDisplayId;
getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
@@ -488,10 +492,10 @@
dump += INDENT3 "Parameters:\n";
switch (mParameters.gestureMode) {
- case Parameters::GESTURE_MODE_SINGLE_TOUCH:
+ case Parameters::GestureMode::SINGLE_TOUCH:
dump += INDENT4 "GestureMode: single-touch\n";
break;
- case Parameters::GESTURE_MODE_MULTI_TOUCH:
+ case Parameters::GestureMode::MULTI_TOUCH:
dump += INDENT4 "GestureMode: multi-touch\n";
break;
default:
@@ -499,16 +503,16 @@
}
switch (mParameters.deviceType) {
- case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+ case Parameters::DeviceType::TOUCH_SCREEN:
dump += INDENT4 "DeviceType: touchScreen\n";
break;
- case Parameters::DEVICE_TYPE_TOUCH_PAD:
+ case Parameters::DeviceType::TOUCH_PAD:
dump += INDENT4 "DeviceType: touchPad\n";
break;
- case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+ case Parameters::DeviceType::TOUCH_NAVIGATION:
dump += INDENT4 "DeviceType: touchNavigation\n";
break;
- case Parameters::DEVICE_TYPE_POINTER:
+ case Parameters::DeviceType::POINTER:
dump += INDENT4 "DeviceType: pointer\n";
break;
default:
@@ -565,7 +569,7 @@
return getDeviceContext().getAssociatedViewport();
}
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
if (viewport) {
@@ -583,18 +587,18 @@
ViewportType viewportTypeToUse;
if (mParameters.associatedDisplayIsExternal) {
- viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+ viewportTypeToUse = ViewportType::EXTERNAL;
} else {
- viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+ viewportTypeToUse = ViewportType::INTERNAL;
}
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportByType(viewportTypeToUse);
- if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) {
+ if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) {
ALOGW("Input device %s should be associated with external display, "
"fallback to internal one for the external viewport is not found.",
getDeviceName().c_str());
- viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ viewport = mConfig.getDisplayViewportByType(ViewportType::INTERNAL);
}
return viewport;
@@ -610,34 +614,34 @@
}
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
- int32_t oldDeviceMode = mDeviceMode;
+ DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
// Determine device mode.
- if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER &&
+ if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
mConfig.pointerGesturesEnabled) {
mSource = AINPUT_SOURCE_MOUSE;
- mDeviceMode = DEVICE_MODE_POINTER;
+ mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN &&
+ } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
mParameters.hasAssociatedDisplay) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
- mDeviceMode = DEVICE_MODE_DIRECT;
+ mDeviceMode = DeviceMode::DIRECT;
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
if (hasExternalStylus()) {
mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
}
- } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+ } else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_NAVIGATION;
+ mDeviceMode = DeviceMode::NAVIGATION;
} else {
mSource = AINPUT_SOURCE_TOUCHPAD;
- mDeviceMode = DEVICE_MODE_UNSCALED;
+ mDeviceMode = DeviceMode::UNSCALED;
}
// Ensure we have valid X and Y axes.
@@ -645,7 +649,7 @@
ALOGW("Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.",
getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
+ mDeviceMode = DeviceMode::DISABLED;
return;
}
@@ -656,7 +660,7 @@
"display. The device will be inoperable until the display size "
"becomes available.",
getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
+ mDeviceMode = DeviceMode::DISABLED;
return;
}
@@ -668,7 +672,7 @@
if (viewportChanged) {
mViewport = *newViewport;
- if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
// Convert rotated viewport to natural surface coordinates.
int32_t naturalLogicalWidth, naturalLogicalHeight;
int32_t naturalPhysicalWidth, naturalPhysicalHeight;
@@ -759,13 +763,13 @@
}
// Create pointer controller if needed.
- if (mDeviceMode == DEVICE_MODE_POINTER ||
- (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+ if (mDeviceMode == DeviceMode::POINTER ||
+ (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches)) {
if (mPointerController == nullptr) {
mPointerController = getContext()->getPointerController(getDeviceId());
}
} else {
- mPointerController.clear();
+ mPointerController.reset();
}
if (viewportChanged || deviceModeChanged) {
@@ -798,7 +802,7 @@
float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
// Size factors.
- if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+ if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) {
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
} else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
@@ -847,8 +851,8 @@
// Pressure factors.
mPressureScale = 0;
float pressureMax = 1.0;
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL ||
- mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+ if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL ||
+ mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
if (mCalibration.havePressureScale) {
mPressureScale = mCalibration.pressureScale;
pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
@@ -901,9 +905,9 @@
mOrientedRanges.orientation.fuzz = 0;
mOrientedRanges.orientation.resolution = 0;
} else if (mCalibration.orientationCalibration !=
- Calibration::ORIENTATION_CALIBRATION_NONE) {
+ Calibration::OrientationCalibration::NONE) {
if (mCalibration.orientationCalibration ==
- Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+ Calibration::OrientationCalibration::INTERPOLATED) {
if (mRawPointerAxes.orientation.valid) {
if (mRawPointerAxes.orientation.maxValue > 0) {
mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
@@ -928,8 +932,8 @@
// Distance
mDistanceScale = 0;
- if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
- if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) {
+ if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) {
+ if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) {
if (mCalibration.haveDistanceScale) {
mDistanceScale = mCalibration.distanceScale;
} else {
@@ -991,7 +995,7 @@
// Location
updateAffineTransformation();
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
// Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
@@ -1112,19 +1116,19 @@
Calibration& out = mCalibration;
// Size
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+ out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
String8 sizeCalibrationString;
if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
if (sizeCalibrationString == "none") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ out.sizeCalibration = Calibration::SizeCalibration::NONE;
} else if (sizeCalibrationString == "geometric") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+ out.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
} else if (sizeCalibrationString == "diameter") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER;
+ out.sizeCalibration = Calibration::SizeCalibration::DIAMETER;
} else if (sizeCalibrationString == "box") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX;
+ out.sizeCalibration = Calibration::SizeCalibration::BOX;
} else if (sizeCalibrationString == "area") {
- out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA;
+ out.sizeCalibration = Calibration::SizeCalibration::AREA;
} else if (sizeCalibrationString != "default") {
ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
}
@@ -1135,15 +1139,15 @@
out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
// Pressure
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+ out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
String8 pressureCalibrationString;
if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
if (pressureCalibrationString == "none") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ out.pressureCalibration = Calibration::PressureCalibration::NONE;
} else if (pressureCalibrationString == "physical") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ out.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
} else if (pressureCalibrationString == "amplitude") {
- out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
} else if (pressureCalibrationString != "default") {
ALOGW("Invalid value for touch.pressure.calibration: '%s'",
pressureCalibrationString.string());
@@ -1153,15 +1157,15 @@
out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
// Orientation
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+ out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
String8 orientationCalibrationString;
if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
if (orientationCalibrationString == "none") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ out.orientationCalibration = Calibration::OrientationCalibration::NONE;
} else if (orientationCalibrationString == "interpolated") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ out.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
} else if (orientationCalibrationString == "vector") {
- out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR;
+ out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
} else if (orientationCalibrationString != "default") {
ALOGW("Invalid value for touch.orientation.calibration: '%s'",
orientationCalibrationString.string());
@@ -1169,13 +1173,13 @@
}
// Distance
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
+ out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
String8 distanceCalibrationString;
if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
if (distanceCalibrationString == "none") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+ out.distanceCalibration = Calibration::DistanceCalibration::NONE;
} else if (distanceCalibrationString == "scaled") {
- out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+ out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
} else if (distanceCalibrationString != "default") {
ALOGW("Invalid value for touch.distance.calibration: '%s'",
distanceCalibrationString.string());
@@ -1184,13 +1188,13 @@
out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT;
+ out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT;
String8 coverageCalibrationString;
if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
if (coverageCalibrationString == "none") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+ out.coverageCalibration = Calibration::CoverageCalibration::NONE;
} else if (coverageCalibrationString == "box") {
- out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX;
+ out.coverageCalibration = Calibration::CoverageCalibration::BOX;
} else if (coverageCalibrationString != "default") {
ALOGW("Invalid value for touch.coverage.calibration: '%s'",
coverageCalibrationString.string());
@@ -1201,43 +1205,43 @@
void TouchInputMapper::resolveCalibration() {
// Size
if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC;
+ if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) {
+ mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
}
} else {
- mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ mCalibration.sizeCalibration = Calibration::SizeCalibration::NONE;
}
// Pressure
if (mRawPointerAxes.pressure.valid) {
- if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) {
+ mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
}
} else {
- mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ mCalibration.pressureCalibration = Calibration::PressureCalibration::NONE;
}
// Orientation
if (mRawPointerAxes.orientation.valid) {
- if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) {
+ mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
}
} else {
- mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ mCalibration.orientationCalibration = Calibration::OrientationCalibration::NONE;
}
// Distance
if (mRawPointerAxes.distance.valid) {
- if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+ if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) {
+ mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED;
}
} else {
- mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+ mCalibration.distanceCalibration = Calibration::DistanceCalibration::NONE;
}
// Coverage
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) {
- mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE;
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::DEFAULT) {
+ mCalibration.coverageCalibration = Calibration::CoverageCalibration::NONE;
}
}
@@ -1246,19 +1250,19 @@
// Size
switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_NONE:
+ case Calibration::SizeCalibration::NONE:
dump += INDENT4 "touch.size.calibration: none\n";
break;
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
+ case Calibration::SizeCalibration::GEOMETRIC:
dump += INDENT4 "touch.size.calibration: geometric\n";
break;
- case Calibration::SIZE_CALIBRATION_DIAMETER:
+ case Calibration::SizeCalibration::DIAMETER:
dump += INDENT4 "touch.size.calibration: diameter\n";
break;
- case Calibration::SIZE_CALIBRATION_BOX:
+ case Calibration::SizeCalibration::BOX:
dump += INDENT4 "touch.size.calibration: box\n";
break;
- case Calibration::SIZE_CALIBRATION_AREA:
+ case Calibration::SizeCalibration::AREA:
dump += INDENT4 "touch.size.calibration: area\n";
break;
default:
@@ -1280,13 +1284,13 @@
// Pressure
switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_NONE:
+ case Calibration::PressureCalibration::NONE:
dump += INDENT4 "touch.pressure.calibration: none\n";
break;
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ case Calibration::PressureCalibration::PHYSICAL:
dump += INDENT4 "touch.pressure.calibration: physical\n";
break;
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ case Calibration::PressureCalibration::AMPLITUDE:
dump += INDENT4 "touch.pressure.calibration: amplitude\n";
break;
default:
@@ -1299,13 +1303,13 @@
// Orientation
switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_NONE:
+ case Calibration::OrientationCalibration::NONE:
dump += INDENT4 "touch.orientation.calibration: none\n";
break;
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ case Calibration::OrientationCalibration::INTERPOLATED:
dump += INDENT4 "touch.orientation.calibration: interpolated\n";
break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR:
+ case Calibration::OrientationCalibration::VECTOR:
dump += INDENT4 "touch.orientation.calibration: vector\n";
break;
default:
@@ -1314,10 +1318,10 @@
// Distance
switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_NONE:
+ case Calibration::DistanceCalibration::NONE:
dump += INDENT4 "touch.distance.calibration: none\n";
break;
- case Calibration::DISTANCE_CALIBRATION_SCALED:
+ case Calibration::DistanceCalibration::SCALED:
dump += INDENT4 "touch.distance.calibration: scaled\n";
break;
default:
@@ -1329,10 +1333,10 @@
}
switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_NONE:
+ case Calibration::CoverageCalibration::NONE:
dump += INDENT4 "touch.coverage.calibration: none\n";
break;
- case Calibration::COVERAGE_CALIBRATION_BOX:
+ case Calibration::CoverageCalibration::BOX:
dump += INDENT4 "touch.coverage.calibration: box\n";
break;
default:
@@ -1370,7 +1374,7 @@
mCurrentCookedState.clear();
mLastRawState.clear();
mLastCookedState.clear();
- mPointerUsage = POINTER_USAGE_NONE;
+ mPointerUsage = PointerUsage::NONE;
mSentHoverEnter = false;
mHavePointerIds = false;
mCurrentMotionAborted = false;
@@ -1383,7 +1387,7 @@
resetExternalStylus();
if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->clearSpots();
}
@@ -1442,17 +1446,18 @@
#if DEBUG_RAW_EVENTS
ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
- "hovering ids 0x%08x -> 0x%08x",
+ "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
- last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value);
+ last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value,
+ next->rawPointerData.canceledIdBits.value);
#endif
processRawTouches(false /*timeout*/);
}
void TouchInputMapper::processRawTouches(bool timeout) {
- if (mDeviceMode == DEVICE_MODE_DISABLED) {
+ if (mDeviceMode == DeviceMode::DISABLED) {
// Drop all input if the device is disabled.
mCurrentRawState.clear();
mRawStatesPending.clear();
@@ -1517,7 +1522,7 @@
bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
if (initialDown || buttonsPressed) {
// If this is a touch screen, hide the pointer on an initial down.
- if (mDeviceMode == DEVICE_MODE_DIRECT) {
+ if (mDeviceMode == DeviceMode::DIRECT) {
getContext()->fadePointer();
}
@@ -1546,7 +1551,7 @@
mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
@@ -1576,21 +1581,21 @@
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
mCurrentCookedState.mouseIdBits.clear();
mCurrentCookedState.fingerIdBits.clear();
- pointerUsage = POINTER_USAGE_STYLUS;
+ pointerUsage = PointerUsage::STYLUS;
} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
mCurrentCookedState.fingerIdBits.clear();
- pointerUsage = POINTER_USAGE_MOUSE;
+ pointerUsage = PointerUsage::MOUSE;
} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
isPointerDown(mCurrentRawState.buttonState)) {
- pointerUsage = POINTER_USAGE_GESTURES;
+ pointerUsage = PointerUsage::GESTURES;
}
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
- if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
+ if (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches &&
mPointerController != nullptr) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -1627,7 +1632,7 @@
}
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
+ if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
mCurrentRawState.buttonState |= mExternalStylusState.buttons;
}
}
@@ -1654,7 +1659,7 @@
}
bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) {
- if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) {
+ if (mDeviceMode != DeviceMode::DIRECT || !hasExternalStylus()) {
return false;
}
@@ -1697,11 +1702,11 @@
}
void TouchInputMapper::timeoutExpired(nsecs_t when) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
- if (mPointerUsage == POINTER_USAGE_GESTURES) {
+ if (mDeviceMode == DeviceMode::POINTER) {
+ if (mPointerUsage == PointerUsage::GESTURES) {
dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
}
- } else if (mDeviceMode == DEVICE_MODE_DIRECT) {
+ } else if (mDeviceMode == DeviceMode::DIRECT) {
if (mExternalStylusFusionTimeout < when) {
processRawTouches(true /*timeout*/);
} else if (mExternalStylusFusionTimeout != LLONG_MAX) {
@@ -1890,14 +1895,15 @@
// Dispatch pointer up events.
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
-
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
- metaState, buttonState, 0,
+ bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);
+ dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
dispatchedIdBits.clearBit(upId);
+ mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
}
// Dispatch move events if any of the remaining pointers moved from their old locations.
@@ -2023,6 +2029,8 @@
mCurrentRawState.rawPointerData.hoveringIdBits;
mCurrentCookedState.cookedPointerData.touchingIdBits =
mCurrentRawState.rawPointerData.touchingIdBits;
+ mCurrentCookedState.cookedPointerData.canceledIdBits =
+ mCurrentRawState.rawPointerData.canceledIdBits;
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
mCurrentCookedState.buttonState = 0;
@@ -2038,10 +2046,10 @@
// Size
float touchMajor, touchMinor, toolMajor, toolMinor, size;
switch (mCalibration.sizeCalibration) {
- case Calibration::SIZE_CALIBRATION_GEOMETRIC:
- case Calibration::SIZE_CALIBRATION_DIAMETER:
- case Calibration::SIZE_CALIBRATION_BOX:
- case Calibration::SIZE_CALIBRATION_AREA:
+ case Calibration::SizeCalibration::GEOMETRIC:
+ case Calibration::SizeCalibration::DIAMETER:
+ case Calibration::SizeCalibration::BOX:
+ case Calibration::SizeCalibration::AREA:
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
touchMajor = in.touchMajor;
touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
@@ -2083,17 +2091,17 @@
}
}
- if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) {
+ if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
touchMajor *= mGeometricScale;
touchMinor *= mGeometricScale;
toolMajor *= mGeometricScale;
toolMinor *= mGeometricScale;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) {
+ } else if (mCalibration.sizeCalibration == Calibration::SizeCalibration::AREA) {
touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0;
touchMinor = touchMajor;
toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0;
toolMinor = toolMajor;
- } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) {
+ } else if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DIAMETER) {
touchMinor = touchMajor;
toolMinor = toolMajor;
}
@@ -2116,8 +2124,8 @@
// Pressure
float pressure;
switch (mCalibration.pressureCalibration) {
- case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ case Calibration::PressureCalibration::PHYSICAL:
+ case Calibration::PressureCalibration::AMPLITUDE:
pressure = in.pressure * mPressureScale;
break;
default:
@@ -2137,10 +2145,10 @@
tilt = 0;
switch (mCalibration.orientationCalibration) {
- case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ case Calibration::OrientationCalibration::INTERPOLATED:
orientation = in.orientation * mOrientationScale;
break;
- case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
+ case Calibration::OrientationCalibration::VECTOR: {
int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
int32_t c2 = signExtendNybble(in.orientation & 0x0f);
if (c1 != 0 || c2 != 0) {
@@ -2164,7 +2172,7 @@
// Distance
float distance;
switch (mCalibration.distanceCalibration) {
- case Calibration::DISTANCE_CALIBRATION_SCALED:
+ case Calibration::DistanceCalibration::SCALED:
distance = in.distance * mDistanceScale;
break;
default:
@@ -2174,7 +2182,7 @@
// Coverage
int32_t rawLeft, rawTop, rawRight, rawBottom;
switch (mCalibration.coverageCalibration) {
- case Calibration::COVERAGE_CALIBRATION_BOX:
+ case Calibration::CoverageCalibration::BOX:
rawLeft = (in.toolMinor & 0xffff0000) >> 16;
rawRight = in.toolMinor & 0x0000ffff;
rawBottom = in.toolMajor & 0x0000ffff;
@@ -2251,7 +2259,7 @@
out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);
out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
- if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) {
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);
out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);
@@ -2281,36 +2289,36 @@
}
switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
+ case PointerUsage::GESTURES:
dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
break;
- case POINTER_USAGE_STYLUS:
+ case PointerUsage::STYLUS:
dispatchPointerStylus(when, policyFlags);
break;
- case POINTER_USAGE_MOUSE:
+ case PointerUsage::MOUSE:
dispatchPointerMouse(when, policyFlags);
break;
- default:
+ case PointerUsage::NONE:
break;
}
}
void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
switch (mPointerUsage) {
- case POINTER_USAGE_GESTURES:
+ case PointerUsage::GESTURES:
abortPointerGestures(when, policyFlags);
break;
- case POINTER_USAGE_STYLUS:
+ case PointerUsage::STYLUS:
abortPointerStylus(when, policyFlags);
break;
- case POINTER_USAGE_MOUSE:
+ case PointerUsage::MOUSE:
abortPointerMouse(when, policyFlags);
break;
- default:
+ case PointerUsage::NONE:
break;
}
- mPointerUsage = POINTER_USAGE_NONE;
+ mPointerUsage = PointerUsage::NONE;
}
void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
@@ -2326,49 +2334,49 @@
}
// Update the pointer presentation and spots.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (finishPreviousGesture || cancelPreviousGesture) {
mPointerController->clearSpots();
}
- if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
mPointerController->setSpots(mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
mPointerGesture.currentGestureIdBits,
mPointerController->getDisplayId());
}
} else {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
}
// Show or hide the pointer if needed.
switch (mPointerGesture.currentGestureMode) {
- case PointerGesture::NEUTRAL:
- case PointerGesture::QUIET:
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH &&
- mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) {
+ case PointerGesture::Mode::NEUTRAL:
+ case PointerGesture::Mode::QUIET:
+ if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH &&
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) {
// Remind the user of where the pointer is after finishing a gesture with spots.
- mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->unfade(PointerControllerInterface::Transition::GRADUAL);
}
break;
- case PointerGesture::TAP:
- case PointerGesture::TAP_DRAG:
- case PointerGesture::BUTTON_CLICK_OR_DRAG:
- case PointerGesture::HOVER:
- case PointerGesture::PRESS:
- case PointerGesture::SWIPE:
+ case PointerGesture::Mode::TAP:
+ case PointerGesture::Mode::TAP_DRAG:
+ case PointerGesture::Mode::BUTTON_CLICK_OR_DRAG:
+ case PointerGesture::Mode::HOVER:
+ case PointerGesture::Mode::PRESS:
+ case PointerGesture::Mode::SWIPE:
// Unfade the pointer when the current gesture manipulates the
// area directly under the pointer.
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
break;
- case PointerGesture::FREEFORM:
+ case PointerGesture::Mode::FREEFORM:
// Fade the pointer when the current gesture manipulates a different
// area and there are spots to guide the user experience.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
} else {
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
break;
}
@@ -2379,12 +2387,12 @@
// Update last coordinates of pointers that have moved so that we observe the new
// pointer positions at the same time as other pointers that have just gone up.
- bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP ||
- mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG ||
- mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG ||
- mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
- mPointerGesture.currentGestureMode == PointerGesture::SWIPE ||
- mPointerGesture.currentGestureMode == PointerGesture::FREEFORM;
+ bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM;
bool moveNeeded = false;
if (down && !cancelPreviousGesture && !finishPreviousGesture &&
!mPointerGesture.lastGestureIdBits.isEmpty() &&
@@ -2467,7 +2475,7 @@
}
// Send motion events for hover.
- if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
@@ -2537,7 +2545,7 @@
// Remove any current spots.
if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->clearSpots();
}
}
@@ -2553,7 +2561,7 @@
ALOGD("Gestures: Processing timeout");
#endif
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
// The tap/drag timeout has not yet expired.
getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime +
@@ -2566,7 +2574,7 @@
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
mPointerVelocityControl.reset();
@@ -2598,9 +2606,9 @@
// If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
// to NEUTRAL, then we should not generate tap event.
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER &&
- mPointerGesture.lastGestureMode != PointerGesture::TAP &&
- mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::TAP &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::TAP_DRAG) {
mPointerGesture.resetTap();
}
@@ -2633,15 +2641,16 @@
} else {
isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
if (!isQuietTime) {
- if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS ||
- mPointerGesture.lastGestureMode == PointerGesture::SWIPE ||
- mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) &&
+ if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
currentFingerCount < 2) {
// Enter quiet time when exiting swipe or freeform state.
// This is to prevent accidentally entering the hover state and flinging the
// pointer when finishing a swipe and there is still one pointer left onscreen.
isQuietTime = true;
- } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG &&
+ } else if (mPointerGesture.lastGestureMode ==
+ PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
// Enter quiet time when releasing the button and there are still two or more
// fingers down. This may indicate that one finger was used to press the button
@@ -2661,12 +2670,12 @@
ALOGD("Gestures: QUIET for next %0.3fms",
(mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
*outFinishPreviousGesture = true;
}
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::QUIET;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::QUIET;
mPointerGesture.currentGestureIdBits.clear();
mPointerVelocityControl.reset();
@@ -2690,7 +2699,7 @@
activeTouchId, currentFingerCount);
#endif
// Reset state when just starting.
- if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) {
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = 0;
}
@@ -2744,7 +2753,7 @@
float x, y;
mPointerController->getPosition(&x, &y);
- mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -2757,15 +2766,15 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
} else if (currentFingerCount == 0) {
// Case 3. No fingers down and button is not pressed. (NEUTRAL)
- if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::NEUTRAL) {
*outFinishPreviousGesture = true;
}
// Watch for taps coming out of HOVER or TAP_DRAG mode.
// Checking for taps after TAP_DRAG allows us to detect double-taps.
bool tapped = false;
- if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER ||
- mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) &&
+ if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::HOVER ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
float x, y;
@@ -2781,7 +2790,7 @@
mConfig.pointerGestureTapDragInterval);
mPointerGesture.activeGestureId = 0;
- mPointerGesture.currentGestureMode = PointerGesture::TAP;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP;
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -2824,7 +2833,7 @@
ALOGD("Gestures: NEUTRAL");
#endif
mPointerGesture.activeGestureId = -1;
- mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
}
} else if (currentFingerCount == 1) {
@@ -2834,14 +2843,14 @@
// When in TAP_DRAG, emit MOVE events at the pointer location.
ALOG_ASSERT(activeTouchId >= 0);
- mPointerGesture.currentGestureMode = PointerGesture::HOVER;
- if (mPointerGesture.lastGestureMode == PointerGesture::TAP) {
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
+ if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
float x, y;
mPointerController->getPosition(&x, &y);
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
} else {
#if DEBUG_GESTURES
ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
@@ -2854,8 +2863,8 @@
(when - mPointerGesture.tapUpTime) * 0.000001f);
#endif
}
- } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) {
- mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) {
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
}
float deltaX = 0, deltaY = 0;
@@ -2878,7 +2887,7 @@
}
bool down;
- if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
#if DEBUG_GESTURES
ALOGD("Gestures: TAP_DRAG");
#endif
@@ -2887,7 +2896,7 @@
#if DEBUG_GESTURES
ALOGD("Gestures: HOVER");
#endif
- if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
*outFinishPreviousGesture = true;
}
mPointerGesture.activeGestureId = 0;
@@ -2933,9 +2942,9 @@
bool settled = when >=
mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
- if (mPointerGesture.lastGestureMode != PointerGesture::PRESS &&
- mPointerGesture.lastGestureMode != PointerGesture::SWIPE &&
- mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
*outFinishPreviousGesture = true;
} else if (!settled && currentFingerCount > lastFingerCount) {
// Additional pointers have gone down but not yet settled.
@@ -2953,7 +2962,7 @@
}
if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::PRESS;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
mPointerGesture.activeGestureId = 0;
mPointerGesture.referenceIdBits.clear();
mPointerVelocityControl.reset();
@@ -3005,7 +3014,7 @@
}
// Consider transitions from PRESS to SWIPE or MULTITOUCH.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
float dist[MAX_POINTER_ID + 1];
int32_t distOverThreshold = 0;
for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
@@ -3027,7 +3036,7 @@
currentFingerCount);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
// There are exactly two pointers.
BitSet32 idBits(mCurrentCookedState.fingerIdBits);
@@ -3046,7 +3055,7 @@
mutualDistance, mPointerGestureMaxSwipeWidth);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
// There are two pointers. Wait for both pointers to start moving
// before deciding whether this is a SWIPE or FREEFORM gesture.
@@ -3077,7 +3086,7 @@
mConfig.pointerGestureMultitouchMinDistance, cosine,
mConfig.pointerGestureSwipeTransitionAngleCosine);
#endif
- mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
} else {
// Pointers are moving in different directions. Switch to FREEFORM.
#if DEBUG_GESTURES
@@ -3089,13 +3098,13 @@
mConfig.pointerGestureSwipeTransitionAngleCosine);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
}
}
}
}
- } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
// Switch from SWIPE to FREEFORM if additional pointers go down.
// Cancel previous gesture.
if (currentFingerCount > 2) {
@@ -3104,13 +3113,13 @@
currentFingerCount);
#endif
*outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
}
// Move the reference points based on the overall group motion of the fingers
// except in PRESS mode while waiting for a transition to occur.
- if (mPointerGesture.currentGestureMode != PointerGesture::PRESS &&
+ if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
(commonDeltaX || commonDeltaY)) {
for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
@@ -3133,8 +3142,8 @@
}
// Report gestures.
- if (mPointerGesture.currentGestureMode == PointerGesture::PRESS ||
- mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
// PRESS or SWIPE mode.
#if DEBUG_GESTURES
ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
@@ -3155,7 +3164,7 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
mPointerGesture.referenceGestureY);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
// FREEFORM mode.
#if DEBUG_GESTURES
ALOGD("Gestures: FREEFORM activeTouchId=%d,"
@@ -3168,7 +3177,7 @@
BitSet32 mappedTouchIdBits;
BitSet32 usedGestureIdBits;
- if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
// Initially, assign the active gesture id to the active touch point
// if there is one. No other touch id bits are mapped yet.
if (!*outCancelPreviousGesture) {
@@ -3396,12 +3405,12 @@
int32_t displayId = mViewport.displayId;
if (down || hovering) {
- mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
mPointerController->clearSpots();
mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
} else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
- mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
}
displayId = mPointerController->getDisplayId();
@@ -3560,7 +3569,11 @@
if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
action = AMOTION_EVENT_ACTION_DOWN;
} else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
- action = AMOTION_EVENT_ACTION_UP;
+ if ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) {
+ action = AMOTION_EVENT_ACTION_CANCEL;
+ } else {
+ action = AMOTION_EVENT_ACTION_UP;
+ }
} else {
// Can't happen.
ALOG_ASSERT(false);
@@ -3568,7 +3581,7 @@
}
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
}
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
@@ -3907,7 +3920,7 @@
std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
if (mParameters.hasAssociatedDisplay) {
- if (mDeviceMode == DEVICE_MODE_POINTER) {
+ if (mDeviceMode == DeviceMode::POINTER) {
return std::make_optional(mPointerController->getDisplayId());
} else {
return std::make_optional(mViewport.displayId);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 58bfc5c..94486a6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,8 @@
#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#include <stdint.h>
+
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "EventHub.h"
@@ -24,8 +26,6 @@
#include "InputReaderBase.h"
#include "TouchButtonAccumulator.h"
-#include <stdint.h>
-
namespace android {
/* Raw axis information from the driver. */
@@ -71,7 +71,7 @@
uint32_t pointerCount;
Pointer pointers[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
+ BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
uint32_t idToIndex[MAX_POINTER_ID + 1];
RawPointerData();
@@ -90,6 +90,7 @@
inline void clearIdBits() {
hoveringIdBits.clear();
touchingIdBits.clear();
+ canceledIdBits.clear();
}
inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; }
@@ -102,7 +103,7 @@
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits;
+ BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
uint32_t idToIndex[MAX_POINTER_ID + 1];
CookedPointerData();
@@ -133,25 +134,24 @@
class TouchInputMapper : public InputMapper {
public:
explicit TouchInputMapper(InputDeviceContext& deviceContext);
- virtual ~TouchInputMapper();
+ ~TouchInputMapper() override;
- virtual uint32_t getSources() override;
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
- virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ uint32_t getSources() override;
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void dump(std::string& dump) override;
+ void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
+ void reset(nsecs_t when) override;
+ void process(const RawEvent* rawEvent) override;
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) override;
+ int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+ int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+ bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override;
- virtual void cancelTouch(nsecs_t when) override;
- virtual void timeoutExpired(nsecs_t when) override;
- virtual void updateExternalStylusState(const StylusState& state) override;
- virtual std::optional<int32_t> getAssociatedDisplayId() override;
+ void cancelTouch(nsecs_t when) override;
+ void timeoutExpired(nsecs_t when) override;
+ void updateExternalStylusState(const StylusState& state) override;
+ std::optional<int32_t> getAssociatedDisplayId() override;
protected:
CursorButtonAccumulator mCursorButtonAccumulator;
@@ -177,12 +177,12 @@
// Input sources and device mode.
uint32_t mSource;
- enum DeviceMode {
- DEVICE_MODE_DISABLED, // input is disabled
- DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
- DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
- DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- DEVICE_MODE_POINTER, // pointer mapping (pointer)
+ enum class DeviceMode {
+ DISABLED, // input is disabled
+ DIRECT, // direct mapping (touchscreen)
+ UNSCALED, // unscaled mapping (touchpad)
+ NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
+ POINTER, // pointer mapping (pointer)
};
DeviceMode mDeviceMode;
@@ -191,11 +191,11 @@
// Immutable configuration parameters.
struct Parameters {
- enum DeviceType {
- DEVICE_TYPE_TOUCH_SCREEN,
- DEVICE_TYPE_TOUCH_PAD,
- DEVICE_TYPE_TOUCH_NAVIGATION,
- DEVICE_TYPE_POINTER,
+ enum class DeviceType {
+ TOUCH_SCREEN,
+ TOUCH_PAD,
+ TOUCH_NAVIGATION,
+ POINTER,
};
DeviceType deviceType;
@@ -205,9 +205,9 @@
bool hasButtonUnderPad;
std::string uniqueDisplayId;
- enum GestureMode {
- GESTURE_MODE_SINGLE_TOUCH,
- GESTURE_MODE_MULTI_TOUCH,
+ enum class GestureMode {
+ SINGLE_TOUCH,
+ MULTI_TOUCH,
};
GestureMode gestureMode;
@@ -217,13 +217,13 @@
// Immutable calibration parameters in parsed form.
struct Calibration {
// Size
- enum SizeCalibration {
- SIZE_CALIBRATION_DEFAULT,
- SIZE_CALIBRATION_NONE,
- SIZE_CALIBRATION_GEOMETRIC,
- SIZE_CALIBRATION_DIAMETER,
- SIZE_CALIBRATION_BOX,
- SIZE_CALIBRATION_AREA,
+ enum class SizeCalibration {
+ DEFAULT,
+ NONE,
+ GEOMETRIC,
+ DIAMETER,
+ BOX,
+ AREA,
};
SizeCalibration sizeCalibration;
@@ -236,11 +236,11 @@
bool sizeIsSummed;
// Pressure
- enum PressureCalibration {
- PRESSURE_CALIBRATION_DEFAULT,
- PRESSURE_CALIBRATION_NONE,
- PRESSURE_CALIBRATION_PHYSICAL,
- PRESSURE_CALIBRATION_AMPLITUDE,
+ enum class PressureCalibration {
+ DEFAULT,
+ NONE,
+ PHYSICAL,
+ AMPLITUDE,
};
PressureCalibration pressureCalibration;
@@ -248,30 +248,30 @@
float pressureScale;
// Orientation
- enum OrientationCalibration {
- ORIENTATION_CALIBRATION_DEFAULT,
- ORIENTATION_CALIBRATION_NONE,
- ORIENTATION_CALIBRATION_INTERPOLATED,
- ORIENTATION_CALIBRATION_VECTOR,
+ enum class OrientationCalibration {
+ DEFAULT,
+ NONE,
+ INTERPOLATED,
+ VECTOR,
};
OrientationCalibration orientationCalibration;
// Distance
- enum DistanceCalibration {
- DISTANCE_CALIBRATION_DEFAULT,
- DISTANCE_CALIBRATION_NONE,
- DISTANCE_CALIBRATION_SCALED,
+ enum class DistanceCalibration {
+ DEFAULT,
+ NONE,
+ SCALED,
};
DistanceCalibration distanceCalibration;
bool haveDistanceScale;
float distanceScale;
- enum CoverageCalibration {
- COVERAGE_CALIBRATION_DEFAULT,
- COVERAGE_CALIBRATION_NONE,
- COVERAGE_CALIBRATION_BOX,
+ enum class CoverageCalibration {
+ DEFAULT,
+ NONE,
+ BOX,
};
CoverageCalibration coverageCalibration;
@@ -376,7 +376,7 @@
nsecs_t mDownTime;
// The pointer controller, or null if the device is not a pointer.
- sp<PointerControllerInterface> mPointerController;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
std::vector<VirtualKey> mVirtualKeys;
@@ -524,16 +524,16 @@
uint64_t distance : 48; // squared distance
};
- enum PointerUsage {
- POINTER_USAGE_NONE,
- POINTER_USAGE_GESTURES,
- POINTER_USAGE_STYLUS,
- POINTER_USAGE_MOUSE,
+ enum class PointerUsage {
+ NONE,
+ GESTURES,
+ STYLUS,
+ MOUSE,
};
PointerUsage mPointerUsage;
struct PointerGesture {
- enum Mode {
+ enum class Mode {
// No fingers, button is not pressed.
// Nothing happening.
NEUTRAL,
@@ -646,9 +646,9 @@
firstTouchTime = LLONG_MIN;
activeTouchId = -1;
activeGestureId = -1;
- currentGestureMode = NEUTRAL;
+ currentGestureMode = Mode::NEUTRAL;
currentGestureIdBits.clear();
- lastGestureMode = NEUTRAL;
+ lastGestureMode = Mode::NEUTRAL;
lastGestureIdBits.clear();
downTime = 0;
velocityTracker.clear();
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 7665680..8c1e224 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -39,23 +39,17 @@
// TODO: Handle FF_STATUS, although it does not seem to be widely supported.
}
-void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+void VibratorInputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) {
#if DEBUG_VIBRATOR
std::string patternStr;
- for (size_t i = 0; i < patternSize; i++) {
- if (i != 0) {
- patternStr += ", ";
- }
- patternStr += StringPrintf("%" PRId64, pattern[i]);
- }
+ dumpPattern(patternStr);
ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
patternStr.c_str(), repeat, token);
#endif
mVibrating = true;
- memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
- mPatternSize = patternSize;
+ mPattern = pattern;
mRepeat = repeat;
mToken = token;
mIndex = -1;
@@ -85,7 +79,7 @@
void VibratorInputMapper::nextStep() {
mIndex += 1;
- if (size_t(mIndex) >= mPatternSize) {
+ if (size_t(mIndex) >= mPattern.size()) {
if (mRepeat < 0) {
// We are done.
stopVibrating();
@@ -94,13 +88,15 @@
mIndex = mRepeat;
}
- bool vibratorOn = mIndex & 1;
- nsecs_t duration = mPattern[mIndex];
- if (vibratorOn) {
+ const VibrationElement& element = mPattern[mIndex];
+ if (element.isOn()) {
#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
+ std::string description;
+ element.dump(description);
+ ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ description.c_str());
#endif
- getDeviceContext().vibrate(duration);
+ getDeviceContext().vibrate(element);
} else {
#if DEBUG_VIBRATOR
ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -108,10 +104,12 @@
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- mNextStepTime = now + duration;
+ std::chrono::nanoseconds duration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
+ mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
+ ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
#endif
}
@@ -126,6 +124,25 @@
void VibratorInputMapper::dump(std::string& dump) {
dump += INDENT2 "Vibrator Input Mapper:\n";
dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating));
+ if (mVibrating) {
+ dump += INDENT3 "Pattern: ";
+ dumpPattern(dump);
+ dump += "\n";
+ dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat);
+ }
+}
+
+void VibratorInputMapper::dumpPattern(std::string& dump) const {
+ dump += "[";
+
+ if (mPattern.size() > 0) {
+ mPattern[0].dump(dump);
+ std::for_each(mPattern.begin() + 1, mPattern.end(), [&dump](const auto& element) {
+ dump += ", ";
+ element.dump(dump);
+ });
+ }
+ dump += "]";
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index f69fdde..bfa5ec1 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
virtual void process(const RawEvent* rawEvent) override;
- virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+ virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat,
int32_t token) override;
virtual void cancelVibrate(int32_t token) override;
virtual void timeoutExpired(nsecs_t when) override;
@@ -38,13 +38,13 @@
private:
bool mVibrating;
- nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
- size_t mPatternSize;
+ std::vector<VibrationElement> mPattern;
ssize_t mRepeat;
int32_t mToken;
ssize_t mIndex;
nsecs_t mNextStepTime;
+ void dumpPattern(std::string& dump) const;
void nextStep();
void stopVibrating();
};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 73d2272..6465cc9 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -27,14 +27,25 @@
"libinputflinger_defaults",
],
srcs: [
+ "AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
- "TestInputListener.cpp",
+ "IInputFlingerQuery.aidl",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InputFlingerService_test.cpp",
+ "TestInputListener.cpp",
"UinputDevice.cpp",
],
+ aidl: {
+ include_dirs: [
+ "frameworks/native/libs/input",
+ ],
+ },
+ static_libs: [
+ "libc++fs"
+ ],
require_root: true,
}
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
new file mode 100644
index 0000000..b561da1
--- /dev/null
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 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 "../AnrTracker.h"
+
+#include <binder/Binder.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+// --- AnrTrackerTest ---
+
+/**
+ * Add a single entry and ensure it's returned as first, even if the token isn't valid
+ */
+TEST(AnrTrackerTest, SingleEntry_First) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(tracker.firstToken(), nullptr);
+}
+
+TEST(AnrTrackerTest, MultipleEntries_RemoveToken) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(1, token1);
+ tracker.insert(2, token2);
+ tracker.insert(3, token1);
+ tracker.insert(4, token2);
+ tracker.insert(5, token1);
+
+ tracker.eraseToken(token1);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+}
+
+TEST(AnrTrackerTest, AddAndRemove_Empty) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ tracker.insert(1, nullptr);
+ ASSERT_FALSE(tracker.empty());
+
+ tracker.erase(1, nullptr);
+ ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, Clear) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+ tracker.clear();
+ ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, SingleToken_MaintainsOrder) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ tracker.insert(2, nullptr);
+ tracker.insert(5, nullptr);
+ tracker.insert(0, nullptr);
+
+ ASSERT_EQ(0, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(5, token2);
+ tracker.insert(0, token2);
+
+ ASSERT_EQ(0, tracker.firstTimeout());
+ ASSERT_EQ(token2, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(2, token2);
+ tracker.insert(10, token2);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+ // Doesn't matter which token is returned - both are valid results
+ ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(2, token2);
+ tracker.insert(10, token2);
+
+ tracker.erase(2, token2);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+ ASSERT_EQ(token1, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, Empty_DoesntCrash) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+ // Can't call firstToken() if tracker.empty()
+}
+
+TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+
+ // Remove with non-matching timestamp
+ tracker.erase(2, nullptr);
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+
+ // Remove with non-matching token
+ tracker.erase(1, new BBinder());
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+
+ // Remove with both non-matching
+ tracker.erase(2, new BBinder());
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 71731b0..ef68a84 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -199,3 +199,76 @@
lastEventTime = event.when; // Ensure all returned events are monotonic
}
}
+
+// --- BitArrayTest ---
+class BitArrayTest : public testing::Test {
+protected:
+ static constexpr size_t SINGLE_ELE_BITS = 32UL;
+ static constexpr size_t MULTI_ELE_BITS = 256UL;
+
+ virtual void SetUp() override {
+ mBitmaskSingle.loadFromBuffer(mBufferSingle);
+ mBitmaskMulti.loadFromBuffer(mBufferMulti);
+ }
+
+ android::BitArray<SINGLE_ELE_BITS> mBitmaskSingle;
+ android::BitArray<MULTI_ELE_BITS> mBitmaskMulti;
+
+private:
+ const typename android::BitArray<SINGLE_ELE_BITS>::Buffer mBufferSingle = {
+ 0x800F0F0FUL // bit 0 - 31
+ };
+ const typename android::BitArray<MULTI_ELE_BITS>::Buffer mBufferMulti = {
+ 0xFFFFFFFFUL, // bit 0 - 31
+ 0x01000001UL, // bit 32 - 63
+ 0x00000000UL, // bit 64 - 95
+ 0x80000000UL, // bit 96 - 127
+ 0x00000000UL, // bit 128 - 159
+ 0x00000000UL, // bit 160 - 191
+ 0x80000008UL, // bit 192 - 223
+ 0x00000000UL, // bit 224 - 255
+ };
+};
+
+TEST_F(BitArrayTest, SetBit) {
+ ASSERT_TRUE(mBitmaskSingle.test(0));
+ ASSERT_TRUE(mBitmaskSingle.test(31));
+ ASSERT_FALSE(mBitmaskSingle.test(7));
+
+ ASSERT_TRUE(mBitmaskMulti.test(32));
+ ASSERT_TRUE(mBitmaskMulti.test(56));
+ ASSERT_FALSE(mBitmaskMulti.test(192));
+ ASSERT_TRUE(mBitmaskMulti.test(223));
+ ASSERT_FALSE(mBitmaskMulti.test(255));
+}
+
+TEST_F(BitArrayTest, AnyBit) {
+ ASSERT_TRUE(mBitmaskSingle.any(31, 32));
+ ASSERT_FALSE(mBitmaskSingle.any(12, 16));
+
+ ASSERT_TRUE(mBitmaskMulti.any(31, 32));
+ ASSERT_FALSE(mBitmaskMulti.any(33, 33));
+ ASSERT_TRUE(mBitmaskMulti.any(32, 55));
+ ASSERT_TRUE(mBitmaskMulti.any(33, 57));
+ ASSERT_FALSE(mBitmaskMulti.any(33, 55));
+ ASSERT_FALSE(mBitmaskMulti.any(130, 190));
+
+ ASSERT_FALSE(mBitmaskMulti.any(128, 195));
+ ASSERT_TRUE(mBitmaskMulti.any(128, 196));
+ ASSERT_TRUE(mBitmaskMulti.any(128, 224));
+ ASSERT_FALSE(mBitmaskMulti.any(255, 256));
+}
+
+TEST_F(BitArrayTest, SetBit_InvalidBitIndex) {
+ ASSERT_FALSE(mBitmaskSingle.test(32));
+ ASSERT_FALSE(mBitmaskMulti.test(256));
+}
+
+TEST_F(BitArrayTest, AnyBit_InvalidBitIndex) {
+ ASSERT_FALSE(mBitmaskSingle.any(32, 32));
+ ASSERT_FALSE(mBitmaskSingle.any(33, 34));
+
+ ASSERT_FALSE(mBitmaskMulti.any(256, 256));
+ ASSERT_FALSE(mBitmaskMulti.any(257, 258));
+ ASSERT_FALSE(mBitmaskMulti.any(0, 0));
+}
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
new file mode 100644
index 0000000..b5c5c9e
--- /dev/null
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+
+import android.FocusRequest;
+import android.InputChannel;
+import android.InputWindowInfo;
+import android.os.ISetInputWindowsListener;
+
+/** @hide */
+interface IInputFlingerQuery
+{
+ /* Test interfaces */
+ void getInputWindows(out InputWindowInfo[] inputHandles);
+ void getInputChannels(out InputChannel[] channels);
+ void getLastFocusRequest(out FocusRequest request);
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e0d32a0..296201d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -17,16 +17,19 @@
#include "../dispatcher/InputDispatcher.h"
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <input/Input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
#include <cinttypes>
+#include <thread>
#include <unordered_set>
#include <vector>
using android::base::StringPrintf;
+using namespace android::flag_operators;
namespace android::inputdispatcher {
@@ -83,9 +86,13 @@
args.displayId);
}
- void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+ void assertFilterInputEventWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_EQ(nullptr, mFilteredEvent);
+ }
void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+ std::scoped_lock lock(mLock);
ASSERT_TRUE(mConfigurationChangedTime)
<< "Timed out waiting for configuration changed call";
ASSERT_EQ(*mConfigurationChangedTime, when);
@@ -93,6 +100,7 @@
}
void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+ std::scoped_lock lock(mLock);
ASSERT_TRUE(mLastNotifySwitch);
// We do not check id because it is not exposed to the policy
EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
@@ -103,47 +111,114 @@
}
void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+ std::scoped_lock lock(mLock);
ASSERT_EQ(touchedToken, mOnPointerDownToken);
mOnPointerDownToken.clear();
}
void assertOnPointerDownWasNotCalled() {
+ std::scoped_lock lock(mLock);
ASSERT_TRUE(mOnPointerDownToken == nullptr)
<< "Expected onPointerDownOutsideFocus to not have been called";
}
+ // This function must be called soon after the expected ANR timer starts,
+ // because we are also checking how much time has passed.
+ void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
+ const sp<InputApplicationHandle>& expectedApplication,
+ const sp<IBinder>& expectedToken) {
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData;
+ ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
+ ASSERT_EQ(expectedApplication, anrData.first);
+ ASSERT_EQ(expectedToken, anrData.second);
+ }
+
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
+ std::chrono::nanoseconds timeout) {
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::unique_lock lock(mLock);
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+
+ // If there is an ANR, Dispatcher won't be idle because there are still events
+ // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+ // before checking if ANR was called.
+ // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
+ // it some time to act. 100ms seems reasonable.
+ mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
+ return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
+ });
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+ if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
+ ADD_FAILURE() << "Did not receive ANR callback";
+ }
+ // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+ // the dispatcher started counting before this function was called
+ if (std::chrono::abs(timeout - waited) > 100ms) {
+ ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+ << "ms, but waited "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+ << "ms instead";
+ }
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> result =
+ std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
+ mAnrApplications.pop();
+ mAnrWindowTokens.pop();
+ return result;
+ }
+
+ void assertNotifyAnrWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mAnrApplications.empty());
+ ASSERT_TRUE(mAnrWindowTokens.empty());
+ }
+
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
mConfig.keyRepeatTimeout = timeout;
mConfig.keyRepeatDelay = delay;
}
+ void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+
private:
- std::unique_ptr<InputEvent> mFilteredEvent;
- std::optional<nsecs_t> mConfigurationChangedTime;
- sp<IBinder> mOnPointerDownToken;
- std::optional<NotifySwitchArgs> mLastNotifySwitch;
+ std::mutex mLock;
+ std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
+ std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock);
+ sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
+ std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
+
+ // ANR handling
+ std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+ std::condition_variable mNotifyAnr;
+ std::chrono::nanoseconds mAnrTimeout = 0ms;
virtual void notifyConfigurationChanged(nsecs_t when) override {
+ std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
- const sp<IBinder>&,
- const std::string&) {
- return 0;
+ std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& windowToken,
+ const std::string&) override {
+ std::scoped_lock lock(mLock);
+ mAnrApplications.push(application);
+ mAnrWindowTokens.push(windowToken);
+ mNotifyAnr.notify_all();
+ return mAnrTimeout;
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) {
- }
+ virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {
- }
+ virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
+ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ std::scoped_lock lock(mLock);
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
@@ -160,43 +235,43 @@
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) {
- }
+ virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) {
- }
+ virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&,
- const KeyEvent*, uint32_t) {
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
+ uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&,
- const KeyEvent*, uint32_t, KeyEvent*) {
+ virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
+ KeyEvent*) override {
return false;
}
virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
uint32_t policyFlags) override {
+ std::scoped_lock lock(mLock);
/** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
* essentially a passthrough for notifySwitch.
*/
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- virtual void pokeUserActivity(nsecs_t, int32_t) {
- }
+ virtual void pokeUserActivity(nsecs_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
+ virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
return false;
}
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+ std::scoped_lock lock(mLock);
mOnPointerDownToken = newToken;
}
void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
int32_t displayId) {
+ std::scoped_lock lock(mLock);
ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
ASSERT_EQ(mFilteredEvent->getType(), type);
@@ -302,6 +377,20 @@
mFakePolicy.clear();
mDispatcher.clear();
}
+
+ /**
+ * Used for debugging when writing the test
+ */
+ void dumpDispatcherState() {
+ std::string dump;
+ mDispatcher->dump(dump);
+ std::stringstream ss(dump);
+ std::string to;
+
+ while (std::getline(ss, to, '\n')) {
+ ALOGE("%s", to.c_str());
+ }
+ }
};
@@ -313,18 +402,18 @@
INVALID_HMAC,
/*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
ARBITRARY_TIME, ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -344,113 +433,110 @@
constexpr int32_t metaState = AMETA_NONE;
constexpr MotionClassification classification = MotionClassification::NONE;
+ ui::Transform identityTransform;
// Rejects undefined motion actions.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
- 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
pointerProperties[0].id = -1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
@@ -458,13 +544,12 @@
pointerProperties[1].id = 1;
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
- 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
@@ -490,28 +575,50 @@
}
// --- InputDispatcherTest SetInputWindowTest ---
-static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
-static constexpr nsecs_t DISPATCHING_TIMEOUT = seconds_to_nanoseconds(5);
+static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
class FakeApplicationHandle : public InputApplicationHandle {
public:
- FakeApplicationHandle() {}
+ FakeApplicationHandle() {
+ mInfo.name = "Fake Application";
+ mInfo.token = new BBinder();
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ }
virtual ~FakeApplicationHandle() {}
- virtual bool updateInfo() {
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ virtual bool updateInfo() override {
return true;
}
+
+ void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout;
+ }
};
class FakeInputReceiver {
public:
- explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+ explicit FakeInputReceiver(const std::shared_ptr<InputChannel>& clientChannel,
+ const std::string name)
: mName(name) {
mConsumer = std::make_unique<InputConsumer>(clientChannel);
}
InputEvent* consume() {
+ InputEvent* event;
+ std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+ if (!consumeSeq) {
+ return nullptr;
+ }
+ finishEvent(*consumeSeq);
+ return event;
+ }
+
+ /**
+ * Receive an event without acknowledging it.
+ * Return the sequence number that could later be used to send finished signal.
+ */
+ std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
uint32_t consumeSeq;
InputEvent* event;
@@ -528,23 +635,29 @@
if (status == WOULD_BLOCK) {
// Just means there's no event available.
- return nullptr;
+ return std::nullopt;
}
if (status != OK) {
ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
- return nullptr;
+ return std::nullopt;
}
if (event == nullptr) {
ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
- return nullptr;
+ return std::nullopt;
}
+ if (outEvent != nullptr) {
+ *outEvent = event;
+ }
+ return consumeSeq;
+ }
- status = mConsumer->sendFinishedSignal(consumeSeq, true);
- if (status != OK) {
- ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
- }
- return event;
+ /**
+ * To be used together with "receiveEvent" to complete the consumption of an event.
+ */
+ void finishEvent(uint32_t consumeSeq) {
+ const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+ ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
@@ -554,7 +667,7 @@
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+ << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
EXPECT_EQ(expectedDisplayId, event->getDisplayId());
@@ -599,9 +712,24 @@
void assertNoEvents() {
InputEvent* event = consume();
- ASSERT_EQ(nullptr, event)
- << mName.c_str()
- << ": should not have received any events, so consume() should return NULL";
+ if (event == nullptr) {
+ return;
+ }
+ if (event->getType() == AINPUT_EVENT_TYPE_KEY) {
+ KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+ ADD_FAILURE() << "Received key event "
+ << KeyEvent::actionToString(keyEvent.getAction());
+ } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) {
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ ADD_FAILURE() << "Received motion event "
+ << MotionEvent::actionToString(motionEvent.getAction());
+ } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) {
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ ADD_FAILURE() << "Received focus event, hasFocus = "
+ << (focusEvent.getHasFocus() ? "true" : "false");
+ }
+ FAIL() << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
}
sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
@@ -623,26 +751,26 @@
int32_t displayId, sp<IBinder> token = nullptr)
: mName(name) {
if (token == nullptr) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
- dispatcher->registerInputChannel(serverChannel);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
token = serverChannel->getConnectionToken();
+ dispatcher->registerInputChannel(std::move(serverChannel));
}
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
mInfo.token = token;
- mInfo.id = 0;
+ mInfo.id = sId++;
mInfo.name = name;
- mInfo.layoutParamsFlags = 0;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.type = InputWindowInfo::Type::APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.frameLeft = 0;
mInfo.frameTop = 0;
mInfo.frameRight = WIDTH;
mInfo.frameBottom = HEIGHT;
+ mInfo.transform.set(0, 0);
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
@@ -653,7 +781,6 @@
mInfo.paused = false;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
mInfo.displayId = displayId;
}
@@ -661,29 +788,39 @@
void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
+ void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout;
+ }
+
+ void setPaused(bool paused) { mInfo.paused = paused; }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
mInfo.frameRight = frame.right;
mInfo.frameBottom = frame.bottom;
+ mInfo.transform.set(frame.left, frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
- void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+ void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
- void setId(int32_t id) { mInfo.id = id; }
-
- void setWindowScale(float xScale, float yScale) {
- mInfo.windowXScale = xScale;
- mInfo.windowYScale = yScale;
+ void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+ mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
}
+ void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
}
+ void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+ }
+
void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
@@ -735,6 +872,19 @@
expectedFlags);
}
+ std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
+ if (mInputReceiver == nullptr) {
+ ADD_FAILURE() << "Invalid receive event on window with no receiver";
+ return std::nullopt;
+ }
+ return mInputReceiver->receiveEvent(outEvent);
+ }
+
+ void finishEvent(uint32_t sequenceNum) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->finishEvent(sequenceNum);
+ }
+
InputEvent* consume() {
if (mInputReceiver == nullptr) {
return nullptr;
@@ -755,68 +905,189 @@
private:
const std::string mName;
std::unique_ptr<FakeInputReceiver> mInputReceiver;
+ static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
};
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
+static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ int32_t displayId = ADISPLAY_ID_NONE,
+ int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key down event.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
- INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
- AMETA_NONE,
- /* repeatCount */ 0, currentTime, currentTime);
+ INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+ repeatCount, currentTime, currentTime);
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
-static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action,
- int32_t source, int32_t displayId, int32_t x, int32_t y,
- int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION,
- int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) {
- MotionEvent event;
- PointerProperties pointerProperties[1];
- PointerCoords pointerCoords[1];
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
+}
- pointerProperties[0].clear();
- pointerProperties[0].id = 0;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
+}
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+class PointerBuilder {
+public:
+ PointerBuilder(int32_t id, int32_t toolType) {
+ mProperties.clear();
+ mProperties.id = id;
+ mProperties.toolType = toolType;
+ mCoords.clear();
+ }
- nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- // Define a valid motion down event.
- event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
- /* actionButton */ 0,
- /* flags */ 0,
- /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
- /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
- currentTime, currentTime,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+ PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+ PointerBuilder& axis(int32_t axis, float value) {
+ mCoords.setAxisValue(axis, value);
+ return *this;
+ }
+
+ PointerProperties buildProperties() const { return mProperties; }
+
+ PointerCoords buildCoords() const { return mCoords; }
+
+private:
+ PointerProperties mProperties;
+ PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+ MotionEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ MotionEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ MotionEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ MotionEventBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& buttonState(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+ mRawXCursorPosition = rawXCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+ mRawYCursorPosition = rawYCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& pointer(PointerBuilder pointer) {
+ mPointers.push_back(pointer);
+ return *this;
+ }
+
+ MotionEvent build() {
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (const PointerBuilder& pointer : mPointers) {
+ pointerProperties.push_back(pointer.buildProperties());
+ pointerCoords.push_back(pointer.buildCoords());
+ }
+
+ // Set mouse cursor position for the most common cases to avoid boilerplate.
+ if (mSource == AINPUT_SOURCE_MOUSE &&
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+ mPointers.size() == 1) {
+ mRawXCursorPosition = pointerCoords[0].getX();
+ mRawYCursorPosition = pointerCoords[0].getY();
+ }
+
+ MotionEvent event;
+ ui::Transform identityTransform;
+ event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mButtonState, MotionClassification::NONE, identityTransform,
+ /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
+ mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
+
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mSource;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mActionButton{0};
+ int32_t mButtonState{0};
+ float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
+static int32_t injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT) {
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static int32_t injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ const PointF& position,
+ const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
+ MotionEvent event = MotionEventBuilder(action, source)
+ .displayId(displayId)
+ .eventTime(eventTime)
+ .rawXCursorPosition(cursorPosition.x)
+ .rawYCursorPosition(cursorPosition.y)
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(position.x)
+ .y(position.y))
+ .build();
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return injectMotionEvent(dispatcher, event);
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId, int32_t x = 100, int32_t y = 200) {
- return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y);
+ int32_t displayId, const PointF& location = {100, 200}) {
+ return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId, int32_t x = 100, int32_t y = 200) {
- return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y);
+ int32_t displayId, const PointF& location = {100, 200}) {
+ return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
@@ -880,6 +1151,55 @@
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
+/**
+ * Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues.
+ * To ensure that window receives only events that were directly inside of it, add
+ * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input
+ * when finding touched windows.
+ * This test serves as a sanity check for the next test, where setInputWindows is
+ * called twice.
+ */
+TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Window should receive motion event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+/**
+ * Calling setInputWindows twice, with the same info, should not cause any issues.
+ * To ensure that window receives only events that were directly inside of it, add
+ * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input
+ * when finding touched windows.
+ */
+TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Window should receive motion event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
// The foreground window should receive the first touch down event.
TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
@@ -972,17 +1292,209 @@
windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
}
+TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> windowLeft =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ windowLeft->setFrame(Rect(0, 0, 600, 800));
+ windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+ sp<FakeWindowHandle> windowRight =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ windowRight->setFrame(Rect(600, 0, 1200, 800));
+ windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+
+ // Start cursor position in right window so that we can move the cursor to left window.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Move cursor into left window
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ // Move mouse cursor back to right window
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+}
+
+// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
+// directly in this test.
+TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 1200, 800));
+ window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+}
+
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> windowLeft =
new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
windowLeft->setFrame(Rect(0, 0, 600, 800));
- windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
sp<FakeWindowHandle> windowRight =
new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
windowRight->setFrame(Rect(600, 0, 1200, 800));
- windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -992,7 +1504,7 @@
// left window. This event should be dispatched to the left window.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
- ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
+ ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowRight->assertNoEvents();
}
@@ -1141,15 +1653,15 @@
sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
"First Window", ADISPLAY_ID_DEFAULT);
firstWindow->setFrame(Rect(0, 0, 600, 400));
- firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
- | InputWindowInfo::FLAG_SPLIT_TOUCH);
+ firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Create a non touch modal window that supports split touch
sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
"Second Window", ADISPLAY_ID_DEFAULT);
secondWindow->setFrame(Rect(0, 400, 600, 800));
- secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
- | InputWindowInfo::FLAG_SPLIT_TOUCH);
+ secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
// Add the windows to the dispatcher
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -1255,10 +1767,10 @@
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false) {
- sp<InputChannel> serverChannel, clientChannel;
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
- dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
+ dispatcher->registerInputMonitor(std::move(serverChannel), displayId, isGestureMonitor);
}
sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -1268,6 +1780,10 @@
expectedDisplayId, expectedFlags);
}
+ std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
expectedDisplayId, expectedFlags);
@@ -1346,6 +1862,21 @@
monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
+TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
+ ASSERT_TRUE(consumeSeq);
+
+ mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+ monitor.finishEvent(*consumeSeq);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
@@ -1815,13 +2346,12 @@
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
// Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
// window.
- mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mFocusedWindow =
new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
- mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
- mFocusedWindowTouchPoint = 60;
+ mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -1842,15 +2372,16 @@
protected:
sp<FakeWindowHandle> mUnfocusedWindow;
sp<FakeWindowHandle> mFocusedWindow;
- int32_t mFocusedWindowTouchPoint;
+ static constexpr PointF FOCUSED_WINDOW_TOUCH_POINT = {60, 60};
};
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {20, 20}))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
@@ -1862,8 +2393,8 @@
// DOWN on the window that doesn't have focus. Ensure no window received the
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -1889,7 +2420,7 @@
OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
+ FOCUSED_WINDOW_TOUCH_POINT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -1908,16 +2439,14 @@
ADISPLAY_ID_DEFAULT);
// Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
// We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
- mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
- mWindow1->setId(0);
+ mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mWindow1->setFrame(Rect(0, 0, 100, 100));
mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
ADISPLAY_ID_DEFAULT, mWindow1->getToken());
- mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
- InputWindowInfo::FLAG_SPLIT_TOUCH);
- mWindow2->setId(1);
+ mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
mWindow2->setFrame(Rect(100, 100, 200, 200));
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
@@ -1929,9 +2458,8 @@
// Helper function to convert the point from screen coordinates into the window's space
static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
- float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
- float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
- return {x, y};
+ vec2 vals = windowInfo->transform.transform(point.x, point.y);
+ return {vals.x, vals.y};
}
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
@@ -1961,133 +2489,123 @@
<< ", got " << motionEvent.getY(i);
}
}
+
+ void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+ std::vector<PointF> expectedPoints) {
+ NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Always consume from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, action, expectedPoints);
+ }
};
TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- // consume the UP event
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
-
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
+ // Set scale value for window2
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
-
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
- // consume the UP event
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchedPoint});
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+ // Update the transform so rotation is set
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
+ // Release Window 2
+ int32_t actionPointerUp =
+ AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ expectedPoints.pop_back();
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ // Update the transform so rotation is set for Window 2
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
mWindow2->setWindowScale(0.5f, 0.5f);
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+ // Release Window 2
+ int32_t actionPointerUp =
+ AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints);
+ expectedPoints.pop_back();
+
+ // Touch Window 2
+ mWindow2->setWindowTransform(0, -1, 1, 0);
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
@@ -2096,36 +2614,694 @@
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
int32_t actionPointerDown =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+ touchedPoints.push_back(PointF{150, 150});
+ expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+ touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
+ touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
+}
+
+class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = new FakeApplicationHandle();
+ mApplication->setDispatchingTimeout(20ms);
+ mWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 30, 30));
+ mWindow->setDispatchingTimeout(30ms);
+ mWindow->setFocus(true);
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(true);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+ mWindow.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mWindow;
+ static constexpr PointF WINDOW_LOCATION = {20, 20};
+
+ void tapOnWindow() {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ }
+};
+
+// Send a tap and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) {
+ tapOnWindow();
+ mWindow->consumeMotionDown();
+ mWindow->consumeMotionUp();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send a regular key and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send an event to the app and have the app not respond right away.
+// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
+// So InputDispatcher will enqueue ACTION_CANCEL event as well.
+TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+
+ std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
+ ASSERT_TRUE(sequenceNum);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+
+ // The remaining lines are not really needed for the test, but kept as a sanity check
+ mWindow->finishEvent(*sequenceNum);
+ mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// Send a key to the app and have the app not respond right away.
+TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
+ // Inject a key, and don't respond - expect that ANR is called.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(sequenceNum);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ // taps on the window work as normal
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+ mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ // Once a focused event arrives, we get an ANR for this application
+ // We specify the injection timeout to be smaller than the application timeout, to ensure that
+ // injection times out (instead of failing).
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+// If the policy wants to keep waiting on the focused window to be added, make sure
+// that this timeout extension is honored and ANR is raised again.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+ const std::chrono::duration timeout = 5ms;
+ mFakePolicy->setAnrTimeout(timeout);
+
+ // Once a focused event arrives, we get an ANR for this application
+ // We specify the injection timeout to be smaller than the application timeout, to ensure that
+ // injection times out (instead of failing).
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ const std::chrono::duration appTimeout =
+ mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+
+ // After the extended time has passed, ANR should be raised again
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+ // If we stop extending the timeout, dispatcher should go to idle.
+ // Another ANR may be raised during this time
+ mFakePolicy->setAnrTimeout(0ms);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ // Once a focused event arrives, we get an ANR for this application
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+
+ const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+ // Future focused events get dropped right away
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mWindow->assertNoEvents();
+}
+
+/**
+ * Ensure that the implementation is valid. Since we are using multiset to keep track of the
+ * ANR timeouts, we are allowing entries with identical timestamps in the same connection.
+ * If we process 1 of the events, but ANR on the second event with the same timestamp,
+ * the ANR mechanism should still work.
+ *
+ * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the
+ * DOWN event, while not responding on the second one.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+ // Now send ACTION_UP, with identical timestamp
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+ // We have now sent down and up. Let's consume first event and then ANR on the second.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+}
+
+// If an app is not responding to a key event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+
+ // Stuck on the ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+ // New tap will go to the gesture monitor, but not to the window
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
+// If an app is not responding to a motion event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeMotionDown();
+ // Stuck on the ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+ // New tap will go to the gesture monitor, but not to the window
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
+// If a window is unresponsive, then you get anr. if the window later catches up and starts to
+// process events, you don't get an anr. When the window later becomes unresponsive again, you
+// get an ANR again.
+// 1. tap -> block on ACTION_UP -> receive ANR
+// 2. consume all pending events (= queue becomes healthy again)
+// 3. tap again -> block on ACTION_UP again -> receive ANR second time
+TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) {
+ tapOnWindow();
+
+ mWindow->consumeMotionDown();
+ // Block on ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mWindow->consumeMotionUp(); // Now the connection should be healthy again
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+
+ tapOnWindow();
+ mWindow->consumeMotionDown();
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mWindow->consumeMotionUp();
+
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+}
+
+// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
+// is honored
+TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
+ const std::chrono::duration timeout = 5ms;
+ mFakePolicy->setAnrTimeout(timeout);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+
+ const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
+ mWindow->getToken());
+
+ // Since the policy wanted to extend ANR, make sure it is called again after the extension
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->setAnrTimeout(0ms);
+ std::this_thread::sleep_for(windowTimeout);
+ // We are not checking if ANR has been called, because it may have been called again by the
+ // time we set the timeout to 0
+
+ // When the policy finally says stop, we should get ACTION_CANCEL
+ mWindow->consumeMotionDown();
+ mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ mWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
+ mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ tapOnWindow();
+ std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection will "succeed" because we will eventually give up and send the key to the focused
+ // window even if motions are still being processed. But because the injection timeout is short,
+ // we will receive INJECTION_TIMED_OUT as the result.
+
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ // Key will not be sent to the window, yet, because the window is still processing events
+ // and the key remains pending, waiting for the touch events to be processed
+ std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ std::this_thread::sleep_for(500ms);
+ // if we wait long enough though, dispatcher will give up, and still send the key
+ // to the focused window, even though we have not yet finished the motion event
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->finishEvent(*downSequenceNum);
+ mWindow->finishEvent(*upSequenceNum);
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not go to the focused window until the motion is processed.
+ * If then a new motion comes in, then the pending key event should be going to the currently
+ * focused window right away.
+ */
+TEST_F(InputDispatcherSingleWindowAnr,
+ PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
+ mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ tapOnWindow();
+ std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection is async, so it will succeed
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE));
+ // At this point, key is still pending, and should not be sent to the application yet.
+ std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ // Now tap down again. It should cause the pending key to go to the focused window right away.
+ tapOnWindow();
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd
+ // the other events yet. We can finish events in any order.
+ mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN
+ mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP
+ mWindow->consumeMotionDown();
+ mWindow->consumeMotionUp();
+ mWindow->assertNoEvents();
+}
+
+class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = new FakeApplicationHandle();
+ mApplication->setDispatchingTimeout(10ms);
+ mUnfocusedWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
+ mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ mFocusedWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->setDispatchingTimeout(30ms);
+ mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+ mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+ InputWindowInfo::Flag::SPLIT_TOUCH);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mFocusedWindow->setFocus(true);
+
+ // Expect one focus window exist in display.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(true);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+
+ mUnfocusedWindow.clear();
+ mFocusedWindow.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mUnfocusedWindow;
+ sp<FakeWindowHandle> mFocusedWindow;
+ static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
+ static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75};
+ static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40};
+
+ void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); }
+
+ void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); }
+
+private:
+ void tap(const PointF& location) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ location));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ location));
+ }
+};
+
+// If we have 2 windows that are both unresponsive, the one with the shortest timeout
+// should be ANR'd first.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mFocusedWindow->consumeMotionDown();
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ // We consumed all events, so no ANR
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(unfocusedSequenceNum);
+ std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
+ ASSERT_TRUE(focusedSequenceNum);
+
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ mFocusedWindow->finishEvent(*focusedSequenceNum);
+ mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// If we have 2 windows with identical timeouts that are both unresponsive,
+// it doesn't matter which order they should have ANR.
+// But we should receive ANR for both.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
+ // Set the timeout for unfocused window to match the focused window
+ mUnfocusedWindow->setDispatchingTimeout(10ms);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+ tapOnFocusedWindow();
+ // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 =
+ mFakePolicy->getNotifyAnrData(10ms);
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 =
+ mFakePolicy->getNotifyAnrData(0ms);
+
+ // We don't know which window will ANR first. But both of them should happen eventually.
+ ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
+ mFocusedWindow->getToken() == anrData2.second);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
+ mUnfocusedWindow->getToken() == anrData2.second);
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If a window is already not responding, the second tap on the same window should be ignored.
+// We should also log an error to account for the dropped event (not tested here).
+// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
+TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
+ tapOnFocusedWindow();
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ // Receive the events, but don't respond
+ std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
+ ASSERT_TRUE(downEventSequenceNum);
+ std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP
+ ASSERT_TRUE(upEventSequenceNum);
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ // Tap once again
+ // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
+ // valid touch target
+ mUnfocusedWindow->assertNoEvents();
+
+ // Consume the first tap
+ mFocusedWindow->finishEvent(*downEventSequenceNum);
+ mFocusedWindow->finishEvent(*upEventSequenceNum);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // The second tap did not go to the focused window
+ mFocusedWindow->assertNoEvents();
+ // should not have another ANR after the window just became healthy again
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If you tap outside of all windows, there will not be ANR
+TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ LOCATION_OUTSIDE_ALL_WINDOWS));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Since the focused window is paused, tapping on it should not produce any events
+TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
+ mFocusedWindow->setPaused(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+
+ std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because the window is paused, and touches shouldn't go to it
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ * If a different window becomes focused at this time, the key should go to that window instead.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
+ // Set a long ANR timeout to prevent it from triggering
+ mFocusedWindow->setDispatchingTimeout(2s);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+ tapOnUnfocusedWindow();
+ std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection will succeed because we will eventually give up and send the key to the focused
+ // window even if motions are still being processed.
+
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+ // Key will not be sent to the window, yet, because the window is still processing events
+ // and the key remains pending, waiting for the touch events to be processed
+ std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
+ mFocusedWindow->setFocus(false);
+ mUnfocusedWindow->setFocus(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+ // Focus events should precede the key events
+ mUnfocusedWindow->consumeFocusEvent(true);
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Finish the tap events, which should unblock dispatcher
+ mUnfocusedWindow->finishEvent(*downSequenceNum);
+ mUnfocusedWindow->finishEvent(*upSequenceNum);
+
+ // Now that all queues are cleared and no backlog in the connections, the key event
+ // can finally go to the newly focused "mUnfocusedWindow".
+ mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
+}
+
+// When the touch stream is split across 2 windows, and one of them does not respond,
+// then ANR should be raised and the touch should be canceled for the unresponsive window.
+// The other window should not be affected by that.
+TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
+ // Touch Window 1
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ motionArgs =
+ generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ mUnfocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionDown();
+ // Focused window may or may not receive ACTION_MOVE
+ // But it should definitely receive ACTION_CANCEL due to the ANR
+ InputEvent* event;
+ std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event);
+ ASSERT_TRUE(moveOrCancelSequenceNum);
+ mFocusedWindow->finishEvent(*moveOrCancelSequenceNum);
+ ASSERT_NE(nullptr, event);
+ ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION);
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) {
+ mFocusedWindow->consumeMotionCancel();
+ } else {
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
+ }
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mUnfocusedWindow->assertNoEvents();
+ mFocusedWindow->assertNoEvents();
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
new file mode 100644
index 0000000..02342c0
--- /dev/null
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2020 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 <BnInputFlingerQuery.h>
+#include <IInputFlingerQuery.h>
+
+#include <android/os/BnInputFlinger.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
+#include <android/os/ISetInputWindowsListener.h>
+
+#include <binder/Binder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/InputWindow.h>
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <chrono>
+#include <thread>
+#include <unordered_map>
+
+#define TAG "InputFlingerServiceTest"
+
+using android::os::BnInputFlinger;
+using android::os::BnSetInputWindowsListener;
+using android::os::IInputFlinger;
+using android::os::ISetInputWindowsListener;
+
+using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
+
+namespace android {
+
+static const sp<IBinder> TestInfoToken = new BBinder();
+static const sp<IBinder> FocusedTestInfoToken = new BBinder();
+static constexpr int32_t TestInfoId = 1;
+static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo";
+static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE;
+static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD;
+static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
+static constexpr int32_t TestInfoFrameLeft = 93;
+static constexpr int32_t TestInfoFrameTop = 34;
+static constexpr int32_t TestInfoFrameRight = 16;
+static constexpr int32_t TestInfoFrameBottom = 19;
+static constexpr int32_t TestInfoSurfaceInset = 17;
+static constexpr float TestInfoGlobalScaleFactor = 0.3;
+static constexpr float TestInfoWindowXScale = 0.4;
+static constexpr float TestInfoWindowYScale = 0.5;
+static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */,
+ 450 /* bottom */};
+static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect);
+static constexpr bool TestInfoVisible = false;
+static constexpr bool TestInfoCanReceiveKeys = false;
+static constexpr bool TestInfoTrustedOverlay = true;
+static constexpr bool TestInfoHasFocus = false;
+static constexpr bool TestInfoHasWallpaper = false;
+static constexpr bool TestInfoPaused = false;
+static constexpr int32_t TestInfoOwnerPid = 19;
+static constexpr int32_t TestInfoOwnerUid = 24;
+static constexpr InputWindowInfo::Feature TestInfoInputFeatures =
+ InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+static constexpr int32_t TestInfoDisplayId = 34;
+static constexpr int32_t TestInfoPortalToDisplayId = 2;
+static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
+static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder();
+
+static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo";
+static const sp<IBinder> TestAppInfoToken = new BBinder();
+static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms;
+
+static const String16 kTestServiceName = String16("InputFlingerService");
+static const String16 kQueryServiceName = String16("InputFlingerQueryService");
+
+struct SetInputWindowsListener;
+// --- InputFlingerServiceTest ---
+class InputFlingerServiceTest : public testing::Test {
+public:
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ void InitializeInputFlinger();
+ void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos);
+ void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken,
+ nsecs_t timestampNanos);
+
+ void setInputWindowsFinished();
+ void verifyInputWindowInfo(const InputWindowInfo& info) const;
+ InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); }
+
+ sp<IInputFlinger> mService;
+ sp<IInputFlingerQuery> mQuery;
+
+private:
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
+ std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
+ InputWindowInfo mInfo;
+ std::mutex mLock;
+ std::condition_variable mSetInputWindowsFinishedCondition;
+};
+
+struct SetInputWindowsListener : BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {}
+
+ binder::Status onSetInputWindowsFinished() override;
+
+ std::function<void()> mCbFunc;
+};
+
+class TestInputManager : public BnInputFlinger {
+protected:
+ virtual ~TestInputManager(){};
+
+public:
+ TestInputManager(){};
+ void checkFdFlags(const android::base::unique_fd& fd);
+
+ binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
+ binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
+ binder::Status getLastFocusRequest(FocusRequest*);
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+
+ binder::Status setInputWindows(
+ const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
+
+ binder::Status registerInputChannel(const InputChannel& channel) override;
+ binder::Status unregisterInputChannel(const InputChannel& channel) override;
+ binder::Status setFocusedWindow(const FocusRequest&) override;
+
+private:
+ mutable Mutex mLock;
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
+ std::vector<std::shared_ptr<InputChannel>> mInputChannels;
+ FocusRequest mFocusRequest;
+};
+
+class TestInputQuery : public BnInputFlingerQuery {
+public:
+ TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
+ binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
+ binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
+ binder::Status getLastFocusRequest(FocusRequest*) override;
+
+private:
+ sp<android::TestInputManager> mManager;
+};
+
+binder::Status TestInputQuery::getInputWindows(
+ std::vector<::android::InputWindowInfo>* inputHandles) {
+ return mManager->getInputWindows(inputHandles);
+}
+
+binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
+ return mManager->getInputChannels(channels);
+}
+
+binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) {
+ return mManager->getLastFocusRequest(request);
+}
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+ if (mCbFunc != nullptr) {
+ mCbFunc();
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setInputWindows(
+ const std::vector<InputWindowInfo>& infos,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+ AutoMutex _l(mLock);
+
+ for (const auto& info : infos) {
+ mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
+ mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info));
+ }
+ if (setInputWindowsListener) {
+ setInputWindowsListener->onSetInputWindowsFinished();
+ }
+ return binder::Status::ok();
+}
+
+void TestInputManager::checkFdFlags(const android::base::unique_fd& fd) {
+ const int result = fcntl(fd, F_GETFL);
+ EXPECT_NE(result, -1);
+ EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+binder::Status TestInputManager::registerInputChannel(const InputChannel& channel) {
+ AutoMutex _l(mLock);
+ // check Fd flags
+ checkFdFlags(channel.getFd());
+
+ mInputChannels.push_back(channel.dup());
+
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::unregisterInputChannel(const InputChannel& channel) {
+ AutoMutex _l(mLock);
+ // check Fd flags
+ checkFdFlags(channel.getFd());
+
+ auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
+ [&](std::shared_ptr<InputChannel>& c) { return *c == channel; });
+ if (it != mInputChannels.end()) {
+ mInputChannels.erase(it);
+ }
+
+ return binder::Status::ok();
+}
+
+status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
+ std::string dump;
+
+ dump += " InputFlinger dump\n";
+
+ ::write(fd, dump.c_str(), dump.size());
+ return NO_ERROR;
+}
+
+binder::Status TestInputManager::getInputWindows(
+ std::vector<::android::InputWindowInfo>* inputInfos) {
+ for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
+ for (auto& inputHandle : inputHandles) {
+ inputInfos->push_back(*inputHandle->getInfo());
+ }
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
+ channels->clear();
+ for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
+ channels->push_back(*channel);
+ }
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) {
+ *request = mFocusRequest;
+ return binder::Status::ok();
+}
+
+binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
+ mFocusRequest = request;
+ return binder::Status::ok();
+}
+
+void InputFlingerServiceTest::SetUp() {
+ mSetInputWindowsListener = new SetInputWindowsListener([&]() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mSetInputWindowsFinishedCondition.notify_all();
+ });
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+
+ mInfo.token = TestInfoToken;
+ mInfo.id = TestInfoId;
+ mInfo.name = TestInfoName;
+ mInfo.flags = TestInfoFlags;
+ mInfo.type = TestInfoType;
+ mInfo.dispatchingTimeout = TestInfoDispatchingTimeout;
+ mInfo.frameLeft = TestInfoFrameLeft;
+ mInfo.frameTop = TestInfoFrameTop;
+ mInfo.frameRight = TestInfoFrameRight;
+ mInfo.frameBottom = TestInfoFrameBottom;
+ mInfo.surfaceInset = TestInfoSurfaceInset;
+ mInfo.globalScaleFactor = TestInfoGlobalScaleFactor;
+ mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale,
+ TestInfoFrameTop, 0, 0, 1});
+ mInfo.touchableRegion = TestInfoTouchableRegion;
+ mInfo.visible = TestInfoVisible;
+ mInfo.canReceiveKeys = TestInfoCanReceiveKeys;
+ mInfo.trustedOverlay = TestInfoTrustedOverlay;
+ mInfo.hasFocus = TestInfoHasFocus;
+ mInfo.hasWallpaper = TestInfoHasWallpaper;
+ mInfo.paused = TestInfoPaused;
+ mInfo.ownerPid = TestInfoOwnerPid;
+ mInfo.ownerUid = TestInfoOwnerUid;
+ mInfo.inputFeatures = TestInfoInputFeatures;
+ mInfo.displayId = TestInfoDisplayId;
+ mInfo.portalToDisplayId = TestInfoPortalToDisplayId;
+ mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop;
+ mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle;
+
+ mInfo.applicationInfo.name = TestAppInfoName;
+ mInfo.applicationInfo.token = TestAppInfoToken;
+ mInfo.applicationInfo.dispatchingTimeout = TestAppInfoDispatchingTimeout;
+
+ InitializeInputFlinger();
+}
+
+void InputFlingerServiceTest::TearDown() {}
+
+void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
+ EXPECT_EQ(mInfo, info);
+}
+
+void InputFlingerServiceTest::InitializeInputFlinger() {
+ sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
+ ASSERT_TRUE(input != nullptr);
+ mService = interface_cast<IInputFlinger>(input);
+
+ input = defaultServiceManager()->waitForService(kQueryServiceName);
+ ASSERT_TRUE(input != nullptr);
+ mQuery = interface_cast<IInputFlingerQuery>(input);
+}
+
+void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) {
+ std::unique_lock<std::mutex> lock(mLock);
+ mService->setInputWindows(infos, mSetInputWindowsListener);
+ // Verify listener call
+ EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout);
+}
+
+void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token,
+ const sp<IBinder> focusedToken,
+ nsecs_t timestampNanos) {
+ FocusRequest request;
+ request.token = TestInfoToken;
+ request.focusedToken = focusedToken;
+ request.timestamp = timestampNanos;
+ mService->setFocusedWindow(request);
+ // call set input windows and wait for the callback to drain the queue.
+ setInputWindowsByInfos(std::vector<InputWindowInfo>());
+}
+
+/**
+ * Test InputFlinger service interface SetInputWindows
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
+ std::vector<InputWindowInfo> infos = {getInfo()};
+ setInputWindowsByInfos(infos);
+
+ // Verify input windows from service
+ std::vector<::android::InputWindowInfo> windowInfos;
+ mQuery->getInputWindows(&windowInfos);
+ for (const ::android::InputWindowInfo& windowInfo : windowInfos) {
+ verifyInputWindowInfo(windowInfo);
+ }
+}
+
+/**
+ * Test InputFlinger service interface registerInputChannel
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannel) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+
+ InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
+ mService->registerInputChannel(*serverChannel);
+
+ std::vector<::android::InputChannel> channels;
+ mQuery->getInputChannels(&channels);
+ ASSERT_EQ(channels.size(), 1UL);
+ EXPECT_EQ(channels[0], *serverChannel);
+
+ mService->unregisterInputChannel(*serverChannel);
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+}
+
+/**
+ * Test InputFlinger service interface registerInputChannel with invalid cases
+ */
+TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannelInvalid) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
+
+ std::vector<::android::InputChannel> channels;
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+
+ mService->registerInputChannel(InputChannel());
+ mService->unregisterInputChannel(*clientChannel);
+
+ mService->registerInputChannel(*serverChannel);
+ mService->registerInputChannel(*clientChannel);
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 2UL);
+
+ mService->unregisterInputChannel(*clientChannel);
+ mService->unregisterInputChannel(*serverChannel);
+ mQuery->getInputChannels(&channels);
+ EXPECT_EQ(channels.size(), 0UL);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now);
+
+ FocusRequest request;
+ mQuery->getLastFocusRequest(&request);
+
+ EXPECT_EQ(request.token, TestInfoToken);
+ EXPECT_EQ(request.focusedToken, nullptr);
+ EXPECT_EQ(request.timestamp, now);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now);
+
+ FocusRequest request;
+ mQuery->getLastFocusRequest(&request);
+
+ EXPECT_EQ(request.token, TestInfoToken);
+ EXPECT_EQ(request.focusedToken, FocusedTestInfoToken);
+ EXPECT_EQ(request.timestamp, now);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ pid_t forkPid = fork();
+
+ if (forkPid == 0) {
+ // Server process
+ android::sp<android::TestInputManager> manager = new android::TestInputManager();
+ android::sp<android::TestInputQuery> query = new android::TestInputQuery(manager);
+
+ android::defaultServiceManager()->addService(android::kTestServiceName, manager,
+ false /*allowIsolated*/);
+ android::defaultServiceManager()->addService(android::kQueryServiceName, query,
+ false /*allowIsolated*/);
+ android::ProcessState::self()->startThreadPool();
+ android::IPCThreadState::self()->joinThreadPool();
+ } else {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ kill(forkPid, SIGKILL);
+ return result;
+ }
+ return 0;
+}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 109edfe..21dd3c7 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -27,12 +27,13 @@
#include <TestInputListener.h>
#include <TouchInputMapper.h>
#include <UinputDevice.h>
-
#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include <inttypes.h>
#include <math.h>
+#include <memory>
+
namespace android {
using std::chrono_literals::operator""ms;
@@ -44,16 +45,24 @@
static const nsecs_t ARBITRARY_TIME = 1234;
// Arbitrary display properties.
-static const int32_t DISPLAY_ID = 0;
-static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
-static const int32_t DISPLAY_WIDTH = 480;
-static const int32_t DISPLAY_HEIGHT = 800;
-static const int32_t VIRTUAL_DISPLAY_ID = 1;
-static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
-static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
+static constexpr int32_t VIRTUAL_DISPLAY_WIDTH = 400;
+static constexpr int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+static constexpr int32_t FIRST_SLOT = 0;
+static constexpr int32_t SECOND_SLOT = 1;
+static constexpr int32_t THIRD_SLOT = 2;
+static constexpr int32_t INVALID_TRACKING_ID = -1;
+static constexpr int32_t FIRST_TRACKING_ID = 0;
+static constexpr int32_t SECOND_TRACKING_ID = 1;
+static constexpr int32_t THIRD_TRACKING_ID = 2;
+
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
@@ -76,15 +85,14 @@
int32_t mButtonState;
int32_t mDisplayId;
-protected:
- virtual ~FakePointerController() { }
-
public:
FakePointerController() :
mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
}
+ virtual ~FakePointerController() {}
+
void setBounds(float minX, float minY, float maxX, float maxY) {
mHaveBounds = true;
mMinX = minX;
@@ -176,7 +184,7 @@
std::condition_variable mDevicesChangedCondition;
InputReaderConfiguration mConfig;
- KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
+ std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers;
std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
bool mInputDevicesChanged GUARDED_BY(mLock){false};
std::vector<DisplayViewport> mViewports;
@@ -256,8 +264,8 @@
void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
- void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
- mPointerControllers.add(deviceId, controller);
+ void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) {
+ mPointerControllers.insert_or_assign(deviceId, std::move(controller));
}
const InputReaderConfiguration* getReaderConfiguration() const {
@@ -318,8 +326,8 @@
*outConfig = mConfig;
}
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
- return mPointerControllers.valueFor(deviceId);
+ virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
+ return mPointerControllers[deviceId];
}
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
@@ -813,8 +821,7 @@
return false;
}
- virtual void vibrate(int32_t, nsecs_t) {
- }
+ virtual void vibrate(int32_t, const VibrationElement&) {}
virtual void cancelVibrate(int32_t) {
}
@@ -847,7 +854,7 @@
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
int32_t mNextId;
- wp<PointerControllerInterface> mPointerController;
+ std::weak_ptr<PointerControllerInterface> mPointerController;
public:
FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
@@ -876,7 +883,7 @@
}
void updatePointerDisplay() {
- sp<PointerControllerInterface> controller = mPointerController.promote();
+ std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
if (controller != nullptr) {
InputReaderConfiguration config;
mPolicy->getReaderConfiguration(&config);
@@ -913,8 +920,8 @@
virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
- virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) {
- sp<PointerControllerInterface> controller = mPointerController.promote();
+ virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) {
+ std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
if (controller == nullptr) {
controller = mPolicy->obtainPointerController(deviceId);
mPointerController = controller;
@@ -1188,20 +1195,21 @@
// We didn't add any viewports yet, so there shouldn't be any.
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, uniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Check matching by uniqueId
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_TRUE(internalViewport);
- ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
+ ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type);
// Check matching by viewport type
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(uniqueId, internalViewport->uniqueId);
@@ -1209,7 +1217,7 @@
// Make sure nothing is found after clear
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_FALSE(internalViewport);
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_FALSE(internalViewport);
}
@@ -1223,26 +1231,30 @@
// Add an internal viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Add an external viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT,
+ ViewportType::EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT,
+ ViewportType::VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT,
+ ViewportType::VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
// Check matching by type for external
std::optional<DisplayViewport> externalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL);
ASSERT_TRUE(externalViewport);
ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
@@ -1250,7 +1262,7 @@
std::optional<DisplayViewport> virtualViewport1 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
ASSERT_TRUE(virtualViewport1);
- ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
+ ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type);
ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
@@ -1258,7 +1270,7 @@
std::optional<DisplayViewport> virtualViewport2 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
ASSERT_TRUE(virtualViewport2);
- ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
+ ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type);
ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
}
@@ -1275,8 +1287,8 @@
constexpr int32_t displayId1 = 2;
constexpr int32_t displayId2 = 3;
- std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
- ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+ std::vector<ViewportType> types = {ViewportType::INTERNAL, ViewportType::EXTERNAL,
+ ViewportType::VIRTUAL};
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
@@ -1314,7 +1326,7 @@
* Check getDisplayViewportByPort
*/
TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
const std::string uniqueId1 = "uniqueId1";
const std::string uniqueId2 = "uniqueId2";
constexpr int32_t displayId1 = 1;
@@ -1708,9 +1720,11 @@
// Add default and second display.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, "local:1", hdmi1,
+ ViewportType::EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
@@ -1848,13 +1862,31 @@
ASSERT_LE(prevTimestamp, keyArgs.eventTime);
}
+/**
+ * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
+ * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
+ * are passed to the listener.
+ */
+static_assert(BTN_GEAR_DOWN == BTN_WHEEL);
+TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) {
+ std::unique_ptr<UinputSteamController> controller = createUinputDevice<UinputSteamController>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ NotifyKeyArgs keyArgs;
+
+ controller->pressAndReleaseKey(BTN_GEAR_DOWN);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP
+ ASSERT_EQ(BTN_GEAR_DOWN, keyArgs.scanCode);
+
+ controller->pressAndReleaseKey(BTN_GEAR_UP);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP
+ ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode);
+}
+
// --- TouchProcessTest ---
class TouchIntegrationTest : public InputReaderIntegrationTest {
protected:
- static const int32_t FIRST_SLOT = 0;
- static const int32_t SECOND_SLOT = 1;
- static const int32_t FIRST_TRACKING_ID = 0;
- static const int32_t SECOND_TRACKING_ID = 1;
const std::string UNIQUE_ID = "local:0";
virtual void SetUp() override {
@@ -1862,7 +1894,7 @@
// At least add an internal display.
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
- ViewportType::VIEWPORT_INTERNAL);
+ ViewportType::INTERNAL);
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1925,9 +1957,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
// ACTION_POINTER_UP (Second slot)
- mDevice->sendUp();
+ mDevice->sendPointerUp();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
args.action);
// ACTION_UP
@@ -1942,11 +1974,13 @@
const Point centerPoint = mDevice->getCenterPoint();
// ACTION_DOWN
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
mDevice->sendDown(centerPoint);
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- // ACTION_POINTER_DOWN (Second slot)
+ // ACTION_POINTER_DOWN (second slot)
const Point secondPoint = centerPoint + Point(100, 100);
mDevice->sendSlot(SECOND_SLOT);
mDevice->sendTrackingId(SECOND_TRACKING_ID);
@@ -1955,26 +1989,31 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
args.action);
- // ACTION_MOVE (Second slot)
+ // ACTION_MOVE (second slot)
mDevice->sendMove(secondPoint + Point(1, 1));
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
- // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+ // Send MT_TOOL_PALM (second slot), which indicates that the touch IC has determined this to be
+ // a palm event.
+ // Expect to receive the ACTION_POINTER_UP with cancel flag.
mDevice->sendToolType(MT_TOOL_PALM);
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags);
- // ACTION_POINTER_UP (Second slot)
- mDevice->sendUp();
+ // Send up to second slot, expect first slot send moving.
+ mDevice->sendPointerUp();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- // ACTION_UP
+ // Send ACTION_UP (first slot)
mDevice->sendSlot(FIRST_SLOT);
mDevice->sendUp();
- // Expect no event received after abort the entire gesture.
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
// --- InputDeviceTest ---
@@ -2198,8 +2237,7 @@
// Prepare displays.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
- ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, ViewportType::INTERNAL);
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
@@ -2326,9 +2364,9 @@
ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
}
- static void assertPosition(const sp<FakePointerController>& controller, float x, float y) {
+ static void assertPosition(const FakePointerController& controller, float x, float y) {
float actualX, actualY;
- controller->getPosition(&actualX, &actualY);
+ controller.getPosition(&actualX, &actualY);
ASSERT_NEAR(x, actualX, 1);
ASSERT_NEAR(y, actualY, 1);
}
@@ -2399,8 +2437,8 @@
* orientation.
*/
void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+ NO_PORT, ViewportType::INTERNAL);
}
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
@@ -2705,7 +2743,7 @@
// ^--- already checked by the previous test
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2715,7 +2753,7 @@
constexpr int32_t newDisplayId = 2;
clearViewports();
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2876,9 +2914,9 @@
// Prepare second display.
constexpr int32_t newDisplayId = 2;
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+ SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2999,12 +3037,12 @@
protected:
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
- sp<FakePointerController> mFakePointerController;
+ std::shared_ptr<FakePointerController> mFakePointerController;
virtual void SetUp() override {
InputMapperTest::SetUp();
- mFakePointerController = new FakePointerController();
+ mFakePointerController = std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
}
@@ -3013,7 +3051,7 @@
void prepareDisplay(int32_t orientation) {
const std::string uniqueId = "local:0";
- const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+ const ViewportType viewportType = ViewportType::INTERNAL;
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
orientation, uniqueId, NO_PORT, viewportType);
}
@@ -3660,7 +3698,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, Process_PointerCapture) {
@@ -3688,7 +3726,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
+ ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
// Button press.
process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
@@ -3727,7 +3765,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
+ ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
// Disable pointer capture and check that the device generation got bumped
// and events are generated the usual way.
@@ -3748,7 +3786,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
@@ -3758,8 +3796,7 @@
constexpr int32_t SECOND_DISPLAY_ID = 1;
const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
- SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
- ViewportType::VIEWPORT_EXTERNAL);
+ SECOND_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::EXTERNAL);
mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3776,7 +3813,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
}
@@ -3886,8 +3923,8 @@
};
void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
- UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
+ port, ViewportType::INTERNAL);
}
void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
@@ -3896,9 +3933,9 @@
}
void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
- VIRTUAL_DISPLAY_HEIGHT, orientation,
- VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
+ orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::VIRTUAL);
}
void TouchInputMapperTest::prepareVirtualKeys() {
@@ -6751,7 +6788,7 @@
const uint8_t hdmi1 = 0;
const uint8_t hdmi2 = 1;
const std::string secondaryUniqueId = "uniqueId2";
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareAxes(POSITION);
@@ -6784,14 +6821,15 @@
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
// Setup for second display.
- sp<FakePointerController> fakePointerController = new FakePointerController();
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(100, 200);
fakePointerController->setButtonState(0);
mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL);
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION);
@@ -6844,7 +6882,8 @@
device2->reset(ARBITRARY_TIME);
// Setup PointerController.
- sp<FakePointerController> fakePointerController = new FakePointerController();
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController);
@@ -6857,11 +6896,11 @@
// Create displays.
prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL, hdmi2);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
// Default device will reconfigure above, need additional reconfiguration for another device.
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -6968,7 +7007,7 @@
TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
constexpr uint8_t hdmi2 = 1;
const std::string secondaryUniqueId = "uniqueId2";
- constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ constexpr ViewportType type = ViewportType::EXTERNAL;
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
@@ -7035,10 +7074,10 @@
}
/**
- * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
- * UP events should be ignored.
+ * Test single touch should be canceled when received the MT_TOOL_PALM event, and the following
+ * MOVE and UP events should be ignored.
*/
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7048,7 +7087,7 @@
// default tool type is finger
constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
- processId(mapper, 1);
+ processId(mapper, FIRST_TRACKING_ID);
processPosition(mapper, x1, y1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7062,19 +7101,19 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
// Ignore the following MOVE and UP events if had detect a palm event.
- processId(mapper, 1);
+ processId(mapper, FIRST_TRACKING_ID);
processPosition(mapper, x2, y2);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// finger up.
- processId(mapper, -1);
+ processId(mapper, INVALID_TRACKING_ID);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// new finger down
+ processId(mapper, FIRST_TRACKING_ID);
processToolType(mapper, MT_TOOL_FINGER);
- processId(mapper, 1);
processPosition(mapper, x3, y3);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7083,11 +7122,10 @@
}
/**
- * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger,
- * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is
- * MT_TOOL_FINGER.
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the rest active fingers could still be allowed to receive the events
*/
-TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) {
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
@@ -7096,8 +7134,8 @@
NotifyMotionArgs motionArgs;
// default tool type is finger
- constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
- processId(mapper, 1);
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+ processId(mapper, FIRST_TRACKING_ID);
processPosition(mapper, x1, y1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7105,51 +7143,232 @@
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
// Second finger down.
- processSlot(mapper, 1);
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
processPosition(mapper, x2, y2);
- processId(mapper, 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+
+ // If the tool type of the first finger changes to MT_TOOL_PALM,
+ // we expect to receive ACTION_POINTER_UP with cancel flag.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+ // The following MOVE events of second finger should be processed.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2 + 1, y2 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for
+ // it. Second finger receive move.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // Second finger keeps moving.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2 + 2, y2 + 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // Second finger up.
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event, if only 1 finger
+ * is active, it should send CANCEL after receiving the MT_TOOL_PALM event.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- // If the tool type of the first pointer changes to MT_TOOL_PALM,
- // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL.
- processSlot(mapper, 0);
- processId(mapper, 1);
+ // If the tool type of the first finger changes to MT_TOOL_PALM,
+ // we expect to receive ACTION_POINTER_UP with cancel flag.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+ // Second finger keeps moving.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2 + 1, y2 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // second finger becomes palm, receive cancel due to only 1 finger is active.
+ processId(mapper, SECOND_TRACKING_ID);
processToolType(mapper, MT_TOOL_PALM);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
- // Ignore the following MOVE and UP events if had detect a palm event.
- processSlot(mapper, 1);
- processId(mapper, 2);
+ // third finger down.
+ processSlot(mapper, THIRD_SLOT);
+ processId(mapper, THIRD_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_FINGER);
processPosition(mapper, x3, y3);
processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // second finger up.
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // first finger move, but still in palm
- processSlot(mapper, 0);
- processId(mapper, 1);
- processPosition(mapper, x1 - 1, y1 - 1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // second finger down, expect as new finger down.
- processSlot(mapper, 1);
- processId(mapper, 2);
- processPosition(mapper, x2, y2);
- processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // third finger move
+ processId(mapper, THIRD_TRACKING_ID);
+ processPosition(mapper, x3 + 1, y3 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // first finger up, third finger receive move.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // second finger up, third finger receive move.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // third finger up.
+ processSlot(mapper, THIRD_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+}
+
+/**
+ * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger,
+ * and the active finger could still be allowed to receive the events
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // default tool type is finger
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220;
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // If the tool type of the second finger changes to MT_TOOL_PALM,
+ // we expect to receive ACTION_POINTER_UP with cancel flag.
+ processId(mapper, SECOND_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
+
+ // The following MOVE event should be processed.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1 + 1, y1 + 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+ // second finger up.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // first finger keep moving
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1 + 2, y1 + 2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+ // first finger up.
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
}
// --- MultiTouchInputMapperTest_ExternalDevice ---
@@ -7182,7 +7401,7 @@
ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
// Expect the event to be sent to the external viewport if it is present.
- prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+ prepareSecondaryDisplay(ViewportType::EXTERNAL);
processPosition(mapper, 100, 100);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -7196,7 +7415,7 @@
protected:
void halfDisplayToCenterHorizontal(int32_t orientation) {
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
// Half display to (width/4, 0, width * 3/4, height) to make display has offset.
internalViewport->orientation = orientation;
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 10e7293..7fec2c8 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -125,6 +125,9 @@
pressAndReleaseKey(KEY_HOME);
}
+// --- UinputSteamController
+UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+
// --- UinputTouchScreen ---
UinputTouchScreen::UinputTouchScreen(const Rect* size)
: UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
@@ -176,6 +179,11 @@
injectEvent(EV_SYN, SYN_REPORT, 0);
}
+void UinputTouchScreen::sendPointerUp() {
+ sendTrackingId(0xffffffff);
+ injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
void UinputTouchScreen::sendUp() {
sendTrackingId(0xffffffff);
injectEvent(EV_KEY, BTN_TOUCH, 0);
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index ec3cd9f..01a557c 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -108,6 +108,16 @@
UinputHomeKey();
};
+// A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key.
+class UinputSteamController : public UinputKeyboard {
+public:
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+ UinputSteamController();
+};
+
// --- UinputTouchScreen ---
// A touch screen device with specific size.
class UinputTouchScreen : public UinputDevice {
@@ -129,6 +139,7 @@
void sendTrackingId(int32_t trackingId);
void sendDown(const Point& point);
void sendMove(const Point& point);
+ void sendPointerUp();
void sendUp();
void sendToolType(int32_t toolType);
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index b0d3e3b..ec3dfc8 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -2,9 +2,14 @@
name: "libpowermanager",
srcs: [
- "IPowerManager.cpp",
- "Temperature.cpp",
+ "BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
+ "PowerHalController.cpp",
+ "PowerHalLoader.cpp",
+ "PowerHalWrapper.cpp",
+ "PowerSaveState.cpp",
+ "Temperature.cpp",
+ "WorkSource.cpp",
":libpowermanager_aidl",
],
@@ -17,9 +22,13 @@
},
shared_libs: [
- "libutils",
"libbinder",
- "liblog"
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
],
cflags: [
@@ -34,22 +43,3 @@
"include",
],
}
-
-cc_test {
- name: "thermalmanager-test",
- srcs: ["IThermalManagerTest.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- shared_libs: [
- "libbase",
- "libhidlbase",
- "liblog",
- "libpowermanager",
- "libbinder",
- "libutils",
- ],
-}
diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp
new file mode 100644
index 0000000..ee55b6b
--- /dev/null
+++ b/services/powermanager/BatterySaverPolicyConfig.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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 "BatterySaverPolicyConfig"
+
+#include <android/BatterySaverPolicyConfig.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) {
+ int32_t num = 0;
+ status_t ret = parcel->readInt32(&num);
+ if (ret != OK) {
+ return ret;
+ }
+ for (int i = 0; i < num; i++) {
+ String16 key, val;
+ ret = parcel->readString16(&key) ?:
+ parcel->readString16(&val);
+ if (ret != OK) {
+ return ret;
+ }
+ mDeviceSpecificSettings.emplace_back(key, val);
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readFloat(&mAdjustBrightnessFactor)
+ ?: parcel->readBool(&mAdvertiseIsEnabled)
+ ?: parcel->readBool(&mDeferFullBackup)
+ ?: parcel->readBool(&mDeferKeyValueBackup)
+ ?: readDeviceSpecificSettings(parcel)
+ ?: parcel->readBool(&mDisableAnimation)
+ ?: parcel->readBool(&mDisableAod)
+ ?: parcel->readBool(&mDisableLaunchBoost)
+ ?: parcel->readBool(&mDisableOptionalSensors)
+ ?: parcel->readBool(&mDisableSoundTrigger)
+ ?: parcel->readBool(&mDisableVibration)
+ ?: parcel->readBool(&mEnableAdjustBrightness)
+ ?: parcel->readBool(&mEnableDataSaver)
+ ?: parcel->readBool(&mEnableFirewall)
+ ?: parcel->readBool(&mEnableNightMode)
+ ?: parcel->readBool(&mEnableQuickDoze)
+ ?: parcel->readBool(&mForceAllAppsStandby)
+ ?: parcel->readBool(&mForceBackgroundCheck)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode));
+}
+
+status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const {
+ status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size());
+ if (ret != OK) {
+ return ret;
+ }
+ for (auto& settings : mDeviceSpecificSettings) {
+ ret = parcel->writeString16(settings.first) ?:
+ parcel->writeString16(settings.second);
+ if (ret != OK) {
+ return ret;
+ }
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeFloat(mAdjustBrightnessFactor)
+ ?: parcel->writeBool(mAdvertiseIsEnabled)
+ ?: parcel->writeBool(mDeferFullBackup)
+ ?: parcel->writeBool(mDeferKeyValueBackup)
+ ?: writeDeviceSpecificSettings(parcel)
+ ?: parcel->writeBool(mDisableAnimation)
+ ?: parcel->writeBool(mDisableAod)
+ ?: parcel->writeBool(mDisableLaunchBoost)
+ ?: parcel->writeBool(mDisableOptionalSensors)
+ ?: parcel->writeBool(mDisableSoundTrigger)
+ ?: parcel->writeBool(mDisableVibration)
+ ?: parcel->writeBool(mEnableAdjustBrightness)
+ ?: parcel->writeBool(mEnableDataSaver)
+ ?: parcel->writeBool(mEnableFirewall)
+ ?: parcel->writeBool(mEnableNightMode)
+ ?: parcel->writeBool(mEnableQuickDoze)
+ ?: parcel->writeBool(mForceAllAppsStandby)
+ ?: parcel->writeBool(mForceBackgroundCheck)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode));
+}
+
+} // namespace android::os
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
deleted file mode 100644
index ea3a831..0000000
--- a/services/powermanager/IPowerManager.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2011 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 "IPowerManager"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <powermanager/IPowerManager.h>
-
-namespace android {
-
-class BpPowerManager : public BpInterface<IPowerManager>
-{
-public:
- explicit BpPowerManager(const sp<IBinder>& impl)
- : BpInterface<IPowerManager>(impl)
- {
- }
-
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(0); // no WorkSource
- data.writeString16(NULL, 0); // no history tag
- return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(uid); // uid to blame for the work
- return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- return remote()->transact(RELEASE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay) {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32Array(len, uids);
- return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t powerHint(int hintId, int param)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(hintId);
- data.writeInt32(param);
- // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it
- return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt64(event_time_ms);
- data.writeInt32(reason);
- data.writeInt32(flags);
- return remote()->transact(GO_TO_SLEEP, data, &reply, 0);
- }
-
- virtual status_t reboot(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(REBOOT, data, &reply, 0);
- }
-
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(SHUTDOWN, data, &reply, 0);
- }
-
- virtual status_t crash(const String16& message)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeString16(message);
- return remote()->transact(CRASH, data, &reply, 0);
- }
-};
-
-IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
new file mode 100644
index 0000000..178f545
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalController"
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHalLoader.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+std::unique_ptr<HalWrapper> HalConnector::connect() {
+ sp<IPower> halAidl = PowerHalLoader::loadAidl();
+ if (halAidl) {
+ return std::make_unique<AidlHalWrapper>(halAidl);
+ }
+ sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
+ sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
+ if (halHidlV1_1) {
+ return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+ }
+ if (halHidlV1_0) {
+ return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
+ }
+ return nullptr;
+}
+
+void HalConnector::reset() {
+ PowerHalLoader::unloadAll();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalController::init() {
+ initHal();
+}
+
+// Check validity of current handle to the power HAL service, and create a new
+// one if necessary.
+std::shared_ptr<HalWrapper> PowerHalController::initHal() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect();
+ if (mConnectedHal == nullptr) {
+ // Unable to connect to Power HAL service. Fallback to default.
+ return mDefaultHal;
+ }
+ }
+ return mConnectedHal;
+}
+
+// Check if a call to Power HAL function failed; if so, log the failure and
+// invalidate the current Power HAL handle.
+HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) {
+ if (result == HalResult::FAILED) {
+ ALOGE("%s() failed: power HAL service not available.", fnName);
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ // Drop Power HAL handle. This will force future api calls to reconnect.
+ mConnectedHal = nullptr;
+ mHalConnector->reset();
+ }
+ return result;
+}
+
+HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setBoost(boost, durationMs);
+ return processHalResult(result, "setBoost");
+}
+
+HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setMode(mode, enabled);
+ return processHalResult(result, "setMode");
+}
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
new file mode 100644
index 0000000..1f1b43a
--- /dev/null
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalLoader"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <powermanager/PowerHalLoader.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T, typename F>
+sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) {
+ if (!exists) {
+ return nullptr;
+ }
+ if (hal) {
+ return hal;
+ }
+ hal = loadFn();
+ if (hal) {
+ ALOGV("Successfully connected to Power HAL %s service.", halName);
+ } else {
+ ALOGV("Power HAL %s service not available.", halName);
+ exists = false;
+ }
+ return hal;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::mutex PowerHalLoader::gHalMutex;
+sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
+sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
+
+void PowerHalLoader::unloadAll() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ gHalAidl = nullptr;
+ gHalHidlV1_0 = nullptr;
+ gHalHidlV1_1 = nullptr;
+}
+
+sp<IPower> PowerHalLoader::loadAidl() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return waitForVintfService<IPower>(); };
+ return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ return loadHidlV1_0Locked();
+}
+
+sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_0::IPower::getService(); };
+ return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
new file mode 100644
index 0000000..95f8623
--- /dev/null
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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 "HalWrapper"
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+inline HalResult toHalResult(const binder::Status& result) {
+ return result.isOk() ? HalResult::SUCCESSFUL : HalResult::FAILED;
+}
+
+template <typename T>
+inline HalResult toHalResult(const hardware::Return<T>& result) {
+ return result.isOk() ? HalResult::SUCCESSFUL : HalResult::FAILED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
+ toString(boost).c_str(), durationMs);
+ return HalResult::UNSUPPORTED;
+}
+
+HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+ ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
+ enabled ? "true" : "false");
+ return HalResult::UNSUPPORTED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+ if (boost == Boost::INTERACTION) {
+ return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+ } else {
+ ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+}
+
+HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ switch (mode) {
+ case Mode::LAUNCH:
+ return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+ case Mode::LOW_POWER:
+ return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+ case Mode::SUSTAINED_PERFORMANCE:
+ return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+ case Mode::VR:
+ return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+ case Mode::INTERACTIVE:
+ return setInteractive(enabled);
+ case Mode::DOUBLE_TAP_TO_WAKE:
+ return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
+ default:
+ ALOGV("Skipped setMode %s because Power HAL AIDL not available",
+ toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+}
+
+HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ return toHalResult(mHandleV1_0->powerHint(hintId, data));
+}
+
+HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
+ return toHalResult(mHandleV1_0->setInteractive(enabled));
+}
+
+HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+ return toHalResult(mHandleV1_0->setFeature(feature, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ std::unique_lock<std::mutex> lock(mBoostMutex);
+ // Quick return if boost is not supported by HAL
+ if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
+ mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+
+ if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGV("Skipped setBoost %s because Power HAL is not available to check support",
+ toString(boost).c_str());
+ return HalResult::FAILED;
+ }
+
+ mBoostSupportedArray[static_cast<int32_t>(boost)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
+ toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setBoost(boost, durationMs));
+}
+
+HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
+ std::unique_lock<std::mutex> lock(mModeMutex);
+ // Quick return if mode is not supported by HAL
+ if (mode > Mode::DISPLAY_INACTIVE ||
+ mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+
+ if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGV("Skipped setMode %s because Power HAL is not available to check support",
+ toString(mode).c_str());
+ return HalResult::FAILED;
+ }
+
+ mModeSupportedArray[static_cast<int32_t>(mode)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it",
+ toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setMode(mode, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp
new file mode 100644
index 0000000..6d1830a
--- /dev/null
+++ b/services/powermanager/PowerSaveState.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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 "PowerSaveState"
+
+#include <android/PowerSaveState.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readBool(&mBatterySaverEnabled)
+ ?: parcel->readBool(&mGlobalBatterySaverEnabled)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readFloat(&mBrightnessFactor);
+}
+
+status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeBool(mBatterySaverEnabled)
+ ?: parcel->writeBool(mGlobalBatterySaverEnabled)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeFloat(mBrightnessFactor);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING
new file mode 100644
index 0000000..caaec55
--- /dev/null
+++ b/services/powermanager/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libpowermanager_test"
+ }
+ ]
+}
diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
new file mode 100644
index 0000000..1006a06
--- /dev/null
+++ b/services/powermanager/WorkSource.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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 "WorkSource"
+
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t WorkSource::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ int32_t num;
+ status_t ret = parcel->readInt32(&num)
+ ?: parcel->readInt32Vector(&mUids)
+ ?: parcel->readString16Vector(&mNames);
+
+ return ret;
+}
+
+status_t WorkSource::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeInt32(mUids.size())
+ ?: parcel->writeInt32Vector(mUids)
+ ?: parcel->writeString16Vector(mNames);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
new file mode 100644
index 0000000..5975269
--- /dev/null
+++ b/services/powermanager/benchmarks/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_benchmark {
+ name: "libpowermanager_benchmarks",
+ srcs: [
+ "main.cpp",
+ "PowerHalAidlBenchmarks.cpp",
+ "PowerHalControllerBenchmarks.cpp",
+ "PowerHalHidlBenchmarks.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/powermanager/benchmarks/AndroidTest.xml b/services/powermanager/benchmarks/AndroidTest.xml
new file mode 100644
index 0000000..40f4872
--- /dev/null
+++ b/services/powermanager/benchmarks/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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 libpowermanager benchmarks">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libpowermanager_benchmarks->/data/benchmarktest/benchmark" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/benchmarktest" />
+ <option name="benchmark-module-name" value="benchmark" />
+ </test>
+</configuration>
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
new file mode 100644
index 0000000..a6dad51
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalAidlBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+#include <benchmark/benchmark.h>
+
+#include <binder/IServiceManager.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+
+using namespace android;
+
+template <class R, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, R (IPower::*fn)(Args0...), Args1&&... args1) {
+ sp<IPower> hal = waitForVintfService<IPower>();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL AIDL not available, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ (*hal.*fn)(std::forward<Args1>(args1)...);
+ }
+}
+
+static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
+ bool isSupported;
+ runBenchmark(state, &IPower::isBoostSupported, Boost::INTERACTION, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_isModeSupported(benchmark::State& state) {
+ bool isSupported;
+ runBenchmark(state, &IPower::isModeSupported, Mode::INTERACTIVE, &isSupported);
+}
+
+static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) {
+ runBenchmark(state, &IPower::setBoost, Boost::INTERACTION, 0);
+}
+
+static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) {
+ runBenchmark(state, &IPower::setMode, Mode::INTERACTIVE, false);
+}
+
+BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported);
+BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost);
+BENCHMARK(BM_PowerHalAidlBenchmarks_setMode);
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..a3a1f4e
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalControllerBenchmarks"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+
+#include <benchmark/benchmark.h>
+
+#include <powermanager/PowerHalController.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::power::PowerHalController;
+
+using namespace android;
+
+static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ controller.init();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_initCached(benchmark::State& state) {
+ PowerHalController controller;
+ // First connection out of test.
+ controller.init();
+
+ while (state.KeepRunning()) {
+ controller.init();
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ controller.setBoost(Boost::INTERACTION, 0);
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) {
+ PowerHalController controller;
+ // First call out of test, to cache supported boost.
+ controller.setBoost(Boost::INTERACTION, 0);
+
+ while (state.KeepRunning()) {
+ controller.setBoost(Boost::INTERACTION, 0);
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ PowerHalController controller;
+ controller.setMode(Mode::INTERACTIVE, false);
+ }
+}
+
+static void BM_PowerHalControllerBenchmarks_setModeCached(benchmark::State& state) {
+ PowerHalController controller;
+ // First call out of test, to cache supported mode.
+ controller.setMode(Mode::INTERACTIVE, false);
+
+ while (state.KeepRunning()) {
+ controller.setMode(Mode::INTERACTIVE, false);
+ }
+}
+
+BENCHMARK(BM_PowerHalControllerBenchmarks_init);
+BENCHMARK(BM_PowerHalControllerBenchmarks_initCached);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoost);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setBoostCached);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setMode);
+BENCHMARK(BM_PowerHalControllerBenchmarks_setModeCached);
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
new file mode 100644
index 0000000..5542ac4
--- /dev/null
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalHidlBenchmarks"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+#include <benchmark/benchmark.h>
+
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using IPower1_0 = android::hardware::power::V1_0::IPower;
+using IPower1_1 = android::hardware::power::V1_1::IPower;
+
+using namespace android;
+
+template <class R, class I, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, R (I::*fn)(Args0...), Args1&&... args1) {
+ sp<I> hal = I::getService();
+
+ if (hal == nullptr) {
+ ALOGI("Power HAL HIDL not available, skipping test...");
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ (*hal.*fn)(std::forward<Args1>(args1)...);
+ }
+}
+
+static void BM_PowerHalHidlBenchmarks_setFeature(benchmark::State& state) {
+ runBenchmark(state, &IPower1_0::setFeature, Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, false);
+}
+
+static void BM_PowerHalHidlBenchmarks_setInteractive(benchmark::State& state) {
+ runBenchmark(state, &IPower1_0::setInteractive, false);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHint(benchmark::State& state) {
+ runBenchmark(state, &IPower1_0::powerHint, PowerHint::INTERACTION, 0);
+}
+
+static void BM_PowerHalHidlBenchmarks_powerHintAsync(benchmark::State& state) {
+ runBenchmark(state, &IPower1_1::powerHintAsync, PowerHint::INTERACTION, 0);
+}
+
+BENCHMARK(BM_PowerHalHidlBenchmarks_setFeature);
+BENCHMARK(BM_PowerHalHidlBenchmarks_setInteractive);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHint);
+BENCHMARK(BM_PowerHalHidlBenchmarks_powerHintAsync);
diff --git a/services/powermanager/benchmarks/main.cpp b/services/powermanager/benchmarks/main.cpp
new file mode 100644
index 0000000..15c57bf
--- /dev/null
+++ b/services/powermanager/benchmarks/main.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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 <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h
new file mode 100644
index 0000000..728c8a0
--- /dev/null
+++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 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_OS_BATTERY_SAVER_POLICY_CONFIG_H
+#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java
+ */
+struct BatterySaverPolicyConfig : public android::Parcelable {
+
+ BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f,
+ bool advertiseIsEnabled = false,
+ bool deferFullBackup = false,
+ bool deferKeyValueBackup = false,
+ std::vector<std::pair<String16, String16>> deviceSpecificSettings = {},
+ bool disableAnimation = false,
+ bool disableAod = false,
+ bool disableLaunchBoost = false,
+ bool disableOptionalSensors = false,
+ bool disableSoundTrigger = false,
+ bool disableVibration = false,
+ bool enableAdjustBrightness = false,
+ bool enableDataSaver = false,
+ bool enableFirewall = false,
+ bool enableNightMode = false,
+ bool enableQuickDoze = false,
+ bool forceAllAppsStandby = false,
+ bool forceBackgroundCheck = false,
+ LocationMode locationMode = static_cast<LocationMode>(0))
+ : mAdjustBrightnessFactor(adjustBrightnessFactor),
+ mAdvertiseIsEnabled(advertiseIsEnabled),
+ mDeferFullBackup(deferFullBackup),
+ mDeferKeyValueBackup(deferKeyValueBackup),
+ mDeviceSpecificSettings(deviceSpecificSettings),
+ mDisableAnimation(disableAnimation),
+ mDisableAod(disableAod),
+ mDisableLaunchBoost(disableLaunchBoost),
+ mDisableOptionalSensors(disableOptionalSensors),
+ mDisableSoundTrigger(disableSoundTrigger),
+ mDisableVibration(disableVibration),
+ mEnableAdjustBrightness(enableAdjustBrightness),
+ mEnableDataSaver(enableDataSaver),
+ mEnableFirewall(enableFirewall),
+ mEnableNightMode(enableNightMode),
+ mEnableQuickDoze(enableQuickDoze),
+ mForceAllAppsStandby(forceAllAppsStandby),
+ mForceBackgroundCheck(forceBackgroundCheck),
+ mLocationMode(locationMode) {
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ bool operator == (const BatterySaverPolicyConfig &bsp) const {
+ return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f &&
+ mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled &&
+ mDeferFullBackup == bsp.mDeferFullBackup &&
+ mDeferKeyValueBackup == bsp.mDeferKeyValueBackup &&
+ mDeviceSpecificSettings == bsp.mDeviceSpecificSettings &&
+ mDisableAnimation == bsp.mDisableAnimation &&
+ mDisableAod == bsp.mDisableAod &&
+ mDisableLaunchBoost == bsp.mDisableLaunchBoost &&
+ mDisableOptionalSensors == bsp.mDisableOptionalSensors &&
+ mDisableSoundTrigger == bsp.mDisableSoundTrigger &&
+ mDisableVibration == bsp.mDisableVibration &&
+ mEnableAdjustBrightness == bsp.mEnableAdjustBrightness &&
+ mEnableDataSaver == bsp.mEnableDataSaver &&
+ mEnableFirewall == bsp.mEnableFirewall &&
+ mEnableNightMode == bsp.mEnableNightMode &&
+ mEnableQuickDoze == bsp.mEnableQuickDoze &&
+ mForceAllAppsStandby == bsp.mForceAllAppsStandby &&
+ mForceBackgroundCheck == bsp.mForceBackgroundCheck &&
+ mLocationMode == bsp.mLocationMode;
+ }
+
+private:
+ status_t readDeviceSpecificSettings(const android::Parcel *parcel);
+ status_t writeDeviceSpecificSettings(android::Parcel *parcel) const;
+ /** Adjust screen brightness factor */
+ float mAdjustBrightnessFactor;
+ /** Is advertise enabled */
+ bool mAdvertiseIsEnabled;
+ /** Defer full backup */
+ bool mDeferFullBackup;
+ /** Defer key value backup */
+ bool mDeferKeyValueBackup;
+ /** Device specific settings */
+ std::vector<std::pair<String16, String16>> mDeviceSpecificSettings;
+ /** Disable animation */
+ bool mDisableAnimation;
+ /** Disable Aod */
+ bool mDisableAod;
+ /** Disable launch boost */
+ bool mDisableLaunchBoost;
+ /** Disable optional sensors */
+ bool mDisableOptionalSensors;
+ /** Disable sound trigger */
+ bool mDisableSoundTrigger;
+ /** Disable vibration */
+ bool mDisableVibration;
+ /** Enable adjust brightness */
+ bool mEnableAdjustBrightness;
+ /** Enable data saver */
+ bool mEnableDataSaver;
+ /** Enable firewall */
+ bool mEnableFirewall;
+ /** Enable night mode */
+ bool mEnableNightMode;
+ /** Enable quick doze */
+ bool mEnableQuickDoze;
+ /** Force all Apps standby */
+ bool mForceAllAppsStandby;
+ /** Force Background check */
+ bool mForceBackgroundCheck;
+ /** Location mode */
+ LocationMode mLocationMode;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */
diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h
new file mode 100644
index 0000000..42933d4
--- /dev/null
+++ b/services/powermanager/include/android/LocationMode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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_OS_LOCATION_MODE_H
+#define ANDROID_OS_LOCATION_MODE_H
+
+namespace android::os {
+
+enum class LocationMode : int32_t {
+ NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+ ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+ FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY,
+ THROTTLE_REQUESTS_WHEN_SCREEN_OFF =
+ IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+ MIN = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_LOCATION_MODE_H */
diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h
new file mode 100644
index 0000000..b421f6a
--- /dev/null
+++ b/services/powermanager/include/android/PowerSaveState.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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_OS_POWER_SAVE_STATE_H
+#define ANDROID_OS_POWER_SAVE_STATE_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * PowerSaveState is a structure to encapsulate PowerSaveState status.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java
+ */
+struct PowerSaveState : public android::Parcelable {
+
+ PowerSaveState(bool batterySaverEnabled = false,
+ bool globalBatterySaverEnabled = false,
+ LocationMode locationMode = static_cast<LocationMode>(0),
+ float brightnessFactor = 0.5f)
+ : mBatterySaverEnabled(batterySaverEnabled),
+ mGlobalBatterySaverEnabled(globalBatterySaverEnabled),
+ mLocationMode(locationMode),
+ mBrightnessFactor(brightnessFactor) {
+ }
+
+ bool getBatterySaverEnabled() const { return mBatterySaverEnabled; }
+ bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; }
+ LocationMode getLocationMode() const { return mLocationMode; }
+ float getBrightnessFactor() const { return mBrightnessFactor; }
+ bool operator == (const PowerSaveState &ps) const {
+ return mBatterySaverEnabled == ps.mBatterySaverEnabled &&
+ mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled &&
+ mLocationMode == ps.mLocationMode &&
+ fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f;
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** Whether we should enable battery saver for this service. */
+ bool mBatterySaverEnabled;
+ /** Whether battery saver mode is enabled. */
+ bool mGlobalBatterySaverEnabled;
+ /** Location mode */
+ LocationMode mLocationMode;
+ /** Screen brightness factor. */
+ float mBrightnessFactor;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_POWER_SAVE_STATE_H */
diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h
new file mode 100644
index 0000000..f12847d
--- /dev/null
+++ b/services/powermanager/include/android/WorkSource.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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_OS_WORKSOURCE_H
+#define ANDROID_OS_WORKSOURCE_H
+
+#include <optional>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * WorkSource is a structure to describes the source of some work that may be done by someone else.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java
+ */
+struct WorkSource : public android::Parcelable {
+ WorkSource(
+ std::vector<int32_t> uids = {},
+ std::optional<std::vector<std::optional<String16>>> names = std::nullopt)
+ : mUids(uids),
+ mNames(names) {
+ }
+ std::vector<int32_t> getUids() const { return mUids; }
+ std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; }
+ bool operator == (const WorkSource &ws) const {
+ return mUids == ws.mUids && mNames == ws.mNames;
+ }
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** WorkSource UID array */
+ std::vector<int32_t> mUids = {};
+ /** WorkSource Tag array */
+ std::optional<std::vector<std::optional<String16>>> mNames = {};
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_WORKSOURCE_H */
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
new file mode 100644
index 0000000..49abc11
--- /dev/null
+++ b/services/powermanager/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "libpowermanager_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "IThermalManagerTest.cpp",
+ "PowerHalControllerTest.cpp",
+ "PowerHalLoaderTest.cpp",
+ "PowerHalWrapperAidlTest.cpp",
+ "PowerHalWrapperHidlV1_0Test.cpp",
+ "PowerHalWrapperHidlV1_1Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
similarity index 100%
rename from services/powermanager/IThermalManagerTest.cpp
rename to services/powermanager/tests/IThermalManagerTest.cpp
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
new file mode 100644
index 0000000..141b244
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalControllerTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalController.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class TestPowerHalConnector : public HalConnector {
+public:
+ TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
+ virtual ~TestPowerHalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect() override {
+ mCountMutex.lock();
+ ++mConnectedCount;
+ mCountMutex.unlock();
+ return std::make_unique<HidlHalWrapperV1_0>(mHal);
+ }
+
+ void reset() override {
+ mCountMutex.lock();
+ ++mResetCount;
+ mCountMutex.unlock();
+ }
+
+ int getConnectCount() { return mConnectedCount; }
+
+ int getResetCount() { return mResetCount; }
+
+private:
+ sp<IPower> mHal = nullptr;
+ std::mutex mCountMutex;
+ int mConnectedCount = 0;
+ int mResetCount = 0;
+};
+
+class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector {
+public:
+ AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
+
+ std::unique_ptr<HalWrapper> connect() override {
+ // Call parent to update counter, but ignore connected HalWrapper.
+ TestPowerHalConnector::connect();
+ return nullptr;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ std::unique_ptr<TestPowerHalConnector> halConnector =
+ std::make_unique<TestPowerHalConnector>(mMockHal);
+ mHalConnector = halConnector.get();
+ mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
+ }
+
+protected:
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+ TestPowerHalConnector* mHalConnector = nullptr;
+ std::unique_ptr<PowerHalController> mHalController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ mHalController->init();
+ mHalController->init();
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
+ std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
+ std::make_unique<AlwaysFailingTestPowerHalConnector>();
+ AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get();
+ PowerHalController halController(std::move(halConnector));
+
+ int powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ // Still works with EmptyPowerHalWrapper as fallback ignoring every api call
+ // and logging.
+ auto result = halController.setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = halController.setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+ // PowerHalConnector was called every time to attempt to reconnect with
+ // underlying service.
+ powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was never reset.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ {
+ InSequence seg;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ }
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mHalController->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LOW_POWER, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+ // PowerHalConnector was called only twice: on first api call and after failed
+ // call.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was reset once after failed call.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 1);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called only by the first thread to use the api and
+ // never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::VR, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called at least once by the first thread.
+ // Reset and reconnect calls were made at most 10 times, once after each
+ // failure.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_THAT(powerHalResetCount, Le(10));
+}
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
new file mode 100644
index 0000000..058e1b5
--- /dev/null
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalLoader.h>
+
+#include <future>
+
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+sp<T> loadHal();
+
+template <>
+sp<IPowerAidl> loadHal<IPowerAidl>() {
+ return PowerHalLoader::loadAidl();
+}
+
+template <>
+sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+ return PowerHalLoader::loadHidlV1_0();
+}
+
+template <>
+sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+ return PowerHalLoader::loadHidlV1_1();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+class PowerHalLoaderTest : public Test {
+public:
+ sp<T> load() { return ::loadHal<T>(); }
+ void unload() { PowerHalLoader::unloadAll(); }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_EQ(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestUnload) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ this->unload();
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_NE(secondHal, nullptr);
+ ASSERT_NE(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
+ std::vector<std::future<sp<TypeParam>>> futures;
+ for (int i = 0; i < 10; i++) {
+ futures.push_back(
+ std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
+ }
+
+ futures[0].wait();
+ sp<TypeParam> firstHal = futures[0].get();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+
+ for (int i = 1; i < 10; i++) {
+ futures[i].wait();
+ sp<TypeParam> currentHal = futures[i].get();
+ ASSERT_EQ(firstHal, currentHal);
+ }
+}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..a765659
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalWrapperAidlTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPower : public IPower {
+public:
+ MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPower>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperAidlTest::SetUp() {
+ mMockHal = new StrictMock<MockIPower>();
+ mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setMode(Mode::LAUNCH, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..6693d0b
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalWrapperHidlV1_0Test"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_0Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..55bbd6d
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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 "PowerHalWrapperHidlV1_1Test"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPowerV1_0 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class MockIPowerV1_1 : public IPowerV1_1 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
+ sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_1Test::SetUp() {
+ mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
+ mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(),
+ powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 45e67f7..e355594 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -153,12 +153,18 @@
SensorDeviceUtils::defaultResolutionForType(sensor.type);
}
- double promotedResolution = sensor.resolution;
- double promotedMaxRange = sensor.maxRange;
- if (fmod(promotedMaxRange, promotedResolution) != 0) {
- ALOGW("%s's max range %f is not a multiple of the resolution %f",
- sensor.name, sensor.maxRange, sensor.resolution);
- SensorDeviceUtils::quantizeValue(&sensor.maxRange, promotedResolution);
+ // Some sensors don't have a default resolution and will be left at 0.
+ // Don't crash in this case since CTS will verify that devices don't go to
+ // production with a resolution of 0.
+ if (sensor.resolution != 0) {
+ double promotedResolution = sensor.resolution;
+ double promotedMaxRange = sensor.maxRange;
+ if (fmod(promotedMaxRange, promotedResolution) != 0) {
+ ALOGW("%s's max range %f is not a multiple of the resolution %f",
+ sensor.name, sensor.maxRange, sensor.resolution);
+ SensorDeviceUtils::quantizeValue(
+ &sensor.maxRange, promotedResolution);
+ }
}
}
@@ -820,7 +826,7 @@
status_t err(NO_ERROR);
// If the min period or min timeout has changed since the last batch call, call batch.
- if (prevBestBatchParams != info.bestBatchParams) {
+ if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
err = checkReturnAndGetStatus(mSensors->batch(
@@ -890,14 +896,13 @@
Info& info = mActivationCount.editValueAt(i);
if (info.hasBatchParamsForIdent(ident)) {
- if (updateBatchParamsLocked(handle, info) != NO_ERROR) {
- bool enable = info.numActiveClients() == 0 && info.isActive;
- bool disable = info.numActiveClients() > 0 && !info.isActive;
+ updateBatchParamsLocked(handle, info);
+ bool disable = info.numActiveClients() == 0 && info.isActive;
+ bool enable = info.numActiveClients() > 0 && !info.isActive;
- if ((enable || disable) &&
- doActivateHardwareLocked(handle, enable) == NO_ERROR) {
- info.isActive = enable;
- }
+ if ((enable || disable) &&
+ doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+ info.isActive = enable;
}
}
}
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 0dcf8c0..52213cf 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -40,22 +40,12 @@
switch ((SensorTypeV2_1)event->type) {
case SensorTypeV2_1::ACCELEROMETER:
case SensorTypeV2_1::MAGNETIC_FIELD:
- case SensorTypeV2_1::ORIENTATION:
case SensorTypeV2_1::GYROSCOPE:
- case SensorTypeV2_1::GRAVITY:
- case SensorTypeV2_1::LINEAR_ACCELERATION:
case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED:
axes = 3;
break;
- case SensorTypeV2_1::GAME_ROTATION_VECTOR:
- axes = 4;
- break;
- case SensorTypeV2_1::ROTATION_VECTOR:
- case SensorTypeV2_1::GEOMAGNETIC_ROTATION_VECTOR:
- axes = 5;
- break;
case SensorTypeV2_1::DEVICE_ORIENTATION:
case SensorTypeV2_1::LIGHT:
case SensorTypeV2_1::PRESSURE:
@@ -77,11 +67,8 @@
case SensorTypeV2_1::HINGE_ANGLE:
axes = 1;
break;
- case SensorTypeV2_1::POSE_6DOF:
- axes = 15;
- break;
default:
- // No other sensors have data that needs to be rounded.
+ // No other sensors have data that needs to be quantized.
break;
}
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index d7e621c..c232f0b 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -36,7 +36,7 @@
// Increase the value of the sensor's nominal resolution to ensure that
// sensor accuracy improvements, like runtime calibration, are not masked
// during requantization.
- double incRes = 0.25 * resolution;
+ double incRes = 0.125 * resolution;
*value = round(static_cast<double>(*value) / incRes) * incRes;
}
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index ccf05d9..b4b5f98 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -37,6 +37,7 @@
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) {
mChannel = new BitTube(mService->mSocketBufferSize);
+ mTargetSdk = SensorService::getTargetSdkVersion(opPackageName);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
mTotalAcksNeeded = mTotalAcksReceived = 0;
@@ -271,11 +272,16 @@
}
}
-void SensorService::SensorEventConnection::incrementPendingFlushCount(int32_t handle) {
- Mutex::Autolock _l(mConnectionLock);
- if (mSensorInfo.count(handle) > 0) {
- FlushInfo& flushInfo = mSensorInfo[handle];
- flushInfo.mPendingFlushEventsToSend++;
+bool SensorService::SensorEventConnection::incrementPendingFlushCountIfHasAccess(int32_t handle) {
+ if (hasSensorAccess()) {
+ Mutex::Autolock _l(mConnectionLock);
+ if (mSensorInfo.count(handle) > 0) {
+ FlushInfo& flushInfo = mSensorInfo[handle];
+ flushInfo.mPendingFlushEventsToSend++;
+ }
+ return true;
+ } else {
+ return false;
}
}
@@ -439,8 +445,17 @@
bool success = true;
const auto iter = mHandleToAppOp.find(event.sensor);
if (iter != mHandleToAppOp.end()) {
- int32_t appOpMode = mService->sAppOpsManager.noteOp((*iter).second, mUid, mOpPackageName);
- success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ // Special handling for step count/detect backwards compatibility: if the app's target SDK
+ // is pre-Q, still permit delivering events to the app even if permission isn't granted
+ // (since this permission was only introduced in Q)
+ if ((event.type == SENSOR_TYPE_STEP_COUNTER || event.type == SENSOR_TYPE_STEP_DETECTOR) &&
+ mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
+ success = true;
+ } else {
+ int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
+ mOpPackageName);
+ success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ }
}
return success;
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 13cee6f..8f2d5db 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -116,8 +116,9 @@
// for writing send the data from the cache.
virtual int handleEvent(int fd, int events, void* data);
- // Increment mPendingFlushEventsToSend for the given sensor handle.
- void incrementPendingFlushCount(int32_t handle);
+ // Increment mPendingFlushEventsToSend for the given handle if the connection has sensor access.
+ // Returns true if this connection does have sensor access.
+ bool incrementPendingFlushCountIfHasAccess(int32_t handle);
// Add or remove the file descriptor associated with the BitTube to the looper. If mDead is set
// to true or there are no more sensors for this connection, the file descriptor is removed if
@@ -175,6 +176,7 @@
int mEventsDropped;
String8 mPackageName;
const String16 mOpPackageName;
+ int mTargetSdk;
#if DEBUG_CONNECTIONS
int mEventsReceived, mEventsSent, mEventsSentFromCache;
int mTotalAcksNeeded, mTotalAcksReceived;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index ffcd0a0..aa0dc92 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -699,6 +699,9 @@
if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
return PERMISSION_DENIED;
}
+ if (args.size() == 0) {
+ return BAD_INDEX;
+ }
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
@@ -1774,7 +1777,10 @@
if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0 || isVirtualSensor(handle)) {
// For older devices just increment pending flush count which will send a trivial
// flush complete event.
- connection->incrementPendingFlushCount(handle);
+ if (!connection->incrementPendingFlushCountIfHasAccess(handle)) {
+ ALOGE("flush called on an inaccessible sensor");
+ err = INVALID_OPERATION;
+ }
} else {
if (!canAccessSensor(sensor->getSensor(), "Tried flushing", opPackageName)) {
err = INVALID_OPERATION;
@@ -1802,36 +1808,28 @@
const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
IPCThreadState::self()->getCallingUid(), opPackageName);
bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
+ int targetSdkVersion = getTargetSdkVersion(opPackageName);
bool canAccess = false;
- if (hasPermissionForSensor(sensor)) {
+ if (targetSdkVersion > 0 && targetSdkVersion <= __ANDROID_API_P__ &&
+ (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
+ sensor.getType() == SENSOR_TYPE_STEP_DETECTOR)) {
+ // Allow access to step sensors if the application targets pre-Q, which is before the
+ // requirement to hold the AR permission to access Step Counter and Step Detector events
+ // was introduced.
+ canAccess = true;
+ } else if (hasPermissionForSensor(sensor)) {
// Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
if (opCode < 0 || appOpAllowed) {
canAccess = true;
}
- } else if (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
- sensor.getType() == SENSOR_TYPE_STEP_DETECTOR) {
- int targetSdkVersion = getTargetSdkVersion(opPackageName);
- // Allow access to the sensor if the application targets pre-Q, which is before the
- // requirement to hold the AR permission to access Step Counter and Step Detector events
- // was introduced, and the user hasn't revoked the app op.
- //
- // Verifying the app op is required to ensure that the user hasn't revoked the necessary
- // permissions to access the Step Detector and Step Counter when the application targets
- // pre-Q. Without this check, if the user revokes the pre-Q install-time GMS Core AR
- // permission, the app would still be able to receive Step Counter and Step Detector events.
- if (appOpAllowed &&
- targetSdkVersion > 0 &&
- targetSdkVersion <= __ANDROID_API_P__) {
- canAccess = true;
- }
}
if (canAccess) {
sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
} else {
- ALOGE("%s a sensor (%s) without holding its required permission: %s",
- operation, sensor.getName().string(), sensor.getRequiredPermission().string());
+ ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
+ operation, sensor.getName().string(), sensor.getRequiredPermission().string());
}
return canAccess;
diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp
index d0c83d6..0e1af59 100644
--- a/services/sensorservice/hidl/Android.bp
+++ b/services/sensorservice/hidl/Android.bp
@@ -10,6 +10,7 @@
"-Wall",
"-Werror",
],
+ header_libs: ["jni_headers"],
shared_libs: [
"libbase",
"libhidlbase",
@@ -24,6 +25,7 @@
export_include_dirs: [
"include/"
],
+ export_header_lib_headers: ["jni_headers"],
local_include_dirs: [
"include/sensorservicehidl/"
]
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 0182937..804a3c3 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -38,7 +38,6 @@
"libbinder",
"libbufferhubqueue",
"libcutils",
- "libdl",
"libEGL",
"libfmq",
"libGLESv1_CM",
@@ -73,7 +72,6 @@
"librenderengine",
"libserviceutils",
"libtrace_proto",
- "libvr_manager",
"libvrflinger",
],
header_libs: [
@@ -147,6 +145,7 @@
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/PowerAdvisor.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
+ "DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
"FrameTracer/FrameTracer.cpp",
@@ -154,6 +153,7 @@
"Layer.cpp",
"LayerProtoHelper.cpp",
"LayerRejecter.cpp",
+ "LayerRenderArea.cpp",
"LayerVector.cpp",
"MonitoredProducer.cpp",
"NativeWindowSurface.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 5b28384..cf60b71 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -319,7 +319,7 @@
return hasReadyFrame();
}
-bool BufferLayer::onPostComposition(sp<const DisplayDevice> displayDevice,
+bool BufferLayer::onPostComposition(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
const CompositorTiming& compositorTiming) {
@@ -342,7 +342,7 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
- const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ const auto outputLayer = findOutputLayerForDisplay(display);
if (outputLayer && outputLayer->requiresClientComposition()) {
nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
@@ -359,13 +359,15 @@
mFrameTracker.setFrameReadyTime(desiredPresentTime);
}
- const auto displayId = displayDevice->getId();
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
- } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+ } else if (!display) {
+ // Do nothing.
+ } else if (const auto displayId = display->getId();
+ displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
@@ -537,20 +539,6 @@
return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
}
-bool BufferLayer::latchUnsignaledBuffers() {
- static bool propertyLoaded = false;
- static bool latch = false;
- static std::mutex mutex;
- std::lock_guard<std::mutex> lock(mutex);
- if (!propertyLoaded) {
- char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.latch_unsignaled", value, "0");
- latch = atoi(value);
- propertyLoaded = true;
- }
- return latch;
-}
-
// h/w composer set-up
bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
@@ -600,14 +588,8 @@
return true;
}
-bool BufferLayer::needsFiltering(const sp<const DisplayDevice>& displayDevice) const {
- // If we are not capturing based on the state of a known display device,
- // just return false.
- if (displayDevice == nullptr) {
- return false;
- }
-
- const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+bool BufferLayer::needsFiltering(const DisplayDevice* display) const {
+ const auto outputLayer = findOutputLayerForDisplay(display);
if (outputLayer == nullptr) {
return false;
}
@@ -621,15 +603,9 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
-bool BufferLayer::needsFilteringForScreenshots(const sp<const DisplayDevice>& displayDevice,
+bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display,
const ui::Transform& inverseParentTransform) const {
- // If we are not capturing based on the state of a known display device,
- // just return false.
- if (displayDevice == nullptr) {
- return false;
- }
-
- const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ const auto outputLayer = findOutputLayerForDisplay(display);
if (outputLayer == nullptr) {
return false;
}
@@ -637,7 +613,7 @@
// We need filtering if the sourceCrop rectangle size does not match the
// viewport rectangle size (not a 1:1 render)
const auto& compositionState = outputLayer->getState();
- const ui::Transform& displayTransform = displayDevice->getTransform();
+ const ui::Transform& displayTransform = display->getTransform();
const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
// Undo the transformation of the displayFrame so that we're back into
// layer-stack space.
@@ -843,6 +819,13 @@
mDrawingState.inputInfo = tmpInputInfo;
}
+void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+ mTransformHint = getFixedTransformHint();
+ if (mTransformHint == ui::Transform::ROT_INVALID) {
+ mTransformHint = displayTransformHint;
+ }
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 56bab1b..2483abb 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -79,10 +79,9 @@
bool isHdrY410() const override;
- bool onPostComposition(sp<const DisplayDevice> displayDevice,
- const std::shared_ptr<FenceTime>& glDoneFence,
+ bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming& compositorTiming) override;
+ const CompositorTiming&) override;
// latchBuffer - called each time the screen is redrawn and returns whether
// the visible regions need to be recomputed (this is a fairly heavy
@@ -117,7 +116,7 @@
sp<GraphicBuffer> getBuffer() const override;
- // -----------------------------------------------------------------------
+ ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
// -----------------------------------------------------------------------
// Functions that must be implemented by derived classes
@@ -149,6 +148,11 @@
virtual status_t updateActiveBuffer() = 0;
virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+ // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
+ // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
+ // detection.
+ bool needsInputInfo() const override { return !mPotentialCursor; }
+
protected:
struct BufferInfo {
nsecs_t mDesiredPresentTime;
@@ -183,9 +187,6 @@
bool onPreComposition(nsecs_t) override;
void preparePerFrameCompositionState() override;
- // Loads the corresponding system property once per process
- static bool latchUnsignaledBuffers();
-
// Check all of the local sync points to ensure that all transactions
// which need to have been applied prior to the frame which is about to
// be latched have signaled
@@ -205,10 +206,16 @@
virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+ void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
+ // Transform hint provided to the producer. This must be accessed holding
+ /// the mStateLock.
+ ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+
private:
// Returns true if this layer requires filtering
- bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const override;
- bool needsFilteringForScreenshots(const sp<const DisplayDevice>& displayDevice,
+ bool needsFiltering(const DisplayDevice*) const override;
+ bool needsFilteringForScreenshots(const DisplayDevice*,
const ui::Transform& inverseParentTransform) const override;
// BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 648d129..8722952 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -452,6 +452,13 @@
}
void BufferLayerConsumer::onDisconnect() {
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
mLayer->onDisconnect();
}
@@ -486,6 +493,13 @@
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index c71a1d9..5e3044f 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -331,8 +331,8 @@
// construction time.
const uint32_t mTexName;
- // The layer for this BufferLayerConsumer
- Layer* mLayer;
+ // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
+ Layer* mLayer GUARDED_BY(mMutex);
wp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index f4e630e..89284f2 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -31,6 +31,7 @@
#include "SurfaceInterceptor.h"
#include "FrameTracer/FrameTracer.h"
+#include "Scheduler/LayerHistory.h"
#include "TimeStats/TimeStats.h"
namespace android {
@@ -58,8 +59,9 @@
}
}
-void BufferQueueLayer::setTransformHint(uint32_t orientation) const {
- mConsumer->setTransformHint(orientation);
+void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+ BufferLayer::setTransformHint(displayTransformHint);
+ mConsumer->setTransformHint(mTransformHint);
}
std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
@@ -129,10 +131,6 @@
// -----------------------------------------------------------------------
bool BufferQueueLayer::fenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
if (!hasFrameUpdate()) {
return true;
}
@@ -206,8 +204,12 @@
}
bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+ // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+ const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
bool sidebandStreamChanged = true;
- if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
+ if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) ||
+ updateSidebandStream) {
// mSidebandStreamChanged was changed to false
mSidebandStream = mConsumer->getSidebandStream();
auto* layerCompositionState = editCompositionState();
@@ -322,9 +324,6 @@
}
uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
- mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber,
- mQueueItems[0].mFenceTime,
- FrameTracer::FrameEvent::ACQUIRE_FENCE);
mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
@@ -391,14 +390,19 @@
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
const int32_t layerId = getSequence();
- mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
- systemTime(), FrameTracer::FrameEvent::QUEUE);
+ const uint64_t bufferId = item.mGraphicBuffer->getId();
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
+ FrameTracer::FrameEvent::QUEUE);
+ mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
+ std::make_shared<FenceTime>(item.mFence),
+ FrameTracer::FrameEvent::ACQUIRE_FENCE);
ATRACE_CALL();
// Add this buffer from our internal queue tracker
{ // Autolock scope
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+ LayerHistory::LayerUpdateType::Buffer);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
@@ -457,8 +461,12 @@
}
const int32_t layerId = getSequence();
- mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
- systemTime(), FrameTracer::FrameEvent::QUEUE);
+ const uint64_t bufferId = item.mGraphicBuffer->getId();
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
+ FrameTracer::FrameEvent::QUEUE);
+ mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
+ std::make_shared<FenceTime>(item.mFence),
+ FrameTracer::FrameEvent::ACQUIRE_FENCE);
mConsumer->onBufferAvailable(item);
}
@@ -493,10 +501,6 @@
if (!mFlinger->isLayerTripleBufferingDisabled()) {
mProducer->setMaxDequeuedBufferCount(2);
}
-
- if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
- updateTransformHint(display);
- }
}
status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 16b4b6e..5ebc22d 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -43,8 +43,6 @@
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
- void setTransformHint(uint32_t orientation) const override;
-
std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
// If a buffer was replaced this frame, release the former buffer
@@ -72,6 +70,7 @@
bool getSidebandStreamChanged() const override;
bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+ void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
bool hasFrameUpdate() const override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a121ce0..884cc0c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -33,7 +33,6 @@
#include <renderengine/Image.h>
#include "EffectLayer.h"
-#include "FrameTracer/FrameTracer.h"
#include "TimeStats/TimeStats.h"
namespace android {
@@ -51,9 +50,6 @@
: BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
- if (const auto display = args.displayDevice) {
- updateTransformHint(display);
- }
}
BufferStateLayer::~BufferStateLayer() {
@@ -101,17 +97,10 @@
// Prevent tracing the same release multiple times.
if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
- mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
- std::make_shared<FenceTime>(releaseFence),
- FrameTracer::FrameEvent::RELEASE_FENCE);
mPreviousReleasedFrameNumber = mPreviousFrameNumber;
}
}
-void BufferStateLayer::setTransformHint(uint32_t orientation) const {
- mTransformHint = orientation;
-}
-
void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->transformHint = mTransformHint;
@@ -284,13 +273,11 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
postTime);
- mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
- postTime, FrameTracer::FrameEvent::POST);
desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
mCurrentState.desiredPresentTime = desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+ mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+ LayerHistory::LayerUpdateType::Buffer);
addFrameEvent(acquireFence, postTime, desiredPresentTime);
return true;
@@ -435,10 +422,6 @@
// Interface implementation for BufferLayer
// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
const bool fenceSignaled =
getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
if (!fenceSignaled) {
@@ -586,20 +569,13 @@
status_t err = bindTextureImage();
if (err != NO_ERROR) {
mFlinger->mTimeStats->onDestroy(layerId);
- mFlinger->mFrameTracer->onDestroy(layerId);
return BAD_VALUE;
}
}
- const uint64_t bufferID = getCurrentBufferId();
mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
- mBufferInfo.mFenceTime);
- mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
- mBufferInfo.mFenceTime,
- FrameTracer::FrameEvent::ACQUIRE_FENCE);
+ std::make_shared<FenceTime>(mDrawingState.acquireFence));
mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
- mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
- FrameTracer::FrameEvent::LATCH);
mCurrentStateModified = false;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 5873a73..00fa7f7 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -42,7 +42,6 @@
const char* getType() const override { return "BufferStateLayer"; }
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
- void setTransformHint(uint32_t orientation) const override;
void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
@@ -68,7 +67,6 @@
}
Rect getCrop(const Layer::State& s) const;
- uint32_t getTransformHint() const { return mTransformHint; }
bool setTransform(uint32_t transform) override;
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
@@ -164,8 +162,6 @@
bool mReleasePreviousBuffer = false;
nsecs_t mCallbackHandleAcquireTime = -1;
- mutable uint32_t mTransformHint = 0;
-
// TODO(marissaw): support sticky transform for LEGACY camera mode
class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index fda451b..b3b9fe5 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -21,7 +21,6 @@
"liblog",
"libnativewindow",
"libprotobuf-cpp-lite",
- "libsync",
"libtimestats",
"libui",
"libutils",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index d3712d9..a0606b4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -24,6 +24,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputColorSetting.h>
#include <math/mat4.h>
+#include <ui/Transform.h>
namespace android::compositionengine {
@@ -57,6 +58,9 @@
// Forces a color mode on the outputs being refreshed
ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
+ // Used to correctly apply an inverse-display buffer transform if applicable
+ ui::Transform::RotationFlags internalDisplayRotationFlags{ui::Transform::ROT_0};
+
// If true, GPU clocks will be increased when rendering blurs
bool blursAreExpensive{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index cf77738..aa70ef8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
#include <optional>
#include <string>
+#include <ui/Transform.h>
#include <utils/StrongPointer.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -77,7 +78,12 @@
// Recalculates the state of the output layer from the output-independent
// layer. If includeGeometry is false, the geometry state can be skipped.
- virtual void updateCompositionState(bool includeGeometry, bool forceClientComposition) = 0;
+ // internalDisplayRotationFlags must be set to the rotation flags for the
+ // internal display, and is used to properly compute the inverse-display
+ // transform, if needed.
+ virtual void updateCompositionState(
+ bool includeGeometry, bool forceClientComposition,
+ ui::Transform::RotationFlags internalDisplayRotationFlags) = 0;
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index 5ce2fdc..f680460 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -63,6 +63,12 @@
// Sets the dataspace used for rendering the surface
virtual void setBufferDataspace(ui::Dataspace) = 0;
+ // Sets the pixel format used for rendering the surface.
+ // Changing the pixel format of the buffer will result in buffer
+ // reallocation as well as some reconfiguration of the graphics context,
+ // which are both expensive operations.
+ virtual void setBufferPixelFormat(ui::PixelFormat) = 0;
+
// Configures the protected rendering on the surface
virtual void setProtected(bool useProtected) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 9ca7d2f..7a4f738 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -70,11 +70,13 @@
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
+ using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
virtual bool anyLayersRequireClientComposition() const;
virtual bool allLayersRequireClientComposition() const;
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
+ virtual void applyClientTargetRequests(const ClientTargetProperty&);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 79df9b2..8cb5ae8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -39,7 +39,8 @@
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
- void updateCompositionState(bool includeGeometry, bool forceClientComposition) override;
+ void updateCompositionState(bool includeGeometry, bool forceClientComposition,
+ ui::Transform::RotationFlags) override;
void writeStateToHWC(bool) override;
void writeCursorPositionToHWC() const override;
@@ -55,7 +56,8 @@
virtual FloatRect calculateOutputSourceCrop() const;
virtual Rect calculateOutputDisplayFrame() const;
- virtual uint32_t calculateOutputRelativeBufferTransform() const;
+ virtual uint32_t calculateOutputRelativeBufferTransform(
+ uint32_t internalDisplayRotationFlags) const;
protected:
// Implemented by the final implementation for the final state it uses.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 692d78d..5127a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -49,6 +49,7 @@
const sp<Fence>& getClientTargetAcquireFence() const override;
void setBufferDataspace(ui::Dataspace) override;
+ void setBufferPixelFormat(ui::PixelFormat) override;
void setDisplaySize(const ui::Size&) override;
void setProtected(bool useProtected) override;
status_t beginFrame(bool mustRecompose) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 2ecbad8..81e1fc7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -38,7 +38,7 @@
MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
- MOCK_METHOD2(updateCompositionState, void(bool, bool));
+ MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
MOCK_METHOD1(writeStateToHWC, void(bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index ed4d492..a0cae6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -36,6 +36,7 @@
MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD1(setProtected, void(bool));
MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+ MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD2(prepareFrame, void(bool, bool));
MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index ab26939..d201104 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -259,6 +259,7 @@
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
+ applyClientTargetRequests(changes->clientTargetProperty);
}
// Determine what type of composition we are doing from the final state
@@ -326,6 +327,16 @@
}
}
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+ if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
+ return;
+ }
+ auto outputState = editState();
+ outputState.dataspace = clientTargetProperty.dataspace;
+ getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
+ getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
+}
+
compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
auto result = impl::Output::presentAndGetFrameFences();
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 34d0cb2..b07c904 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -588,7 +588,8 @@
for (auto* layer : getOutputLayersOrderedByZ()) {
layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
refreshArgs.devOptForceClientComposition ||
- forceClientComposition);
+ forceClientComposition,
+ refreshArgs.internalDisplayRotationFlags);
if (mLayerRequestingBackgroundBlur == layer) {
forceClientComposition = false;
@@ -815,6 +816,43 @@
OutputCompositionState& outputCompositionState = editState();
const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
outputState.usesClientComposition};
+
+ auto& renderEngine = getCompositionEngine().getRenderEngine();
+ const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
+
+ // If we the display is secure, protected content support is enabled, and at
+ // least one layer has protected content, we need to use a secure back
+ // buffer.
+ if (outputState.isSecure && supportsProtectedContent) {
+ auto layers = getOutputLayersOrderedByZ();
+ bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
+ return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+ });
+ if (needsProtected != renderEngine.isProtected()) {
+ renderEngine.useProtectedContext(needsProtected);
+ }
+ if (needsProtected != mRenderSurface->isProtected() &&
+ needsProtected == renderEngine.isProtected()) {
+ mRenderSurface->setProtected(needsProtected);
+ }
+ }
+
+ base::unique_fd fd;
+ sp<GraphicBuffer> buf;
+
+ // If we aren't doing client composition on this output, but do have a
+ // flipClientTarget request for this frame on this output, we still need to
+ // dequeue a buffer.
+ if (hasClientComposition || outputState.flipClientTarget) {
+ buf = mRenderSurface->dequeueBuffer(&fd);
+ if (buf == nullptr) {
+ ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
+ "client composition for this frame",
+ mName.c_str());
+ return {};
+ }
+ }
+
base::unique_fd readyFence;
if (!hasClientComposition) {
setExpensiveRenderingExpected(false);
@@ -823,9 +861,6 @@
ALOGV("hasClientComposition");
- auto& renderEngine = getCompositionEngine().getRenderEngine();
- const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
-
renderengine::DisplaySettings clientCompositionDisplay;
clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
clientCompositionDisplay.clip = outputState.sourceClip;
@@ -851,32 +886,6 @@
clientCompositionDisplay.outputDataspace);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
- // If we the display is secure, protected content support is enabled, and at
- // least one layer has protected content, we need to use a secure back
- // buffer.
- if (outputState.isSecure && supportsProtectedContent) {
- auto layers = getOutputLayersOrderedByZ();
- bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
- return layer->getLayerFE().getCompositionState()->hasProtectedContent;
- });
- if (needsProtected != renderEngine.isProtected()) {
- renderEngine.useProtectedContext(needsProtected);
- }
- if (needsProtected != mRenderSurface->isProtected() &&
- needsProtected == renderEngine.isProtected()) {
- mRenderSurface->setProtected(needsProtected);
- }
- }
-
- base::unique_fd fd;
- sp<GraphicBuffer> buf = mRenderSurface->dequeueBuffer(&fd);
- if (buf == nullptr) {
- ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
- "client composition for this frame",
- mName.c_str());
- return std::nullopt;
- }
-
// Check if the client composition requests were rendered into the provided graphic buffer. If
// so, we can reuse the buffer and avoid client composition.
if (mClientCompositionRequestCache) {
@@ -912,9 +921,8 @@
const nsecs_t renderEngineStart = systemTime();
status_t status =
- renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
- buf->getNativeBuffer(), /*useFramebufferCache=*/true,
- std::move(fd), &readyFence);
+ renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
+ /*useFramebufferCache=*/true, std::move(fd), &readyFence);
if (status != NO_ERROR && mClientCompositionRequestCache) {
// If rendering was not successful, remove the request from the cache.
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index ca5be48..4835aef 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -38,6 +38,7 @@
out.append("\n ");
+ dumpVal(out, "bounds", bounds);
dumpVal(out, "frame", frame);
dumpVal(out, "viewport", viewport);
dumpVal(out, "sourceClip", sourceClip);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index c9a070d..1faf775 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -149,9 +149,9 @@
// a modification of the axes of rotation. To account for this we
// need to reorient the inverse rotation in terms of the current
// axes of rotation.
- bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
- bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
- if (is_h_flipped == is_v_flipped) {
+ bool isHFlipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
+ bool isVFlipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
+ if (isHFlipped == isVFlipped) {
invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
}
std::swap(winWidth, winHeight);
@@ -160,18 +160,18 @@
activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
// below, crop is intersected with winCrop expressed in crop's coordinate space
- float xScale = crop.getWidth() / float(winWidth);
- float yScale = crop.getHeight() / float(winHeight);
+ const float xScale = crop.getWidth() / float(winWidth);
+ const float yScale = crop.getHeight() / float(winHeight);
- float insetL = winCrop.left * xScale;
- float insetT = winCrop.top * yScale;
- float insetR = (winWidth - winCrop.right) * xScale;
- float insetB = (winHeight - winCrop.bottom) * yScale;
+ const float insetLeft = winCrop.left * xScale;
+ const float insetTop = winCrop.top * yScale;
+ const float insetRight = (winWidth - winCrop.right) * xScale;
+ const float insetBottom = (winHeight - winCrop.bottom) * yScale;
- crop.left += insetL;
- crop.top += insetT;
- crop.right -= insetR;
- crop.bottom -= insetB;
+ crop.left += insetLeft;
+ crop.top += insetTop;
+ crop.right -= insetRight;
+ crop.bottom -= insetBottom;
return crop;
}
@@ -223,7 +223,8 @@
return displayTransform.transform(frame);
}
-uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
+uint32_t OutputLayer::calculateOutputRelativeBufferTransform(
+ uint32_t internalDisplayRotationFlags) const {
const auto& layerState = *getLayerFE().getCompositionState();
const auto& outputState = getOutput().getState();
@@ -241,10 +242,11 @@
if (layerState.geomBufferUsesDisplayInverseTransform) {
/*
- * the code below applies the primary display's inverse transform to the
- * buffer
+ * We must apply the internal display's inverse transform to the buffer
+ * transform, and not the one for the output this layer is on.
*/
- uint32_t invTransform = outputState.orientation;
+ uint32_t invTransform = internalDisplayRotationFlags;
+
// calculate the inverse transform
if (invTransform & HAL_TRANSFORM_ROT_90) {
invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -261,9 +263,11 @@
// this gives us only the "orientation" component of the transform
return transform.getOrientation();
-} // namespace impl
+}
-void OutputLayer::updateCompositionState(bool includeGeometry, bool forceClientComposition) {
+void OutputLayer::updateCompositionState(
+ bool includeGeometry, bool forceClientComposition,
+ ui::Transform::RotationFlags internalDisplayRotationFlags) {
const auto* layerFEState = getLayerFE().getCompositionState();
if (!layerFEState) {
return;
@@ -283,8 +287,8 @@
state.displayFrame = calculateOutputDisplayFrame();
state.sourceCrop = calculateOutputSourceCrop();
- state.bufferTransform =
- static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
+ state.bufferTransform = static_cast<Hwc2::Transform>(
+ calculateOutputRelativeBufferTransform(internalDisplayRotationFlags));
if ((layerFEState->isSecure && !outputState.isSecure) ||
(state.bufferTransform & ui::Transform::ROT_INVALID)) {
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 660baff..2773fd3 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -28,7 +28,6 @@
#include <log/log.h>
#include <renderengine/RenderEngine.h>
-#include <sync/sync.h>
#include <system/window.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
@@ -105,6 +104,10 @@
static_cast<android_dataspace>(dataspace));
}
+void RenderSurface::setBufferPixelFormat(ui::PixelFormat pixelFormat) {
+ native_window_set_buffers_format(mNativeWindow.get(), static_cast<int32_t>(pixelFormat));
+}
+
void RenderSurface::setProtected(bool useProtected) {
uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
if (useProtected) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 59889b6..09f37fb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,6 +30,7 @@
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
@@ -156,6 +157,8 @@
DisplayTestCommon() {
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+ EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
}
DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
@@ -182,6 +185,7 @@
StrictMock<android::mock::HWComposer> mHwComposer;
StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+ StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
};
@@ -650,6 +654,7 @@
{{nullptr, hal::Composition::CLIENT}},
hal::DisplayRequest::FLIP_CLIENT_TARGET,
{{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+ {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
};
// Since two calls are made to anyLayersRequireClientComposition with different return
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index bdacb22..020f93a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -361,7 +361,7 @@
mOutputState.orientation = entry.display;
mOutputState.transform = ui::Transform{entry.display};
- auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+ const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
EXPECT_EQ(entry.expected, actual) << "entry " << i;
}
}
@@ -371,56 +371,109 @@
mLayerFEState.geomBufferUsesDisplayInverseTransform = true;
struct Entry {
- uint32_t layer;
+ uint32_t layer; /* shouldn't affect the result, so we just use arbitrary values */
uint32_t buffer;
uint32_t display;
+ uint32_t internal;
uint32_t expected;
};
- // Not an exhaustive list of cases, but hopefully enough.
- const std::array<Entry, 24> testData = {
+ const std::array<Entry, 64> testData = {
// clang-format off
- // layer buffer display expected
- /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT},
- /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_IDENT},
- /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_IDENT},
- /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_IDENT},
+ // layer buffer display internal expected
+ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT},
+ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_ROT_90, TR_ROT_270},
+ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_ROT_180, TR_ROT_180},
+ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_ROT_270, TR_ROT_90},
- /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H},
- /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H},
- /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H},
- /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H},
+ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_IDENT, TR_ROT_90},
+ Entry{TR_ROT_90, TR_IDENT, TR_ROT_90, TR_ROT_90, TR_IDENT},
+ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_ROT_180, TR_ROT_270},
+ Entry{TR_ROT_90, TR_IDENT, TR_ROT_90, TR_ROT_270, TR_ROT_180},
- /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V},
- /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_90},
- /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_ROT_180},
- /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_270},
+ Entry{TR_ROT_180, TR_IDENT, TR_ROT_180, TR_IDENT, TR_ROT_180},
+ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_ROT_90, TR_ROT_90},
+ Entry{TR_ROT_180, TR_IDENT, TR_ROT_180, TR_ROT_180, TR_IDENT},
+ Entry{TR_ROT_270, TR_IDENT, TR_ROT_180, TR_ROT_270, TR_ROT_270},
- /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT},
- /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H},
- /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT},
- /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H},
+ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT, TR_ROT_270},
+ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_ROT_90, TR_ROT_180},
+ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_ROT_180, TR_ROT_90},
+ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_ROT_270, TR_IDENT},
- /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H},
- /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT},
- /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H},
- /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT},
+ // layer buffer display internal expected
+ Entry{TR_IDENT, TR_ROT_90, TR_IDENT, TR_IDENT, TR_ROT_90},
+ Entry{TR_ROT_90, TR_ROT_90, TR_IDENT, TR_ROT_90, TR_IDENT},
+ Entry{TR_ROT_180, TR_ROT_90, TR_IDENT, TR_ROT_180, TR_ROT_270},
+ Entry{TR_ROT_270, TR_ROT_90, TR_IDENT, TR_ROT_270, TR_ROT_180},
- /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT},
- /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H},
- /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H},
- /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT},
+ Entry{TR_ROT_90, TR_ROT_90, TR_ROT_90, TR_IDENT, TR_ROT_180},
+ Entry{TR_ROT_90, TR_ROT_90, TR_ROT_90, TR_ROT_90, TR_ROT_90},
+ Entry{TR_ROT_90, TR_ROT_90, TR_ROT_90, TR_ROT_180, TR_IDENT},
+ Entry{TR_ROT_270, TR_ROT_90, TR_ROT_90, TR_ROT_270, TR_ROT_270},
+
+ Entry{TR_IDENT, TR_ROT_90, TR_ROT_180, TR_IDENT, TR_ROT_270},
+ Entry{TR_ROT_90, TR_ROT_90, TR_ROT_180, TR_ROT_90, TR_ROT_180},
+ Entry{TR_ROT_180, TR_ROT_90, TR_ROT_180, TR_ROT_180, TR_ROT_90},
+ Entry{TR_ROT_90, TR_ROT_90, TR_ROT_180, TR_ROT_270, TR_IDENT},
+
+ Entry{TR_IDENT, TR_ROT_90, TR_ROT_270, TR_IDENT, TR_IDENT},
+ Entry{TR_ROT_270, TR_ROT_90, TR_ROT_270, TR_ROT_90, TR_ROT_270},
+ Entry{TR_ROT_180, TR_ROT_90, TR_ROT_270, TR_ROT_180, TR_ROT_180},
+ Entry{TR_ROT_270, TR_ROT_90, TR_ROT_270, TR_ROT_270, TR_ROT_90},
+
+ // layer buffer display internal expected
+ Entry{TR_IDENT, TR_ROT_180, TR_IDENT, TR_IDENT, TR_ROT_180},
+ Entry{TR_IDENT, TR_ROT_180, TR_IDENT, TR_ROT_90, TR_ROT_90},
+ Entry{TR_ROT_180, TR_ROT_180, TR_IDENT, TR_ROT_180, TR_IDENT},
+ Entry{TR_ROT_270, TR_ROT_180, TR_IDENT, TR_ROT_270, TR_ROT_270},
+
+ Entry{TR_IDENT, TR_ROT_180, TR_ROT_90, TR_IDENT, TR_ROT_270},
+ Entry{TR_ROT_90, TR_ROT_180, TR_ROT_90, TR_ROT_90, TR_ROT_180},
+ Entry{TR_ROT_180, TR_ROT_180, TR_ROT_90, TR_ROT_180, TR_ROT_90},
+ Entry{TR_ROT_180, TR_ROT_180, TR_ROT_90, TR_ROT_270, TR_IDENT},
+
+ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_IDENT, TR_IDENT},
+ Entry{TR_ROT_180, TR_ROT_180, TR_ROT_180, TR_ROT_90, TR_ROT_270},
+ Entry{TR_ROT_180, TR_ROT_180, TR_ROT_180, TR_ROT_180, TR_ROT_180},
+ Entry{TR_ROT_270, TR_ROT_180, TR_ROT_180, TR_ROT_270, TR_ROT_90},
+
+ Entry{TR_ROT_270, TR_ROT_180, TR_ROT_270, TR_IDENT, TR_ROT_90},
+ Entry{TR_ROT_180, TR_ROT_180, TR_ROT_270, TR_ROT_90, TR_IDENT},
+ Entry{TR_ROT_180, TR_ROT_180, TR_ROT_270, TR_ROT_180, TR_ROT_270},
+ Entry{TR_ROT_270, TR_ROT_180, TR_ROT_270, TR_ROT_270, TR_ROT_180},
+
+ // layer buffer display internal expected
+ Entry{TR_IDENT, TR_ROT_270, TR_IDENT, TR_IDENT, TR_ROT_270},
+ Entry{TR_ROT_90, TR_ROT_270, TR_IDENT, TR_ROT_90, TR_ROT_180},
+ Entry{TR_ROT_270, TR_ROT_270, TR_IDENT, TR_ROT_180, TR_ROT_90},
+ Entry{TR_IDENT, TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT},
+
+ Entry{TR_ROT_270, TR_ROT_270, TR_ROT_90, TR_IDENT, TR_IDENT},
+ Entry{TR_ROT_90, TR_ROT_270, TR_ROT_90, TR_ROT_90, TR_ROT_270},
+ Entry{TR_ROT_180, TR_ROT_270, TR_ROT_90, TR_ROT_180, TR_ROT_180},
+ Entry{TR_ROT_90, TR_ROT_270, TR_ROT_90, TR_ROT_270, TR_ROT_90},
+
+ Entry{TR_IDENT, TR_ROT_270, TR_ROT_180, TR_IDENT, TR_ROT_90},
+ Entry{TR_ROT_270, TR_ROT_270, TR_ROT_180, TR_ROT_90, TR_IDENT},
+ Entry{TR_ROT_180, TR_ROT_270, TR_ROT_180, TR_ROT_180, TR_ROT_270},
+ Entry{TR_ROT_270, TR_ROT_270, TR_ROT_180, TR_ROT_270, TR_ROT_180},
+
+ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_IDENT, TR_ROT_180},
+ Entry{TR_ROT_90, TR_ROT_270, TR_ROT_270, TR_ROT_90, TR_ROT_90},
+ Entry{TR_ROT_270, TR_ROT_270, TR_ROT_270, TR_ROT_180, TR_IDENT},
+ Entry{TR_ROT_270, TR_ROT_270, TR_ROT_270, TR_ROT_270, TR_ROT_270},
// clang-format on
};
for (size_t i = 0; i < testData.size(); i++) {
const auto& entry = testData[i];
- mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
+ mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
mOutputState.transform = ui::Transform{entry.display};
- auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+ const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
EXPECT_EQ(entry.expected, actual) << "entry " << i;
}
}
@@ -436,7 +489,7 @@
// Mock everything called by updateCompositionState to simplify testing it.
MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
- MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t());
+ MOCK_CONST_METHOD1(calculateOutputRelativeBufferTransform, uint32_t(uint32_t));
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
@@ -463,10 +516,11 @@
~OutputLayerUpdateCompositionStateTest() = default;
- void setupGeometryChildCallValues() {
+ void setupGeometryChildCallValues(ui::Transform::RotationFlags internalDisplayRotationFlags) {
EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop));
EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame));
- EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform())
+ EXPECT_CALL(mOutputLayer,
+ calculateOutputRelativeBufferTransform(internalDisplayRotationFlags))
.WillOnce(Return(mBufferTransform));
}
@@ -489,7 +543,7 @@
TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.updateCompositionState(true, false);
+ mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
}
TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
@@ -497,9 +551,9 @@
mOutputState.isSecure = true;
mOutputLayer.editState().forceClientComposition = true;
- setupGeometryChildCallValues();
+ setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_90);
- mOutputLayer.updateCompositionState(true, false);
+ mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
validateComputedGeometryState();
@@ -511,9 +565,9 @@
mLayerFEState.isSecure = true;
mOutputState.isSecure = false;
- setupGeometryChildCallValues();
+ setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
- mOutputLayer.updateCompositionState(true, false);
+ mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_0);
validateComputedGeometryState();
@@ -527,9 +581,9 @@
mBufferTransform = ui::Transform::ROT_INVALID;
- setupGeometryChildCallValues();
+ setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
- mOutputLayer.updateCompositionState(true, false);
+ mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_0);
validateComputedGeometryState();
@@ -544,7 +598,7 @@
// should use the layers requested colorspace.
mLayerFEState.isColorspaceAgnostic = false;
- mOutputLayer.updateCompositionState(false, false);
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
@@ -552,7 +606,7 @@
// should use the colorspace chosen for the whole output.
mLayerFEState.isColorspaceAgnostic = true;
- mOutputLayer.updateCompositionState(false, false);
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
}
@@ -560,7 +614,7 @@
TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
mOutputLayer.editState().forceClientComposition = false;
- mOutputLayer.updateCompositionState(false, false);
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
}
@@ -569,7 +623,7 @@
doesNotClearForceClientCompositionIfNotDoingGeometry) {
mOutputLayer.editState().forceClientComposition = true;
- mOutputLayer.updateCompositionState(false, false);
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
}
@@ -578,7 +632,7 @@
mLayerFEState.forceClientComposition = true;
mOutputLayer.editState().forceClientComposition = false;
- mOutputLayer.updateCompositionState(false, false);
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
}
@@ -588,7 +642,7 @@
mOutputLayer.editState().forceClientComposition = false;
EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
- mOutputLayer.updateCompositionState(false, false);
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
}
@@ -597,15 +651,15 @@
mLayerFEState.forceClientComposition = false;
mOutputLayer.editState().forceClientComposition = false;
- mOutputLayer.updateCompositionState(false, true);
+ mOutputLayer.updateCompositionState(false, true, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
mOutputLayer.editState().forceClientComposition = false;
- setupGeometryChildCallValues();
+ setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
- mOutputLayer.updateCompositionState(true, true);
+ mOutputLayer.updateCompositionState(true, true, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
}
@@ -810,7 +864,7 @@
// geomBufferTransform is set to the inverse transform.
mLayerFEState.geomBufferTransform = TR_ROT_270;
- EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform());
+ EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform(ui::Transform::ROT_90));
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1c9cd9c..59ed72e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -696,11 +696,11 @@
InjectedLayer layer2;
InjectedLayer layer3;
- EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
- EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
- EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
injectOutputLayer(layer1);
@@ -712,6 +712,7 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
+ args.internalDisplayRotationFlags = ui::Transform::ROT_180;
mOutput->updateAndWriteCompositionState(args);
}
@@ -720,11 +721,11 @@
InjectedLayer layer2;
InjectedLayer layer3;
- EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
- EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
- EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
injectOutputLayer(layer1);
@@ -744,11 +745,11 @@
InjectedLayer layer2;
InjectedLayer layer3;
- EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
- EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
- EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
injectOutputLayer(layer1);
@@ -2797,6 +2798,7 @@
mOutput.mState.usesClientComposition = true;
mOutput.mState.usesDeviceComposition = false;
mOutput.mState.reusedClientComposition = false;
+ mOutput.mState.flipClientTarget = false;
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -2868,19 +2870,40 @@
TEST_F(OutputComposeSurfacesTest, doesNothingButSignalNoExpensiveRenderingIfNoClientComposition) {
mOutput.mState.usesClientComposition = false;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
verify().execute().expectAFenceWasReturned();
}
-TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFails) {
- EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
- EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+TEST_F(OutputComposeSurfacesTest,
+ dequeuesABufferIfNoClientCompositionButFlipClientTargetRequested) {
+ mOutput.mState.usesClientComposition = false;
+ mOutput.mState.flipClientTarget = true;
+
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
- .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
- EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
- .WillRepeatedly(Return());
+
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+ verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) {
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
+
+ verify().execute().expectNoFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest,
+ doesMinimalWorkIfDequeueBufferFailsForNoClientCompositionButFlipClientTargetRequested) {
+ mOutput.mState.usesClientComposition = false;
+ mOutput.mState.flipClientTarget = true;
+
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
@@ -3315,7 +3338,7 @@
mLayer.layerFEState.backgroundBlurRadius = 10;
mOutput.editState().isEnabled = true;
- EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
@@ -3894,11 +3917,11 @@
InjectedLayer layer3;
// Layer requesting blur, or below, should request client composition.
- EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
- EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
- EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
layer2.layerFEState.backgroundBlurRadius = 10;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9af9cad..9aa274b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,6 +116,10 @@
}
}
+void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
+ mDeviceProductInfo = std::move(info);
+}
+
uint32_t DisplayDevice::getPageFlipCount() const {
return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
@@ -158,19 +162,19 @@
mOrientation = orientation;
const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
- const int w = displayBounds.width();
- const int h = displayBounds.height();
+ const int displayWidth = displayBounds.width();
+ const int displayHeight = displayBounds.height();
- ui::Transform R;
+ ui::Transform rotation;
if (const auto flags = ui::Transform::toRotationFlags(orientation);
flags != ui::Transform::ROT_INVALID) {
- R.set(flags, w, h);
+ rotation.set(flags, displayWidth, displayHeight);
}
if (!frame.isValid()) {
// the destination frame can be invalid if it has never been set,
// in that case we assume the whole display frame.
- frame = Rect(w, h);
+ frame = Rect(displayWidth, displayHeight);
}
if (viewport.isEmpty()) {
@@ -178,45 +182,45 @@
// we assume the whole display size.
// it's also invalid to have an empty viewport, so we handle that
// case in the same way.
- viewport = Rect(w, h);
- if (R.getOrientation() & ui::Transform::ROT_90) {
+ viewport = Rect(displayWidth, displayHeight);
+ if (rotation.getOrientation() & ui::Transform::ROT_90) {
// viewport is always specified in the logical orientation
// of the display (ie: post-rotation).
std::swap(viewport.right, viewport.bottom);
}
}
- ui::Transform TL, TP, S;
- float src_width = viewport.width();
- float src_height = viewport.height();
- float dst_width = frame.width();
- float dst_height = frame.height();
- if (src_width != dst_width || src_height != dst_height) {
- float sx = dst_width / src_width;
- float sy = dst_height / src_height;
- S.set(sx, 0, 0, sy);
+ ui::Transform logicalTranslation, physicalTranslation, scale;
+ const float sourceWidth = viewport.width();
+ const float sourceHeight = viewport.height();
+ const float destWidth = frame.width();
+ const float destHeight = frame.height();
+ if (sourceWidth != destWidth || sourceHeight != destHeight) {
+ const float scaleX = destWidth / sourceWidth;
+ const float scaleY = destHeight / sourceHeight;
+ scale.set(scaleX, 0, 0, scaleY);
}
- float src_x = viewport.left;
- float src_y = viewport.top;
- float dst_x = frame.left;
- float dst_y = frame.top;
- TL.set(-src_x, -src_y);
- TP.set(dst_x, dst_y);
+ const float sourceX = viewport.left;
+ const float sourceY = viewport.top;
+ const float destX = frame.left;
+ const float destY = frame.top;
+ logicalTranslation.set(-sourceX, -sourceY);
+ physicalTranslation.set(destX, destY);
// need to take care of primary display rotation for globalTransform
// for case if the panel is not installed aligned with device orientation
if (isPrimary()) {
if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
flags != ui::Transform::ROT_INVALID) {
- R.set(flags, w, h);
+ rotation.set(flags, displayWidth, displayHeight);
}
}
// The viewport and frame are both in the logical orientation.
// Apply the logical translation, scale to physical size, apply the
// physical translation and finally rotate to the physical orientation.
- ui::Transform globalTransform = R * TP * S * TL;
+ ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
const uint8_t type = globalTransform.getType();
const bool needsFiltering =
@@ -227,6 +231,8 @@
if (destinationClip.isEmpty()) {
destinationClip = displayBounds;
}
+ // Make sure the destination clip is contained in the display bounds
+ destinationClip.intersect(displayBounds, &destinationClip);
uint32_t transformOrientation;
@@ -267,6 +273,12 @@
StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+ StringAppendF(&result, "deviceProductInfo=");
+ if (mDeviceProductInfo) {
+ mDeviceProductInfo->dump(result);
+ } else {
+ result.append("{}");
+ }
getCompositionDisplay()->dump(result);
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 8c86153..576488c 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -40,7 +40,6 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
-#include "RenderArea.h"
#include "Scheduler/HwcStrongTypes.h"
namespace android {
@@ -59,12 +58,14 @@
class DisplaySurface;
} // namespace compositionengine
-class DisplayDevice : public LightRefBase<DisplayDevice> {
+class DisplayDevice : public RefBase {
public:
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
explicit DisplayDevice(DisplayDeviceCreationArgs& args);
+
+ // Must be destroyed on the main thread because it may call into HWComposer.
virtual ~DisplayDevice();
std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
@@ -93,6 +94,10 @@
static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
+ ui::Transform::RotationFlags getTransformHint() const {
+ return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation());
+ }
+
const ui::Transform& getTransform() const;
const Rect& getViewport() const;
const Rect& getFrame() const;
@@ -132,6 +137,11 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
+ void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
+ const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
+ return mDeviceProductInfo;
+ }
+
/* ------------------------------------------------------------------------
* Display power mode management.
*/
@@ -178,6 +188,8 @@
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
+
+ std::optional<DeviceProductInfo> mDeviceProductInfo;
};
struct DisplayDeviceState {
@@ -185,7 +197,7 @@
DisplayId id;
DisplayConnectionType type;
hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-
+ std::optional<DeviceProductInfo> deviceProductInfo;
bool operator==(const Physical& other) const {
return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
}
@@ -233,118 +245,4 @@
bool isPrimary{false};
};
-class DisplayRenderArea : public RenderArea {
-public:
- DisplayRenderArea(const sp<const DisplayDevice>& display,
- RotationFlags rotation = ui::Transform::ROT_0)
- : DisplayRenderArea(display, display->getBounds(),
- static_cast<uint32_t>(display->getWidth()),
- static_cast<uint32_t>(display->getHeight()),
- display->getCompositionDataSpace(), rotation) {}
-
- DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
- bool allowSecureLayers = true)
- : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
- display->getViewport(), applyDeviceOrientation(rotation, display)),
- mDisplay(std::move(display)),
- mSourceCrop(sourceCrop),
- mAllowSecureLayers(allowSecureLayers) {}
-
- const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override { return mDisplay->getBounds(); }
- int getHeight() const override { return mDisplay->getHeight(); }
- int getWidth() const override { return mDisplay->getWidth(); }
- bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); }
- sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; }
-
- bool needsFiltering() const override {
- // check if the projection from the logical render area
- // to the physical render area requires filtering
- const Rect& sourceCrop = getSourceCrop();
- int width = sourceCrop.width();
- int height = sourceCrop.height();
- if (getRotationFlags() & ui::Transform::ROT_90) {
- std::swap(width, height);
- }
- return width != getReqWidth() || height != getReqHeight();
- }
-
- Rect getSourceCrop() const override {
- // use the projected display viewport by default.
- if (mSourceCrop.isEmpty()) {
- return mDisplay->getSourceClip();
- }
-
- // If there is a source crop provided then it is assumed that the device
- // was in portrait orientation. This may not logically be true, so
- // correct for the orientation error by undoing the rotation
-
- ui::Rotation logicalOrientation = mDisplay->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
- int width = mDisplay->getSourceClip().getWidth();
- int height = mDisplay->getSourceClip().getHeight();
- ui::Transform rotation;
- rotation.set(flags, width, height);
- return rotation.transform(mSourceCrop);
- }
-
-private:
- static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag,
- const sp<const DisplayDevice>& device) {
- uint32_t inverseRotate90 = 0;
- uint32_t inverseReflect = 0;
-
- // Reverse the logical orientation.
- ui::Rotation logicalOrientation = device->getOrientation();
- if (logicalOrientation == ui::Rotation::Rotation90) {
- logicalOrientation = ui::Rotation::Rotation270;
- } else if (logicalOrientation == ui::Rotation::Rotation270) {
- logicalOrientation = ui::Rotation::Rotation90;
- }
-
- const ui::Rotation orientation = device->getPhysicalOrientation() + logicalOrientation;
-
- switch (orientation) {
- case ui::ROTATION_0:
- return orientationFlag;
-
- case ui::ROTATION_90:
- inverseRotate90 = ui::Transform::ROT_90;
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_180:
- inverseReflect = ui::Transform::ROT_180;
- break;
-
- case ui::ROTATION_270:
- inverseRotate90 = ui::Transform::ROT_90;
- break;
- }
-
- const uint32_t rotate90 = orientationFlag & ui::Transform::ROT_90;
- uint32_t reflect = orientationFlag & ui::Transform::ROT_180;
-
- // Apply reflection for double rotation.
- if (rotate90 & inverseRotate90) {
- reflect = ~reflect & ui::Transform::ROT_180;
- }
-
- return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) |
- (reflect ^ inverseReflect));
- }
-
- const sp<const DisplayDevice> mDisplay;
- const Rect mSourceCrop;
- const bool mAllowSecureLayers;
- const ui::Transform mTransform = ui::Transform();
-};
-
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 97eeea2..a3f1b52 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1354,6 +1354,12 @@
return error;
}
+Error Composer::getClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ mReader.takeClientTargetProperty(display, outClientTargetProperty);
+ return Error::NONE;
+}
+
CommandReader::~CommandReader()
{
resetData();
@@ -1662,10 +1668,23 @@
*state = data.presentOrValidateState;
}
+void CommandReader::takeClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ auto found = mReturnData.find(display);
+
+ // If not found, return the default values.
+ if (found == mReturnData.end()) {
+ outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
+ outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+ return;
+ }
+
+ ReturnData& data = found->second;
+ *outClientTargetProperty = data.clientTargetProperty;
+}
+
} // namespace impl
-
} // namespace Hwc2
-
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index aa43f09..00ef782 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -240,6 +240,8 @@
const std::vector<uint8_t>& value) = 0;
virtual V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
+ virtual Error getClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
};
namespace impl {
@@ -282,6 +284,10 @@
// Get what stage succeeded during PresentOrValidate: Present or Validate
void takePresentOrValidateStage(Display display, uint32_t * state);
+ // Get the client target properties requested by hardware composer.
+ void takeClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty);
+
private:
void resetData();
@@ -479,6 +485,9 @@
bool mandatory, const std::vector<uint8_t>& value) override;
V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+ Error getClientTargetProperty(
+ Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 4dfc743..52a6380 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -71,12 +71,8 @@
DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
DeviceProductInfo info;
- std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
- info.name[edid.displayName.size()] = '\0';
-
- const auto productId = std::to_string(edid.productId);
- std::copy(productId.begin(), productId.end(), info.productId.begin());
- info.productId[productId.size()] = '\0';
+ info.name.assign(edid.displayName);
+ info.productId = std::to_string(edid.productId);
info.manufacturerPnpId = edid.pnpId;
constexpr uint8_t kModelYearFlag = 0xff;
@@ -99,8 +95,6 @@
if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
info.relativeAddress = {address.a, address.b, address.c, address.d};
- } else {
- info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS;
}
return info;
}
@@ -238,7 +232,6 @@
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
- static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
for (size_t i = 0; i < kDescriptorCount; i++) {
if (view.size() < kDescriptorLength) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 0ea3340..08559bd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -208,13 +208,17 @@
*outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
} else {
// Get the default vsync period
- HWConfigId configId = 0;
- auto intError_2_1 = mComposer.getActiveConfig(mId, &configId);
- error = static_cast<Error>(intError_2_1);
- if (error == Error::NONE) {
- auto config = mConfigs.at(configId);
- *outVsyncPeriod = config->getVsyncPeriod();
+ std::shared_ptr<const Display::Config> config;
+ error = getActiveConfig(&config);
+ if (error != Error::NONE) {
+ return error;
}
+ if (!config) {
+ // HWC has updated the display modes and hasn't notified us yet.
+ return Error::BAD_CONFIG;
+ }
+
+ *outVsyncPeriod = config->getVsyncPeriod();
}
return error;
@@ -349,7 +353,7 @@
Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
std::unordered_map<HWC2::Layer*, LayerRequest>* outLayerRequests) {
- uint32_t intDisplayRequests;
+ uint32_t intDisplayRequests = 0;
std::vector<Hwc2::Layer> layerIds;
std::vector<uint32_t> layerRequests;
auto intError = mComposer.getDisplayRequests(
@@ -668,6 +672,11 @@
return static_cast<Error>(intError);
}
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
+ const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+ return static_cast<Error>(error);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
@@ -885,6 +894,10 @@
mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
if (validTypes & HdrMetadata::HDR10PLUS) {
+ if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
+ return Error::BAD_PARAMETER;
+ }
+
std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
perFrameMetadataBlobs.push_back(
{Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index f4c7fdd..6819ff4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -234,6 +234,8 @@
[[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>*) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
+ [[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
+ hal::ClientTargetProperty* outClientTargetProperty) = 0;
};
namespace impl {
@@ -305,6 +307,8 @@
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
hal::Error setContentType(hal::ContentType) override;
+ hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 038cec4..7a2f0f3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -541,9 +541,12 @@
error = hwcDisplay->getRequests(&displayRequests, &layerRequests);
RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
- outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
- std::move(layerRequests)});
+ DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
+ error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+ outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
+ std::move(layerRequests),
+ std::move(clientTargetProperty)});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b7e9f3a..b800038 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -64,8 +64,22 @@
const uint32_t id;
};
+// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing
+// this class.
class HWComposer {
public:
+ struct DeviceRequestedChanges {
+ using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+ using ClientTargetProperty = hal::ClientTargetProperty;
+ using DisplayRequests = hal::DisplayRequest;
+ using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
+
+ ChangedTypes changedTypes;
+ DisplayRequests displayRequests;
+ LayerRequests layerRequests;
+ ClientTargetProperty clientTargetProperty;
+ };
+
virtual ~HWComposer();
virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
@@ -88,16 +102,6 @@
// Destroy a previously created layer
virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
- struct DeviceRequestedChanges {
- using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
- using DisplayRequests = hal::DisplayRequest;
- using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
-
- ChangedTypes changedTypes;
- DisplayRequests displayRequests;
- LayerRequests layerRequests;
- };
-
// Gets any required composition change requests from the HWC device.
//
// Note that frameUsesClientComposition must be set correctly based on
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 66ee425..bb2888e 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -53,6 +53,7 @@
using Connection = IComposerCallback::Connection;
using ContentType = IComposerClient::ContentType;
using Capability = IComposer::Capability;
+using ClientTargetProperty = IComposerClient::ClientTargetProperty;
using DisplayCapability = IComposerClient::DisplayCapability;
using DisplayRequest = IComposerClient::DisplayRequest;
using DisplayType = IComposerClient::DisplayType;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 1d8179c..4b4c050 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -85,6 +85,7 @@
const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
+ std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
return;
@@ -108,6 +109,7 @@
}
if (mSendUpdateImminent.load()) {
+ std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
return;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 34e63e7..95eb0e2 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -19,6 +19,8 @@
#include <atomic>
#include <unordered_set>
+#include <utils/Mutex.h>
+
#include "../Scheduler/OneShotTimer.h"
#include "DisplayIdentification.h"
@@ -56,10 +58,11 @@
void notifyDisplayUpdateImminent() override;
private:
- HalWrapper* getPowerHal();
+ HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
+ bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
+ std::mutex mPowerHalMutex;
std::atomic_bool mBootFinished = false;
- bool mReconnectPowerHal = false;
std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
new file mode 100644
index 0000000..7cd283d
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2020 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 "DisplayRenderArea.h"
+#include "DisplayDevice.h"
+
+namespace android {
+namespace {
+
+RenderArea::RotationFlags applyDeviceOrientation(RenderArea::RotationFlags rotation,
+ const DisplayDevice& display) {
+ uint32_t inverseRotate90 = 0;
+ uint32_t inverseReflect = 0;
+
+ // Reverse the logical orientation.
+ ui::Rotation logicalOrientation = display.getOrientation();
+ if (logicalOrientation == ui::Rotation::Rotation90) {
+ logicalOrientation = ui::Rotation::Rotation270;
+ } else if (logicalOrientation == ui::Rotation::Rotation270) {
+ logicalOrientation = ui::Rotation::Rotation90;
+ }
+
+ const ui::Rotation orientation = display.getPhysicalOrientation() + logicalOrientation;
+
+ switch (orientation) {
+ case ui::ROTATION_0:
+ return rotation;
+
+ case ui::ROTATION_90:
+ inverseRotate90 = ui::Transform::ROT_90;
+ inverseReflect = ui::Transform::ROT_180;
+ break;
+
+ case ui::ROTATION_180:
+ inverseReflect = ui::Transform::ROT_180;
+ break;
+
+ case ui::ROTATION_270:
+ inverseRotate90 = ui::Transform::ROT_90;
+ break;
+ }
+
+ const uint32_t rotate90 = rotation & ui::Transform::ROT_90;
+ uint32_t reflect = rotation & ui::Transform::ROT_180;
+
+ // Apply reflection for double rotation.
+ if (rotate90 & inverseRotate90) {
+ reflect = ~reflect & ui::Transform::ROT_180;
+ }
+
+ return static_cast<RenderArea::RotationFlags>((rotate90 ^ inverseRotate90) |
+ (reflect ^ inverseReflect));
+}
+
+} // namespace
+
+std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
+ const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace,
+ RotationFlags rotation,
+ bool allowSecureLayers) {
+ if (auto display = displayWeak.promote()) {
+ // Using new to access a private constructor.
+ return std::unique_ptr<DisplayRenderArea>(
+ new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
+ rotation, allowSecureLayers));
+ }
+ return nullptr;
+}
+
+DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace,
+ RotationFlags rotation, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getViewport(),
+ applyDeviceOrientation(rotation, *display)),
+ mDisplay(std::move(display)),
+ mSourceCrop(sourceCrop),
+ mAllowSecureLayers(allowSecureLayers) {}
+
+const ui::Transform& DisplayRenderArea::getTransform() const {
+ return mTransform;
+}
+
+Rect DisplayRenderArea::getBounds() const {
+ return mDisplay->getBounds();
+}
+
+int DisplayRenderArea::getHeight() const {
+ return mDisplay->getHeight();
+}
+
+int DisplayRenderArea::getWidth() const {
+ return mDisplay->getWidth();
+}
+
+bool DisplayRenderArea::isSecure() const {
+ return mAllowSecureLayers && mDisplay->isSecure();
+}
+
+sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const {
+ return mDisplay;
+}
+
+bool DisplayRenderArea::needsFiltering() const {
+ // check if the projection from the logical render area
+ // to the physical render area requires filtering
+ const Rect& sourceCrop = getSourceCrop();
+ int width = sourceCrop.width();
+ int height = sourceCrop.height();
+ if (getRotationFlags() & ui::Transform::ROT_90) {
+ std::swap(width, height);
+ }
+ return width != getReqWidth() || height != getReqHeight();
+}
+
+Rect DisplayRenderArea::getSourceCrop() const {
+ // use the projected display viewport by default.
+ if (mSourceCrop.isEmpty()) {
+ return mDisplay->getSourceClip();
+ }
+
+ // If there is a source crop provided then it is assumed that the device
+ // was in portrait orientation. This may not logically be true, so
+ // correct for the orientation error by undoing the rotation
+
+ ui::Rotation logicalOrientation = mDisplay->getOrientation();
+ if (logicalOrientation == ui::Rotation::Rotation90) {
+ logicalOrientation = ui::Rotation::Rotation270;
+ } else if (logicalOrientation == ui::Rotation::Rotation270) {
+ logicalOrientation = ui::Rotation::Rotation90;
+ }
+
+ const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
+ int width = mDisplay->getSourceClip().getWidth();
+ int height = mDisplay->getSourceClip().getHeight();
+ ui::Transform rotation;
+ rotation.set(flags, width, height);
+ return rotation.transform(mSourceCrop);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
new file mode 100644
index 0000000..340efb5
--- /dev/null
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+
+class DisplayRenderArea : public RenderArea {
+public:
+ static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
+ ui::Size reqSize, ui::Dataspace,
+ RotationFlags rotation,
+ bool allowSecureLayers = true);
+
+ const ui::Transform& getTransform() const override;
+ Rect getBounds() const override;
+ int getHeight() const override;
+ int getWidth() const override;
+ bool isSecure() const override;
+ sp<const DisplayDevice> getDisplayDevice() const override;
+ bool needsFiltering() const override;
+ Rect getSourceCrop() const override;
+
+private:
+ DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace, RotationFlags rotation, bool allowSecureLayers = true);
+
+ const sp<const DisplayDevice> mDisplay;
+ const Rect mSourceCrop;
+ const bool mAllowSecureLayers;
+ const ui::Transform mTransform;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index b986f38..2dfb9a9 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -25,7 +25,7 @@
#include "FrameTracer.h"
#include <android-base/stringprintf.h>
-#include <perfetto/trace/clock_snapshot.pbzero.h>
+#include <perfetto/common/builtin_clock.pbzero.h>
#include <algorithm>
#include <mutex>
@@ -34,7 +34,6 @@
namespace android {
-using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
void FrameTracer::initialize() {
std::call_once(mInitializationFlag, [this]() {
perfetto::TracingInitArgs args;
@@ -136,7 +135,7 @@
uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration) {
auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(Clock::MONOTONIC);
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
packet->set_timestamp(timestamp);
auto* event = packet->set_graphics_frame_event()->set_buffer_event();
event->set_buffer_id(static_cast<uint32_t>(bufferID));
diff --git a/services/surfaceflinger/FrameTracer/OWNERS b/services/surfaceflinger/FrameTracer/OWNERS
new file mode 100644
index 0000000..e4f5d45
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/OWNERS
@@ -0,0 +1 @@
+adsrini@google.com
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 752407a..89c95d2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -67,12 +67,14 @@
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
+#include "input/InputWindow.h"
#define DEBUG_RESIZE 0
namespace android {
using base::StringAppendF;
+using namespace android::flag_operators;
std::atomic<int32_t> Layer::sSequence{1};
@@ -80,7 +82,8 @@
: mFlinger(args.flinger),
mName(args.name),
mClientRef(args.client),
- mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
+ mWindowType(static_cast<InputWindowInfo::Type>(
+ args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -117,6 +120,14 @@
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
mCurrentState.treeHasFrameRateVote = false;
+ mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+
+ if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+ // Set an invalid color so there is no color fill.
+ mCurrentState.color.r = -1.0_hf;
+ mCurrentState.color.g = -1.0_hf;
+ mCurrentState.color.b = -1.0_hf;
+ }
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -144,11 +155,10 @@
mFlinger->onLayerDestroyed(this);
}
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client,
- std::string name, uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata)
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+ uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
: flinger(flinger),
- client(client),
+ client(std::move(client)),
name(std::move(name)),
w(w),
h(h),
@@ -222,13 +232,23 @@
mFlinger->markLayerPendingRemovalLocked(this);
}
+sp<Layer> Layer::getRootLayer() {
+ sp<Layer> parent = getParent();
+ if (parent == nullptr) {
+ return this;
+ }
+ return parent->getRootLayer();
+}
+
void Layer::onRemovedFromCurrentState() {
- auto layersInTree = getLayersInTree(LayerVector::StateSet::Current);
+ // Use the root layer since we want to maintain the hierarchy for the entire subtree.
+ auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current);
std::sort(layersInTree.begin(), layersInTree.end());
- for (const auto& layer : layersInTree) {
+
+ traverse(LayerVector::StateSet::Current, [&](Layer* layer) {
layer->removeFromCurrentState();
layer->removeRelativeZ(layersInTree);
- }
+ });
}
void Layer::addToCurrentState() {
@@ -717,9 +737,8 @@
return {*shadowSettings};
}
-Hwc2::IComposerClient::Composition Layer::getCompositionType(
- const sp<const DisplayDevice>& display) const {
- const auto outputLayer = findOutputLayerForDisplay(display);
+Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+ const auto outputLayer = findOutputLayerForDisplay(&display);
if (outputLayer == nullptr) {
return Hwc2::IComposerClient::Composition::INVALID;
}
@@ -730,12 +749,6 @@
}
}
-bool Layer::getClearClientTarget(const sp<const DisplayDevice>& display) const {
- const auto outputLayer = findOutputLayerForDisplay(display);
- LOG_FATAL_IF(!outputLayer);
- return outputLayer->getState().clearClientTarget;
-}
-
bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
if (point->getFrameNumber() <= mCurrentFrameNumber) {
// Don't bother with a SyncPoint, since we've already latched the
@@ -855,11 +868,13 @@
}
}
- // If we still have pending updates, wake SurfaceFlinger back up and point
- // it at this layer so we can process them
+ // If we still have pending updates, we need to ensure SurfaceFlinger
+ // will keep calling doTransaction, and so we force a traversal.
+ // However, our pending states won't clear until a frame is available,
+ // and so there is no need to specifically trigger a wakeup.
if (!mPendingStates.empty()) {
setTransactionFlags(eTransactionNeeded);
- mFlinger->setTransactionFlags(eTraversalNeeded);
+ mFlinger->setTraversalNeeded();
}
mCurrentState.modified = false;
@@ -1313,6 +1328,10 @@
return Layer::PRIORITY_UNSET;
}
+bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
+ return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
+};
+
uint32_t Layer::getLayerStack() const {
auto p = mDrawingParent.promote();
if (p == nullptr) {
@@ -1333,6 +1352,18 @@
return true;
}
+bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint) {
+ if (mCurrentState.fixedTransformHint == fixedTransformHint) {
+ return false;
+ }
+
+ mCurrentState.sequence++;
+ mCurrentState.fixedTransformHint = fixedTransformHint;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
void Layer::updateTreeHasFrameRateVote() {
const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
auto parent = getParent();
@@ -1348,8 +1379,15 @@
// First traverse the tree and count how many layers has votes
int layersWithVote = 0;
traverseTree([&layersWithVote](Layer* layer) {
- if (layer->mCurrentState.frameRate.rate > 0 ||
- layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+ const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 &&
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote =
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote;
+
+ // We do not count layers that are ExactOrMultiple for the same reason
+ // we are allowing touch boost for those layers. See
+ // RefreshRateConfigs::getBestRefreshRate for more details.
+ if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote) {
layersWithVote++;
}
});
@@ -1380,7 +1418,8 @@
}
// Activate the layer in Scheduler's LayerHistory
- mFlinger->mScheduler->recordLayerHistory(this, systemTime());
+ mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+ LayerHistory::LayerUpdateType::SetFrameRate);
mCurrentState.sequence++;
mCurrentState.frameRate = frameRate;
@@ -1459,20 +1498,12 @@
return usage;
}
-void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const {
- uint32_t orientation = 0;
- // Disable setting transform hint if the debug flag is set.
- if (!mFlinger->mDebugDisableTransformHint) {
- // The transform hint is used to improve performance, but we can
- // only have a single transform hint, it cannot
- // apply to all displays.
- const ui::Transform& planeTransform = display->getTransform();
- orientation = planeTransform.getOrientation();
- if (orientation & ui::Transform::ROT_INVALID) {
- orientation = 0;
- }
+void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
+ if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
+ transformHint = ui::Transform::ROT_0;
}
- setTransformHint(orientation);
+
+ setTransformHint(transformHint);
}
// ----------------------------------------------------------------------------
@@ -1480,18 +1511,18 @@
// ----------------------------------------------------------------------------
// TODO(marissaw): add new layer state info to layer debugging
-LayerDebugInfo Layer::getLayerDebugInfo() const {
+LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
using namespace std::string_literals;
LayerDebugInfo info;
const State& ds = getDrawingState();
info.mName = getName();
- sp<Layer> parent = getParent();
+ sp<Layer> parent = mDrawingParent.promote();
info.mParentName = parent ? parent->getName() : "none"s;
info.mType = getType();
info.mTransparentRegion = ds.activeTransparentRegion_legacy;
- info.mVisibleRegion = debugGetVisibleRegionOnDefaultDisplay();
+ info.mVisibleRegion = getVisibleRegion(display);
info.mSurfaceDamageRegion = surfaceDamageRegion;
info.mLayerStack = getLayerStack();
info.mX = ds.active_legacy.transform.tx();
@@ -1534,7 +1565,7 @@
result.append("-------------------------------");
result.append("-------------------------------");
result.append("-------------------------------");
- result.append("---------\n");
+ result.append("-------------------\n");
result.append(" Layer name\n");
result.append(" Z | ");
result.append(" Window Type | ");
@@ -1542,12 +1573,12 @@
result.append(" Transform | ");
result.append(" Disp Frame (LTRB) | ");
result.append(" Source Crop (LTRB) | ");
- result.append(" Frame Rate (Explicit)\n");
+ result.append(" Frame Rate (Explicit) [Focused]\n");
result.append("-------------------------------");
result.append("-------------------------------");
result.append("-------------------------------");
result.append("-------------------------------");
- result.append("---------\n");
+ result.append("-------------------\n");
}
std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
@@ -1561,8 +1592,8 @@
}
}
-void Layer::miniDump(std::string& result, const sp<DisplayDevice>& displayDevice) const {
- auto outputLayer = findOutputLayerForDisplay(displayDevice);
+void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
+ const auto outputLayer = findOutputLayerForDisplay(&display);
if (!outputLayer) {
return;
}
@@ -1589,7 +1620,7 @@
StringAppendF(&result, " %10d | ", layerState.z);
}
StringAppendF(&result, " %10d | ", mWindowType);
- StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
+ StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str());
StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
const Rect& frame = outputLayerState.displayFrame;
StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
@@ -1598,17 +1629,20 @@
crop.bottom);
if (layerState.frameRate.rate != 0 ||
layerState.frameRate.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "% 6.2ffps %15s\n", layerState.frameRate.rate,
+ StringAppendF(&result, "% 6.2ffps %15s", layerState.frameRate.rate,
frameRateCompatibilityString(layerState.frameRate.type).c_str());
} else {
- StringAppendF(&result, "\n");
+ StringAppendF(&result, " ");
}
+ const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
+ StringAppendF(&result, " [%s]\n", focused ? "*" : " ");
+
result.append("- - - - - - - - - - - - - - - - ");
result.append("- - - - - - - - - - - - - - - - ");
result.append("- - - - - - - - - - - - - - - - ");
result.append("- - - - - - - - - - - - - - - - ");
- result.append("- - -\n");
+ result.append("- - - - - - - -\n");
}
void Layer::dumpFrameStats(std::string& result) const {
@@ -1789,7 +1823,7 @@
onRemovedFromCurrentState();
}
- if (callSetTransactionFlags || attachChildren()) {
+ if (attachChildren() || callSetTransactionFlags) {
setTransactionFlags(eTransactionNeeded);
}
return true;
@@ -1817,9 +1851,9 @@
if (client != nullptr && parentClient != client) {
if (child->mLayerDetached) {
child->mLayerDetached = false;
+ child->attachChildren();
changed = true;
}
- changed |= child->attachChildren();
}
}
@@ -2076,6 +2110,16 @@
return parentAlpha * getDrawingState().color.a;
}
+ui::Transform::RotationFlags Layer::getFixedTransformHint() const {
+ ui::Transform::RotationFlags fixedTransformHint = mCurrentState.fixedTransformHint;
+ if (fixedTransformHint != ui::Transform::ROT_INVALID) {
+ return fixedTransformHint;
+ }
+ const auto& p = mCurrentParent.promote();
+ if (!p) return fixedTransformHint;
+ return p->getFixedTransformHint();
+}
+
half4 Layer::getColor() const {
const half4 color(getDrawingState().color);
return half4(color.r, color.g, color.b, getAlpha());
@@ -2097,7 +2141,9 @@
// but a transform matrix can define horizontal and vertical scales.
// Let's take the average between both of them and pass into the shader, practically we
// never do this type of transformation on windows anyway.
- parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+ auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
+ auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
+ parentState.radius *= (scaleX + scaleY) / 2.0f;
return parentState;
}
}
@@ -2152,27 +2198,28 @@
}
LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
- const sp<const DisplayDevice>& device) const {
+ const DisplayDevice* display) {
LayerProto* layerProto = layersProto.add_layers();
- writeToProtoDrawingState(layerProto, traceFlags);
+ writeToProtoDrawingState(layerProto, traceFlags, display);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
// Only populate for the primary display.
- if (device) {
- const Hwc2::IComposerClient::Composition compositionType = getCompositionType(device);
+ if (display) {
+ const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display);
layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
}
}
for (const sp<Layer>& layer : mDrawingChildren) {
- layer->writeToProto(layersProto, traceFlags, device);
+ layer->writeToProto(layersProto, traceFlags, display);
}
return layerProto;
}
-void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const {
+void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
+ const DisplayDevice* display) {
ui::Transform transform = getTransform();
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
@@ -2201,12 +2248,13 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
[&]() { return layerInfo->mutable_position(); });
LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
- LayerProtoHelper::writeToProto(debugGetVisibleRegionOnDefaultDisplay(),
+ LayerProtoHelper::writeToProto(getVisibleRegion(display),
[&]() { return layerInfo->mutable_visible_region(); });
}
LayerProtoHelper::writeToProto(surfaceDamageRegion,
@@ -2228,7 +2276,7 @@
}
void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
- uint32_t traceFlags) const {
+ uint32_t traceFlags) {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
@@ -2298,7 +2346,14 @@
}
if (traceFlags & SurfaceTracing::TRACE_INPUT) {
- LayerProtoHelper::writeToProto(state.inputInfo, state.touchableRegionCrop,
+ InputWindowInfo info;
+ if (useDrawing) {
+ info = fillInputInfo();
+ } else {
+ info = state.inputInfo;
+ }
+
+ LayerProtoHelper::writeToProto(info, state.touchableRegionCrop,
[&]() { return layerInfo->mutable_input_window_info(); });
}
@@ -2315,6 +2370,15 @@
}
InputWindowInfo Layer::fillInputInfo() {
+ if (!hasInputInfo()) {
+ mDrawingState.inputInfo.name = getName();
+ mDrawingState.inputInfo.ownerUid = mCallingUid;
+ mDrawingState.inputInfo.ownerPid = mCallingPid;
+ mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+ mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+ mDrawingState.inputInfo.displayId = getLayerStack();
+ }
+
InputWindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
@@ -2323,17 +2387,8 @@
}
ui::Transform t = getTransform();
- const float xScale = t.sx();
- const float yScale = t.sy();
int32_t xSurfaceInset = info.surfaceInset;
int32_t ySurfaceInset = info.surfaceInset;
- if (xScale != 1.0f || yScale != 1.0f) {
- info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f;
- info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f;
- info.touchableRegion.scaleSelf(xScale, yScale);
- xSurfaceInset = std::round(xSurfaceInset * xScale);
- ySurfaceInset = std::round(ySurfaceInset * yScale);
- }
// Transform layer size to screen space and inset it by surface insets.
// If this is a portal window, set the touchableRegion to the layerBounds.
@@ -2343,6 +2398,15 @@
if (!layerBounds.isValid()) {
layerBounds = getCroppedBufferSize(getDrawingState());
}
+
+ const float xScale = t.getScaleX();
+ const float yScale = t.getScaleY();
+ if (xScale != 1.0f || yScale != 1.0f) {
+ info.touchableRegion.scaleSelf(xScale, yScale);
+ xSurfaceInset = std::round(xSurfaceInset * xScale);
+ ySurfaceInset = std::round(ySurfaceInset * yScale);
+ }
+
layerBounds = t.transform(layerBounds);
// clamp inset to layer bounds
@@ -2357,10 +2421,22 @@
info.frameRight = layerBounds.right;
info.frameBottom = layerBounds.bottom;
+ ui::Transform inputTransform(t);
+ inputTransform.set(layerBounds.left, layerBounds.top);
+ info.transform = inputTransform.inverse();
+
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
- info.visible = canReceiveInput();
+ // For compatibility reasons we let layers which can receive input
+ // receive input before they have actually submitted a buffer. Because
+ // of this we use canReceiveInput instead of isVisible to check the
+ // policy-visibility, ignoring the buffer state. However for layers with
+ // hasInputInfo()==false we can use the real visibility state.
+ // We are just using these layers for occlusion detection in
+ // InputDispatcher, and obviously if they aren't visible they can't occlude
+ // anything.
+ info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
@@ -2396,7 +2472,7 @@
return mDrawingParent.promote()->getClonedRoot();
}
-bool Layer::hasInput() const {
+bool Layer::hasInputInfo() const {
return mDrawingState.inputInfo.token != nullptr;
}
@@ -2405,22 +2481,14 @@
}
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
- const sp<const DisplayDevice>& display) const {
+ const DisplayDevice* display) const {
+ if (!display) return nullptr;
return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
}
-Region Layer::debugGetVisibleRegionOnDefaultDisplay() const {
- sp<DisplayDevice> displayDevice = mFlinger->getDefaultDisplayDeviceLocked();
- if (displayDevice == nullptr) {
- return {};
- }
-
- auto outputLayer = findOutputLayerForDisplay(displayDevice);
- if (outputLayer == nullptr) {
- return {};
- }
-
- return outputLayer->getState().visibleRegion;
+Region Layer::getVisibleRegion(const DisplayDevice* display) const {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ return outputLayer ? outputLayer->getState().visibleRegion : Region();
}
void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
@@ -2513,7 +2581,7 @@
}
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
- mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+ mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
}
void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b630333..99b1bb1 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_LAYER_H
-#define ANDROID_LAYER_H
+#pragma once
#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
@@ -77,8 +76,8 @@
// ---------------------------------------------------------------------------
struct LayerCreationArgs {
- LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata);
+ LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
+ uint32_t flags, LayerMetadata);
SurfaceFlinger* flinger;
const sp<Client> client;
@@ -87,15 +86,24 @@
uint32_t h;
uint32_t flags;
LayerMetadata metadata;
+
pid_t callingPid;
uid_t callingUid;
- sp<const DisplayDevice> displayDevice;
uint32_t textureName;
};
class Layer : public virtual RefBase, compositionengine::LayerFE {
static std::atomic<int32_t> sSequence;
+ // The following constants represent priority of the window. SF uses this information when
+ // deciding which window has a priority when deciding about the refresh rate of the screen.
+ // Priority 0 is considered the highest priority. -1 means that the priority is unset.
static constexpr int32_t PRIORITY_UNSET = -1;
+ // Windows that are in focus and voted for the preferred mode ID
+ static constexpr int32_t PRIORITY_FOCUSED_WITH_MODE = 0;
+ // // Windows that are in focus, but have not requested a specific mode ID.
+ static constexpr int32_t PRIORITY_FOCUSED_WITHOUT_MODE = 1;
+ // Windows that are not in focus, but voted for a specific mode ID.
+ static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
public:
mutable bool contentDirty{false};
@@ -262,6 +270,15 @@
// Indicates whether parents / children of this layer had set FrameRate
bool treeHasFrameRateVote;
+
+ // Set by window manager indicating the layer and all its children are
+ // in a different orientation than the display. The hint suggests that
+ // the graphic producers should receive a transform hint as if the
+ // display was in this orientation. When the display changes to match
+ // the layer orientation, the graphic producer may not need to allocate
+ // a buffer of a different size. ui::Transform::ROT_INVALID means the
+ // a fixed transform hint is not set.
+ ui::Transform::RotationFlags fixedTransformHint;
};
explicit Layer(const LayerCreationArgs& args);
@@ -269,7 +286,7 @@
void onFirstRef() override;
- int getWindowType() const { return mWindowType; }
+ InputWindowInfo::Type getWindowType() const { return mWindowType; }
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
@@ -388,9 +405,11 @@
virtual bool setColorSpaceAgnostic(const bool agnostic);
bool setShadowRadius(float shadowRadius);
virtual bool setFrameRateSelectionPriority(int32_t priority);
+ virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
// If the variable is not set on the layer, it traverses up the tree to inherit the frame
// rate priority from its parent.
virtual int32_t getFrameRateSelectionPriority() const;
+ static bool isLayerFocusedBasedOnPriority(int32_t priority);
virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
@@ -509,20 +528,17 @@
bool isRemovedFromCurrentState() const;
- LayerProto* writeToProto(LayersProto& layersProto,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
- const sp<const DisplayDevice>& device = nullptr) const;
+ LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
// Write states that are modified by the main thread. This includes drawing
// state as well as buffer data. This should be called in the main or tracing
// thread.
- void writeToProtoDrawingState(LayerProto* layerInfo,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice*);
// Write drawing or current state. If writing current state, the caller should hold the
// external mStateLock. If writing drawing state, this function should be called on the
// main or tracing thread.
void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
@@ -534,7 +550,7 @@
return s.activeTransparentRegion_legacy;
}
virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
- virtual bool needsFiltering(const sp<const DisplayDevice>&) const { return false; }
+ virtual bool needsFiltering(const DisplayDevice*) const { return false; }
// True if this layer requires filtering
// This method is distinct from needsFiltering() in how the filter
// requirement is computed. needsFiltering() compares displayFrame and crop,
@@ -544,8 +560,7 @@
// different.
// If the parent transform needs to be undone when capturing the layer, then
// the inverse parent transform is also required.
- virtual bool needsFilteringForScreenshots(const sp<const DisplayDevice>&,
- const ui::Transform&) const {
+ virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
return false;
}
@@ -606,21 +621,16 @@
virtual bool isHdrY410() const { return false; }
- Hwc2::IComposerClient::Composition getCompositionType(
- const sp<const DisplayDevice>& display) const;
- bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
-
virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
- virtual void setTransformHint(uint32_t /*orientation*/) const { }
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
*/
- virtual bool onPostComposition(sp<const DisplayDevice> /*displayDevice*/,
+ virtual bool onPostComposition(const DisplayDevice*,
const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming& /*compositorTiming*/) {
+ const CompositorTiming&) {
return false;
}
@@ -672,9 +682,10 @@
*/
void addToCurrentState();
- // Updates the transform hint in our SurfaceFlingerConsumer to match
- // the current orientation of the display device.
- void updateTransformHint(const sp<const DisplayDevice>& display) const;
+ /*
+ * Sets display transform hint on BufferLayerConsumer.
+ */
+ void updateTransformHint(ui::Transform::RotationFlags);
/*
* returns the rectangle that crops the content of the layer and scales it
@@ -689,6 +700,8 @@
virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+ virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+
/*
* Returns if a frame is ready
*/
@@ -701,11 +714,10 @@
inline const State& getCurrentState() const { return mCurrentState; }
inline State& getCurrentState() { return mCurrentState; }
- LayerDebugInfo getLayerDebugInfo() const;
+ LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
- /* always call base class first */
static void miniDumpHeader(std::string& result);
- void miniDump(std::string& result, const sp<DisplayDevice>& display) const;
+ void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
void dumpFrameEvents(std::string& result);
void dumpCallingUidPid(std::string& result) const;
@@ -733,6 +745,12 @@
int32_t getBackgroundBlurRadius() const;
bool drawShadows() const { return mEffectiveShadowRadius > 0.f; };
+ // Returns the transform hint set by Window Manager on the layer or one of its parents.
+ // This traverses the current state because the data is needed when creating
+ // the layer(off drawing thread) and the hint should be available before the producer
+ // is ready to acquire a buffer.
+ ui::Transform::RotationFlags getFixedTransformHint() const;
+
// Returns how rounded corners should be drawn for this layer.
// This will traverse the hierarchy until it reaches its root, finding topmost rounded
// corner definition and converting it into current layer's coordinates.
@@ -804,11 +822,6 @@
return parentBounds;
}
- compositionengine::OutputLayer* findOutputLayerForDisplay(
- const sp<const DisplayDevice>& display) const;
-
- Region debugGetVisibleRegionOnDefaultDisplay() const;
-
/**
* Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
* INVALID_RECT if the layer has no buffer and no crop.
@@ -942,10 +955,21 @@
void setInputInfo(const InputWindowInfo& info);
InputWindowInfo fillInputInfo();
- bool hasInput() const;
+ /**
+ * Returns whether this layer has an explicitly set input-info.
+ */
+ bool hasInputInfo() const;
+ /**
+ * Return whether this layer needs an input info. For most layer types
+ * this is only true if they explicitly set an input-info but BufferLayer
+ * overrides this so we can generate input-info for Buffered layers that don't
+ * have them (for input occlusion detection checks).
+ */
+ virtual bool needsInputInfo() const { return hasInputInfo(); }
protected:
- // -----------------------------------------------------------------------
+ compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+
bool usingRelativeZ(LayerVector::StateSet stateSet) const;
bool mPremultipliedAlpha{true};
@@ -1014,9 +1038,14 @@
bool mChildrenChanged{false};
// Window types from WindowManager.LayoutParams
- const int mWindowType;
+ const InputWindowInfo::Type mWindowType;
private:
+ virtual void setTransformHint(ui::Transform::RotationFlags) {}
+
+ Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
+ Region getVisibleRegion(const DisplayDevice*) const;
+
/**
* Returns an unsorted vector of all layers that are part of this tree.
* That includes the current layer and all its descendants.
@@ -1077,17 +1106,10 @@
// Find the root of the cloned hierarchy, this means the first non cloned parent.
// This will return null if first non cloned parent is not found.
sp<Layer> getClonedRoot();
+
+ // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
+ // null.
+ sp<Layer> getRootLayer();
};
} // namespace android
-
-#define RETURN_IF_NO_HWC_LAYER(displayDevice, ...) \
- do { \
- if (!hasHwcLayer(displayDevice)) { \
- ALOGE("[%s] %s failed: no HWC layer found for display %s", mName.string(), \
- __FUNCTION__, displayDevice->getDebugName().c_str()); \
- return __VA_ARGS__; \
- } \
- } while (false)
-
-#endif // ANDROID_LAYER_H
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0fe1421..5d99908 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -131,8 +131,12 @@
}
InputWindowInfoProto* proto = getInputWindowInfoProto();
- proto->set_layout_params_flags(inputInfo.layoutParamsFlags);
- proto->set_layout_params_type(inputInfo.layoutParamsType);
+ proto->set_layout_params_flags(inputInfo.flags.get());
+ using U = std::underlying_type_t<InputWindowInfo::Type>;
+ // TODO(b/129481165): This static assert can be safely removed once conversion warnings
+ // are re-enabled.
+ static_assert(std::is_same_v<U, int32_t>);
+ proto->set_layout_params_type(static_cast<U>(inputInfo.type));
LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
inputInfo.frameBottom},
@@ -147,8 +151,7 @@
proto->set_has_wallpaper(inputInfo.hasWallpaper);
proto->set_global_scale_factor(inputInfo.globalScaleFactor);
- proto->set_window_x_scale(inputInfo.windowXScale);
- proto->set_window_y_scale(inputInfo.windowYScale);
+ LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform());
proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
auto cropLayer = touchableRegionBounds.promote();
if (cropLayer != nullptr) {
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
new file mode 100644
index 0000000..c4f8666
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 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 <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "LayerRenderArea.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+namespace {
+
+struct ReparentForDrawing {
+ const sp<Layer>& oldParent;
+
+ ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+ const Rect& drawingBounds)
+ : oldParent(oldParent) {
+ // Compute and cache the bounds for the new parent layer.
+ newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+ 0.f /* shadowRadius */);
+ oldParent->setChildrenDrawingParent(newParent);
+ }
+ ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+};
+
+} // namespace
+
+LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
+ const Rect& displayViewport)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, displayViewport),
+ mLayer(std::move(layer)),
+ mCrop(crop),
+ mFlinger(flinger),
+ mChildrenOnly(childrenOnly) {}
+
+const ui::Transform& LayerRenderArea::getTransform() const {
+ return mTransform;
+}
+
+Rect LayerRenderArea::getBounds() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState());
+}
+
+int LayerRenderArea::getHeight() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
+}
+
+int LayerRenderArea::getWidth() const {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+}
+
+bool LayerRenderArea::isSecure() const {
+ return false;
+}
+
+bool LayerRenderArea::needsFiltering() const {
+ return mNeedsFiltering;
+}
+
+sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
+ return nullptr;
+}
+
+Rect LayerRenderArea::getSourceCrop() const {
+ if (mCrop.isEmpty()) {
+ return getBounds();
+ } else {
+ return mCrop;
+ }
+}
+
+void LayerRenderArea::render(std::function<void()> drawLayers) {
+ using namespace std::string_literals;
+
+ const Rect sourceCrop = getSourceCrop();
+ // no need to check rotation because there is none
+ mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+
+ if (!mChildrenOnly) {
+ mTransform = mLayer->getTransform().inverse();
+ drawLayers();
+ } else {
+ uint32_t w = static_cast<uint32_t>(getWidth());
+ uint32_t h = static_cast<uint32_t>(getHeight());
+ // In the "childrenOnly" case we reparent the children to a screenshot
+ // layer which has no properties set and which does not draw.
+ sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
+ {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+
+ ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
+ drawLayers();
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
new file mode 100644
index 0000000..81690b9
--- /dev/null
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+#include <utils/StrongPointer.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+class DisplayDevice;
+class Layer;
+class SurfaceFlinger;
+
+class LayerRenderArea : public RenderArea {
+public:
+ LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& displayViewport);
+
+ const ui::Transform& getTransform() const override;
+ Rect getBounds() const override;
+ int getHeight() const override;
+ int getWidth() const override;
+ bool isSecure() const override;
+ bool needsFiltering() const override;
+ sp<const DisplayDevice> getDisplayDevice() const override;
+ Rect getSourceCrop() const override;
+
+ void render(std::function<void()> drawLayers) override;
+
+private:
+ const sp<Layer> mLayer;
+ const Rect mCrop;
+
+ ui::Transform mTransform;
+ bool mNeedsFiltering = false;
+
+ SurfaceFlinger& mFlinger;
+ const bool mChildrenOnly;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index f2bc65d..c5a4689 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -3,8 +3,8 @@
alecmouri@google.com
chaviw@google.com
lpy@google.com
-marissaw@google.com
racarr@google.com
steventhomas@google.com
stoza@google.com
vhau@google.com
+vishnun@google.com
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 2e7fbc1..d8477e7 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -106,41 +106,71 @@
drawSegment(Segment::Buttom, left, color, buffer, pixels);
}
-sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
- const half4& color) {
- if (number < 0 || number > 1000) return nullptr;
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
+ int number, const half4& color, bool showSpinner) {
+ if (number < 0 || number > 1000) return {};
const auto hundreds = number / 100;
const auto tens = (number / 10) % 10;
const auto ones = number % 10;
- sp<GraphicBuffer> buffer =
- new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
- GRALLOC_USAGE_HW_TEXTURE,
- "RefreshRateOverlayBuffer");
- uint8_t* pixels;
- buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
- // Clear buffer content
- drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
- int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, buffer, pixels);
+ std::vector<sp<GraphicBuffer>> buffers;
+ const auto loopCount = showSpinner ? 6 : 1;
+ for (int i = 0; i < loopCount; i++) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "RefreshRateOverlayBuffer");
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+ // Clear buffer content
+ drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+ int left = 0;
+ if (hundreds != 0) {
+ drawDigit(hundreds, left, color, buffer, pixels);
+ }
left += DIGIT_WIDTH + DIGIT_SPACE;
- }
- if (tens != 0) {
- drawDigit(tens, left, color, buffer, pixels);
+ if (tens != 0) {
+ drawDigit(tens, left, color, buffer, pixels);
+ }
left += DIGIT_WIDTH + DIGIT_SPACE;
- }
- drawDigit(ones, left, color, buffer, pixels);
- buffer->unlock();
- return buffer;
+ drawDigit(ones, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+
+ if (showSpinner) {
+ switch (i) {
+ case 0:
+ drawSegment(Segment::Upper, left, color, buffer, pixels);
+ break;
+ case 1:
+ drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ break;
+ case 2:
+ drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ break;
+ case 3:
+ drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ break;
+ case 4:
+ drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ break;
+ case 5:
+ drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ break;
+ }
+ }
+
+ buffer->unlock();
+ buffers.emplace_back(buffer);
+ }
+ return buffers;
}
-RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
- : mFlinger(flinger), mClient(new Client(&mFlinger)) {
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner)
+ : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) {
createLayer();
primeCache();
}
@@ -176,7 +206,7 @@
if (allRefreshRates.size() == 1) {
auto fps = allRefreshRates.begin()->second->getFps();
half4 color = {LOW_FPS_COLOR, ALPHA};
- mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
return;
}
@@ -196,30 +226,38 @@
color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
color.a = ALPHA;
- mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
}
}
-void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
- const auto display = mFlinger.getDefaultDisplayDeviceLocked();
- if (!display) {
- return;
- }
-
- const int32_t left = display->getWidth() / 32;
- const int32_t top = display->getHeight() / 32;
- const int32_t right = left + display->getWidth() / 8;
- const int32_t buttom = top + display->getHeight() / 32;
-
- auto buffer = mBufferCache[refreshRate.getFps()];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
-
- mLayer->setFrame(Rect(left, top, right, buttom));
+void RefreshRateOverlay::setViewport(ui::Size viewport) {
+ Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
+ frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
+ mLayer->setFrame(frame);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
-}; // namespace android
+void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
+ mCurrentFps = refreshRate.getFps();
+ auto buffer = mBufferCache[*mCurrentFps][mFrame];
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+
+ mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+void RefreshRateOverlay::onInvalidate() {
+ if (!mCurrentFps.has_value()) return;
+
+ const auto& buffers = mBufferCache[*mCurrentFps];
+ mFrame = (mFrame + 1) % buffers.size();
+ auto buffer = buffers[mFrame];
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+
+ mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 6d34df2..1a8938f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -13,24 +13,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#pragma once
-#include "SurfaceFlinger.h"
+#include <unordered_map>
+
+#include <math/vec4.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+#include <utils/StrongPointer.h>
+
+#include "Scheduler/RefreshRateConfigs.h"
namespace android {
+class Client;
+class GraphicBuffer;
+class IBinder;
+class IGraphicBufferProducer;
+class Layer;
+class SurfaceFlinger;
+
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
class RefreshRateOverlay {
public:
- RefreshRateOverlay(SurfaceFlinger& flinger);
+ RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
- void changeRefreshRate(const RefreshRate& refreshRate);
+ void setViewport(ui::Size);
+ void changeRefreshRate(const RefreshRate&);
+ void onInvalidate();
private:
class SevenSegmentDrawer {
public:
- static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+ static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
+ bool showSpinner);
static uint32_t getHeight() { return BUFFER_HEIGHT; }
static uint32_t getWidth() { return BUFFER_WIDTH; }
@@ -49,23 +67,26 @@
static constexpr uint32_t DIGIT_SPACE = 16;
static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
static constexpr uint32_t BUFFER_WIDTH =
- 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+ 4 * DIGIT_WIDTH + 3 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit|Space|Spinner
};
bool createLayer();
void primeCache();
SurfaceFlinger& mFlinger;
- sp<Client> mClient;
+ const sp<Client> mClient;
sp<Layer> mLayer;
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
-
+ std::unordered_map<int, std::vector<sp<GraphicBuffer>>> mBufferCache;
+ std::optional<int> mCurrentFps;
+ int mFrame = 0;
static constexpr float ALPHA = 0.8f;
const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+
+ const bool mShowSpinner;
};
-}; // namespace android
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 27353d8..899d1fa 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -35,7 +35,9 @@
#include <string>
#include "DisplayDevice.h"
+#include "DisplayRenderArea.h"
#include "Layer.h"
+#include "Promise.h"
#include "Scheduler/DispSync.h"
#include "SurfaceFlinger.h"
@@ -336,8 +338,20 @@
return;
}
- const auto device = mFlinger.getDefaultDisplayDevice();
- const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
+ wp<const DisplayDevice> displayWeak;
+
+ ui::LayerStack layerStack;
+ ui::Transform::RotationFlags orientation;
+ ui::Size displaySize;
+
+ {
+ // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
+ const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice();
+ displayWeak = display;
+ layerStack = display->getLayerStack();
+ orientation = ui::Transform::toRotationFlags(display->getOrientation());
+ displaySize = display->getSize();
+ }
std::vector<RegionSamplingThread::Descriptor> descriptors;
Region sampleRegion;
@@ -346,20 +360,18 @@
descriptors.emplace_back(descriptor);
}
- const Rect sampledArea = sampleRegion.bounds();
-
auto dx = 0;
auto dy = 0;
switch (orientation) {
case ui::Transform::ROT_90:
- dx = device->getWidth();
+ dx = displaySize.getWidth();
break;
case ui::Transform::ROT_180:
- dx = device->getWidth();
- dy = device->getHeight();
+ dx = displaySize.getWidth();
+ dy = displaySize.getHeight();
break;
case ui::Transform::ROT_270:
- dy = device->getHeight();
+ dy = displaySize.getHeight();
break;
default:
break;
@@ -368,8 +380,14 @@
ui::Transform t(orientation);
auto screencapRegion = t.transform(sampleRegion);
screencapRegion = screencapRegion.translate(dx, dy);
- DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
- sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
+
+ const Rect sampledBounds = sampleRegion.bounds();
+
+ SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(),
+ sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
+ orientation);
+ });
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
@@ -393,9 +411,9 @@
constexpr bool roundOutwards = true;
Rect transformed = transform.transform(bounds, roundOutwards);
- // If this layer doesn't intersect with the larger sampledArea, skip capturing it
+ // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
Rect ignore;
- if (!transformed.intersect(sampledArea, &ignore)) return;
+ if (!transformed.intersect(sampledBounds, &ignore)) return;
// If the layer doesn't intersect a sampling area, skip capturing it
bool intersectsAnyArea = false;
@@ -411,22 +429,22 @@
bounds.top, bounds.right, bounds.bottom);
visitor(layer);
};
- mFlinger.traverseLayersInDisplay(device, filterVisitor);
+ mFlinger.traverseLayersInLayerStack(layerStack, filterVisitor);
};
sp<GraphicBuffer> buffer = nullptr;
- if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
- mCachedBuffer->getHeight() == sampledArea.getHeight()) {
+ if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() &&
+ mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
buffer = mCachedBuffer;
} else {
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
+ buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
}
bool ignored;
- mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
- true /* regionSampling */, ignored);
+ mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ false /* identityTransform */, true /* regionSampling */, ignored);
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
@@ -437,7 +455,7 @@
ALOGV("Sampling %zu descriptors", activeDescriptors.size());
std::vector<float> lumas =
- sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation);
+ sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation);
if (lumas.size() != activeDescriptors.size()) {
ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
activeDescriptors.size());
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 6b0455a..a6246d9 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -23,11 +23,9 @@
static float getCaptureFillValue(CaptureFill captureFill);
- RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace, const Rect& displayViewport,
- RotationFlags rotation = ui::Transform::ROT_0)
- : mReqWidth(reqWidth),
- mReqHeight(reqHeight),
+ RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
+ const Rect& displayViewport, RotationFlags rotation = ui::Transform::ROT_0)
+ : mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation),
@@ -70,8 +68,8 @@
RotationFlags getRotationFlags() const { return mRotationFlags; }
// Returns the size of the physical render area.
- int getReqWidth() const { return static_cast<int>(mReqWidth); }
- int getReqHeight() const { return static_cast<int>(mReqHeight); }
+ int getReqWidth() const { return mReqSize.width; }
+ int getReqHeight() const { return mReqSize.height; }
// Returns the composition data space of the render area.
ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
@@ -86,8 +84,7 @@
const Rect& getDisplayViewport() const { return mDisplayViewport; }
private:
- const uint32_t mReqWidth;
- const uint32_t mReqHeight;
+ const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 5dedb6a..cee36a1 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -152,16 +152,9 @@
mEventThread->requestNextVsync(this);
}
-void EventThreadConnection::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
- ATRACE_NAME("enableConfigEvents");
- mConfigChanged = configChangeFlag;
-
- // In principle it's possible for rapidly toggling config events to drop an
- // event here, but it's unlikely in practice.
- if (configChangeFlag == ISurfaceComposer::eConfigChangedDispatch) {
- mForcedConfigChangeDispatch = true;
- mEventThread->requestLatestConfig();
- }
+void EventThreadConnection::requestLatestConfig() {
+ ATRACE_NAME("requestLatestConfig");
+ mEventThread->requestLatestConfig(this);
}
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
@@ -276,8 +269,12 @@
}
}
-void EventThread::requestLatestConfig() {
+void EventThread::requestLatestConfig(const sp<EventThreadConnection>& connection) {
std::lock_guard<std::mutex> lock(mMutex);
+ if (connection->mForcedConfigChangeDispatch) {
+ return;
+ }
+ connection->mForcedConfigChangeDispatch = true;
auto pendingConfigChange =
std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents),
[&](const DisplayEventReceiver::Event& event) {
@@ -384,6 +381,10 @@
vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
if (event && shouldConsumeEvent(*event, connection)) {
+ if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
+ connection->mForcedConfigChangeDispatch) {
+ connection->mForcedConfigChangeDispatch = false;
+ }
consumers.push_back(connection);
}
@@ -459,8 +460,8 @@
return true;
case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
- const bool forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false);
- return forcedDispatch ||
+ const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch;
+ return oneTimeDispatch ||
connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9e7086e..64acbd7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,19 +81,19 @@
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
status_t setVsyncRate(uint32_t rate) override;
void requestNextVsync() override; // asynchronous
- void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override;
+ void requestLatestConfig() override; // asynchronous
// Called in response to requestNextVsync.
const ResyncCallback resyncCallback;
VSyncRequest vsyncRequest = VSyncRequest::None;
- std::atomic<ISurfaceComposer::ConfigChanged> mConfigChanged =
+ ISurfaceComposer::ConfigChanged mConfigChanged =
ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
// Store whether we need to force dispatching a config change separately -
// if mConfigChanged ever changes before the config change is dispatched
// then we still need to propagate an initial config to the app if we
// haven't already.
- std::atomic<bool> mForcedConfigChangeDispatch = false;
+ bool mForcedConfigChangeDispatch = false;
private:
virtual void onFirstRef();
@@ -129,11 +129,10 @@
virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
-
// Dispatches the most recent configuration
// Usage of this method assumes that only the primary internal display
// supports multiple display configurations.
- virtual void requestLatestConfig() = 0;
+ virtual void requestLatestConfig(const sp<EventThreadConnection>& connection) = 0;
// Retrieves the number of event connections tracked by this EventThread.
virtual size_t getEventThreadConnectionCount() = 0;
@@ -154,7 +153,7 @@
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
void requestNextVsync(const sp<EventThreadConnection>& connection) override;
- void requestLatestConfig() override;
+ void requestLatestConfig(const sp<EventThreadConnection>& connection) override;
// called before the screen is turned off from main thread
void onScreenReleased() override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8958d9a..36433c2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -79,7 +80,8 @@
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+ LayerUpdateType /*updateType*/) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -108,6 +110,8 @@
auto layer = weakLayer.promote();
// Only use the layer if the reference still exists.
if (layer || CC_UNLIKELY(mTraceEnabled)) {
+ const auto layerFocused =
+ Layer::isLayerFocusedBasedOnPriority(layer->getFrameRateSelectionPriority());
// Check if frame rate was set on layer.
const auto frameRate = layer->getFrameRateForLayerTree();
if (frameRate.rate > 0.f) {
@@ -121,11 +125,12 @@
return LayerVoteType::NoVote;
}
}();
- summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f});
+ summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f,
+ layerFocused});
} else if (recent) {
summary.push_back({layer->getName(), LayerVoteType::Heuristic,
info->getRefreshRate(now),
- /* weight */ 1.0f});
+ /* weight */ 1.0f, layerFocused});
}
if (CC_UNLIKELY(mTraceEnabled)) {
@@ -179,4 +184,11 @@
mActiveLayersEnd = 0;
}
+
+std::string LayerHistory::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index a1ae35c..128699b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -22,6 +22,7 @@
#include <memory>
#include <mutex>
+#include <string>
#include <utility>
#include <vector>
@@ -52,8 +53,18 @@
// Sets the display size. Client is responsible for synchronization.
virtual void setDisplayArea(uint32_t displayArea) = 0;
+ // Sets whether a config change is pending to be applied
+ virtual void setConfigChangePending(bool pending) = 0;
+
+ // Represents which layer activity is recorded
+ enum class LayerUpdateType {
+ Buffer, // a new buffer queued
+ AnimationTX, // a new transaction with eAnimation flag set
+ SetFrameRate, // setFrameRate API was called
+ };
+
// Marks the layer as active, and records the given state to its history.
- virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
+ virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
@@ -61,6 +72,7 @@
virtual Summary summarize(nsecs_t now) = 0;
virtual void clear() = 0;
+ virtual std::string dump() const = 0;
};
namespace impl {
@@ -78,13 +90,16 @@
void setDisplayArea(uint32_t /*displayArea*/) override {}
+ void setConfigChangePending(bool /*pending*/) override {}
+
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+ void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
void clear() override;
+ std::string dump() const override;
private:
friend class android::scheduler::LayerHistoryTest;
@@ -124,7 +139,7 @@
class LayerHistoryV2 : public android::scheduler::LayerHistory {
public:
- LayerHistoryV2();
+ LayerHistoryV2(const scheduler::RefreshRateConfigs&);
virtual ~LayerHistoryV2();
// Layers are unregistered when the weak reference expires.
@@ -134,13 +149,16 @@
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
+ void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
+
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+ void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
void clear() override;
+ std::string dump() const override;
private:
friend android::scheduler::LayerHistoryTestV2;
@@ -178,6 +196,9 @@
// Whether to use priority sent from WindowManager to determine the relevancy of the layer.
const bool mUseFrameRatePriority;
+
+ // Whether a config change is in progress or not
+ std::atomic<bool> mConfigChangePending = false;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index b067466..37e67e1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -31,9 +32,8 @@
#include <utility>
#include "../Layer.h"
-#include "SchedulerUtils.h"
-
#include "LayerInfoV2.h"
+#include "SchedulerUtils.h"
namespace android::scheduler::impl {
@@ -58,44 +58,44 @@
return atoi(value);
}
-void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) {
+void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
+ int fps) {
const auto layer = weak.promote();
if (!layer) return;
- const auto& name = layer->getName();
- const auto noVoteTag = "LFPS NoVote " + name;
- const auto heuristicVoteTag = "LFPS Heuristic " + name;
- const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
- const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
- const auto minVoteTag = "LFPS Min " + name;
- const auto maxVoteTag = "LFPS Max " + name;
+ const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+ ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+ };
- ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
- ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
- ATRACE_INT(explicitDefaultVoteTag.c_str(),
- type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
- ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
- type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
- ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
- ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
+ traceType(LayerHistory::LayerVoteType::NoVote, 1);
+ traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+ traceType(LayerHistory::LayerVoteType::Min, 1);
+ traceType(LayerHistory::LayerVoteType::Max, 1);
- ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
}
} // namespace
-LayerHistoryV2::LayerHistoryV2()
- : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+ LayerInfoV2::setTraceEnabled(mTraceEnabled);
+ LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
+}
+
LayerHistoryV2::~LayerHistoryV2() = default;
void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
LayerVoteType type) {
const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
- auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type);
+ auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+ LayerUpdateType updateType) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -103,7 +103,7 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now);
+ info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -125,9 +125,10 @@
continue;
}
- // TODO(b/144307188): This needs to be plugged into layer summary as
- // an additional parameter.
- ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
+ const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+ const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
+ ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
+ frameRateSelectionPriority, layerFocused ? "" : "not");
const auto [type, refreshRate] = info->getRefreshRate(now);
// Skip NoVote layer as those don't have any requirements
@@ -143,10 +144,10 @@
const float layerArea = transformed.getWidth() * transformed.getHeight();
float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back({strong->getName(), type, refreshRate, weight});
+ summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, type, static_cast<int>(std::round(refreshRate)));
+ trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
}
}
@@ -174,8 +175,10 @@
return LayerVoteType::NoVote;
}
}();
- if (layer->isVisible() && (frameRate.rate > 0 || voteType == LayerVoteType::NoVote)) {
- info->setLayerVote(voteType, frameRate.rate);
+
+ if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+ const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+ info->setLayerVote(type, frameRate.rate);
} else {
info->resetLayerVote();
}
@@ -183,10 +186,10 @@
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+ trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
}
- info->clearHistory();
+ info->onLayerInactive(now);
std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
@@ -207,7 +210,14 @@
std::lock_guard lock(mLock);
for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory();
+ info->clearHistory(systemTime());
}
}
+
+std::string LayerHistoryV2::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index cb81ca2..820624b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -98,9 +98,9 @@
return false;
}
- // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
if (mElements.size() < HISTORY_SIZE &&
- mElements.back() - mElements.front() < HISTORY_TIME.count()) {
+ mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
return false;
}
@@ -124,7 +124,7 @@
private:
std::deque<nsecs_t> mElements;
- static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
};
friend class LayerHistoryTest;
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index bf1fb88..44f20d0 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -15,33 +15,51 @@
*/
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "LayerInfoV2.h"
#include <algorithm>
#include <utility>
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
#undef LOG_TAG
#define LOG_TAG "LayerInfoV2"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
namespace android::scheduler {
-LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
- : mHighRefreshRatePeriod(highRefreshRatePeriod),
- mDefaultVote(defaultVote),
- mLayerVote({defaultVote, 0.0f}) {}
+const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
+bool LayerInfoV2::sTraceEnabled = false;
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
+ LayerHistory::LayerVoteType defaultVote)
+ : mName(name),
+ mHighRefreshRatePeriod(highRefreshRatePeriod),
+ mDefaultVote(defaultVote),
+ mLayerVote({defaultVote, 0.0f}),
+ mRefreshRateHistory(name) {}
+
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
+ LayerUpdateType updateType, bool pendingConfigChange) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
-
- FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
-
- mFrameTimes.push_back(frameTime);
- if (mFrameTimes.size() > HISTORY_SIZE) {
- mFrameTimes.pop_front();
+ switch (updateType) {
+ case LayerUpdateType::AnimationTX:
+ mLastAnimationTime = std::max(lastPresentTime, now);
+ break;
+ case LayerUpdateType::SetFrameRate:
+ case LayerUpdateType::Buffer:
+ FrameTimeData frameTime = {.presetTime = lastPresentTime,
+ .queueTime = mLastUpdatedTime,
+ .pendingConfigChange = pendingConfigChange};
+ mFrameTimes.push_back(frameTime);
+ if (mFrameTimes.size() > HISTORY_SIZE) {
+ mFrameTimes.pop_front();
+ }
+ break;
}
}
@@ -52,21 +70,14 @@
}
bool LayerInfoV2::isFrequent(nsecs_t now) const {
- // Find the first valid frame time
- auto it = mFrameTimes.begin();
- for (; it != mFrameTimes.end(); ++it) {
- if (isFrameTimeValid(*it)) {
- break;
- }
- }
-
// If we know nothing about this layer we consider it as frequent as it might be the start
// of an animation.
- if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
+ if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
return true;
}
// Find the first active frame
+ auto it = mFrameTimes.begin();
for (; it != mFrameTimes.end(); ++it) {
if (it->queueTime >= getActiveLayerThreshold(now)) {
break;
@@ -83,80 +94,204 @@
return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
}
+bool LayerInfoV2::isAnimating(nsecs_t now) const {
+ return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
- // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
if (mFrameTimes.size() < 2) {
+ ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
return false;
}
if (!isFrameTimeValid(mFrameTimes.front())) {
+ ALOGV("stale frames still captured");
return false;
}
- if (mFrameTimes.size() < HISTORY_SIZE &&
- mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
+ const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+ if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+ ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+ totalDuration / 1e9f);
return false;
}
return true;
}
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
- static constexpr float MARGIN = 1.0f; // 1Hz
-
- if (!hasEnoughDataForHeuristic()) {
- ALOGV("Not enough data");
- return std::nullopt;
- }
-
- // Calculate the refresh rate by finding the average delta between frames
+std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
nsecs_t totalPresentTimeDeltas = 0;
+ nsecs_t totalQueueTimeDeltas = 0;
+ bool missingPresentTime = false;
+ int numFrames = 0;
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- // If there are no presentation timestamp provided we can't calculate the refresh rate
- if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+ // Ignore frames captured during a config change
+ if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
return std::nullopt;
}
+ totalQueueTimeDeltas +=
+ std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+ numFrames++;
+
+ if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
+ missingPresentTime = true;
+ // If there are no presentation timestamps and we haven't calculated
+ // one in the past then we can't calculate the refresh rate
+ if (mLastRefreshRate.reported == 0) {
+ return std::nullopt;
+ }
+ continue;
+ }
+
totalPresentTimeDeltas +=
std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
}
- const float averageFrameTime =
- static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
- // Now once we calculated the refresh rate we need to make sure that all the frames we captured
- // are evenly distributed and we don't calculate the average across some burst of frames.
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- const nsecs_t presentTimeDeltas =
- std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
- if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
- return std::nullopt;
+ // Calculate the average frame time based on presentation timestamps. If those
+ // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+ // we calculated a refresh rate based on presentation timestamps in the past. The reason
+ // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+ // when implementing render ahead for specific refresh rates. When hwui no longer provides
+ // presentation timestamps we look at the queue time to see if the current refresh rate still
+ // matches the content.
+
+ const auto averageFrameTime =
+ static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
+ numFrames;
+ return static_cast<nsecs_t>(averageFrameTime);
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
+ static constexpr float MARGIN = 1.0f; // 1Hz
+ if (!hasEnoughDataForHeuristic()) {
+ ALOGV("Not enough data");
+ return std::nullopt;
+ }
+
+ const auto averageFrameTime = calculateAverageFrameTime();
+ if (averageFrameTime.has_value()) {
+ const auto refreshRate = 1e9f / *averageFrameTime;
+ const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+ if (refreshRateConsistent) {
+ const auto knownRefreshRate =
+ sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
+
+ // To avoid oscillation, use the last calculated refresh rate if it is
+ // close enough
+ if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
+ mLastRefreshRate.reported != knownRefreshRate) {
+ mLastRefreshRate.calculated = refreshRate;
+ mLastRefreshRate.reported = knownRefreshRate;
+ }
+
+ ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
+ refreshRate, mLastRefreshRate.reported);
+ } else {
+ ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
+ refreshRate, mLastRefreshRate.reported);
}
}
- const auto refreshRate = 1e9f / averageFrameTime;
- if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
- mLastReportedRefreshRate = refreshRate;
- }
-
- ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
- return mLastReportedRefreshRate;
+ return mLastRefreshRate.reported == 0 ? std::nullopt
+ : std::make_optional(mLastRefreshRate.reported);
}
std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+ ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
return {mLayerVote.type, mLayerVote.fps};
}
+ if (isAnimating(now)) {
+ ALOGV("%s is animating", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
+ return {LayerHistory::LayerVoteType::Max, 0};
+ }
+
if (!isFrequent(now)) {
+ ALOGV("%s is infrequent", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
return {LayerHistory::LayerVoteType::Min, 0};
}
- auto refreshRate = calculateRefreshRateIfPossible();
+ // If the layer was previously tagged as animating or infrequent, we clear
+ // the history as it is likely the layer just changed its behavior
+ // and we should not look at stale data
+ if (mLastRefreshRate.animatingOrInfrequent) {
+ clearHistory(now);
+ }
+
+ auto refreshRate = calculateRefreshRateIfPossible(now);
if (refreshRate.has_value()) {
+ ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
}
+ ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
return {LayerHistory::LayerVoteType::Max, 0};
}
+const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+ if (mTraceTags.count(type) == 0) {
+ const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+ mTraceTags.emplace(type, tag);
+ }
+
+ return mTraceTags.at(type).c_str();
+}
+
+LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
+LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
+ const std::string prefix = "LFPS ";
+ const std::string suffix = "Heuristic ";
+ return {.min = prefix + mName + suffix + "min",
+ .max = prefix + mName + suffix + "max",
+ .consistent = prefix + mName + suffix + "consistent",
+ .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfoV2::RefreshRateHistory::clear() {
+ mRefreshRates.clear();
+}
+
+bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
+ mRefreshRates.push_back({refreshRate, now});
+ while (mRefreshRates.size() >= HISTORY_SIZE ||
+ now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+ mRefreshRates.pop_front();
+ }
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
+ }
+
+ return isConsistent();
+}
+
+bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
+ if (mRefreshRates.empty()) return true;
+
+ const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
+ ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
+ ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+ }
+
+ return consistent;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index ad91f18..33dc66f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -43,6 +43,8 @@
// Stores history of present times and refresh rates for a layer.
class LayerInfoV2 {
+ using LayerUpdateType = LayerHistory::LayerUpdateType;
+
// Layer is considered frequent if the earliest value in the window of most recent present times
// is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
// favor of a low refresh rate.
@@ -54,7 +56,14 @@
friend class LayerHistoryTestV2;
public:
- LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote);
+ static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+ static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+ sRefreshRateConfigs = &refreshRateConfigs;
+ }
+
+ LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
+ LayerHistory::LayerVoteType defaultVote);
LayerInfoV2(const LayerInfo&) = delete;
LayerInfoV2& operator=(const LayerInfoV2&) = delete;
@@ -62,7 +71,8 @@
// Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+ bool pendingConfigChange);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -82,12 +92,23 @@
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
- void clearHistory() {
+ // Returns a C string for tracing a vote
+ const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
+ void onLayerInactive(nsecs_t now) {
// Mark mFrameTimeValidSince to now to ignore all previous frame times.
// We are not deleting the old frame to keep track of whether we should treat the first
// buffer as Max as we don't know anything about this layer or Min as this layer is
// posting infrequent updates.
- mFrameTimeValidSince = std::chrono::steady_clock::now();
+ const auto timePoint = std::chrono::nanoseconds(now);
+ mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
+ mLastRefreshRate = {};
+ mRefreshRateHistory.clear();
+ }
+
+ void clearHistory(nsecs_t now) {
+ onLayerInactive(now);
+ mFrameTimes.clear();
}
private:
@@ -95,32 +116,105 @@
struct FrameTimeData {
nsecs_t presetTime; // desiredPresentTime, if provided
nsecs_t queueTime; // buffer queue time
+ bool pendingConfigChange;
+ };
+
+ // Holds information about the calculated and reported refresh rate
+ struct RefreshRateHeuristicData {
+ // Rate calculated on the layer
+ float calculated = 0.0f;
+ // Last reported rate for LayerInfoV2::getRefreshRate()
+ float reported = 0.0f;
+ // Whether the last reported rate for LayerInfoV2::getRefreshRate()
+ // was due to animation or infrequent updates
+ bool animatingOrInfrequent = false;
+ };
+
+ // Holds information about the layer vote
+ struct LayerVote {
+ LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+ float fps = 0.0f;
+ };
+
+ // Class to store past calculated refresh rate and determine whether
+ // the refresh rate calculated is consistent with past values
+ class RefreshRateHistory {
+ public:
+ static constexpr auto HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+ RefreshRateHistory(const std::string& name) : mName(name) {}
+
+ // Clears History
+ void clear();
+
+ // Adds a new refresh rate and returns true if it is consistent
+ bool add(float refreshRate, nsecs_t now);
+
+ private:
+ friend class LayerHistoryTestV2;
+
+ // Holds the refresh rate when it was calculated
+ struct RefreshRateData {
+ float refreshRate = 0.0f;
+ nsecs_t timestamp = 0;
+
+ bool operator<(const RefreshRateData& other) const {
+ return refreshRate < other.refreshRate;
+ }
+ };
+
+ // Holds tracing strings
+ struct HeuristicTraceTagData {
+ std::string min;
+ std::string max;
+ std::string consistent;
+ std::string average;
+ };
+
+ bool isConsistent() const;
+ HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+ const std::string mName;
+ mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+ std::deque<RefreshRateData> mRefreshRates;
+ static constexpr float MARGIN_FPS = 1.0;
};
bool isFrequent(nsecs_t now) const;
+ bool isAnimating(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
- std::optional<float> calculateRefreshRateIfPossible();
+ std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+ std::optional<nsecs_t> calculateAverageFrameTime() const;
bool isFrameTimeValid(const FrameTimeData&) const;
+ const std::string mName;
+
// Used for sanitizing the heuristic data
const nsecs_t mHighRefreshRatePeriod;
LayerHistory::LayerVoteType mDefaultVote;
+ LayerVote mLayerVote;
+
nsecs_t mLastUpdatedTime = 0;
- float mLastReportedRefreshRate = 0.0f;
+ nsecs_t mLastAnimationTime = 0;
- // Holds information about the layer vote
- struct {
- LayerHistory::LayerVoteType type;
- float fps;
- } mLayerVote;
+ RefreshRateHeuristicData mLastRefreshRate;
std::deque<FrameTimeData> mFrameTimes;
std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
std::chrono::steady_clock::now();
- static constexpr size_t HISTORY_SIZE = 90;
- static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+ static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+ RefreshRateHistory mRefreshRateHistory;
+
+ mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+ // Shared for all LayerInfo instances
+ static const RefreshRateConfigs* sRefreshRateConfigs;
+ static bool sTraceEnabled;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 8d958df..8661b6e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -31,6 +31,23 @@
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
+ switch (vote) {
+ case LayerVoteType::NoVote:
+ return "NoVote";
+ case LayerVoteType::Min:
+ return "Min";
+ case LayerVoteType::Max:
+ return "Max";
+ case LayerVoteType::Heuristic:
+ return "Heuristic";
+ case LayerVoteType::ExplicitDefault:
+ return "ExplicitDefault";
+ case LayerVoteType::ExplicitExactOrMultiple:
+ return "ExplicitExactOrMultiple";
+ }
+}
+
const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
const std::vector<LayerRequirement>& layers) const {
std::lock_guard lock(mLock);
@@ -98,12 +115,24 @@
}
const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
- const std::vector<LayerRequirement>& layers, bool touchActive, bool idle,
- bool* touchConsidered) const {
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getRefreshRateForContent %zu layers", layers.size());
- *touchConsidered = false;
+ if (outSignalsConsidered) *outSignalsConsidered = {};
+ const auto setTouchConsidered = [&] {
+ if (outSignalsConsidered) {
+ outSignalsConsidered->touch = true;
+ }
+ };
+
+ const auto setIdleConsidered = [&] {
+ if (outSignalsConsidered) {
+ outSignalsConsidered->idle = true;
+ }
+ };
+
std::lock_guard lock(mLock);
int noVoteLayers = 0;
@@ -128,28 +157,40 @@
}
}
+ const bool hasExplicitVoteLayers =
+ explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0;
+
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
- if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) {
- *touchConsidered = true;
+ if (globalSignals.touch && !hasExplicitVoteLayers) {
+ ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
+ setTouchConsidered();
return getMaxRefreshRateByPolicyLocked();
}
- if (!touchActive && idle) {
+ // If the primary range consists of a single refresh rate then we can only
+ // move out the of range if layers explicitly request a different refresh
+ // rate.
+ const Policy* policy = getCurrentPolicyLocked();
+ const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
+
+ if (!globalSignals.touch && globalSignals.idle &&
+ !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+ ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
+ setIdleConsidered();
return getMinRefreshRateByPolicyLocked();
}
- if (layers.empty()) {
- return getCurrentRefreshRateByPolicyLocked();
+ if (layers.empty() || noVoteLayers == layers.size()) {
+ return getMaxRefreshRateByPolicyLocked();
}
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
+ ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
return getMinRefreshRateByPolicyLocked();
}
- const Policy* policy = getCurrentPolicyLocked();
-
// Find the best refresh rate based on score
std::vector<std::pair<const RefreshRate*, float>> scores;
scores.reserve(mAppRequestRefreshRates.size());
@@ -159,7 +200,8 @@
}
for (const auto& layer : layers) {
- ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
+ ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
+ layerVoteTypeString(layer.vote).c_str(), layer.weight);
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
continue;
}
@@ -169,10 +211,10 @@
for (auto i = 0u; i < scores.size(); i++) {
bool inPrimaryRange =
scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
- if (!inPrimaryRange && layer.vote != LayerVoteType::ExplicitDefault &&
- layer.vote != LayerVoteType::ExplicitExactOrMultiple) {
- // Only layers with explicit frame rate settings are allowed to score refresh rates
- // outside the primary range.
+ if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+ !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
+ // Only focused layers with ExplicitDefault frame rate settings are allowed to score
+ // refresh rates outside the primary range.
continue;
}
@@ -213,7 +255,7 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
- const auto layerScore = [&]() {
+ const auto layerScore = [&] {
// Calculate how many display vsyncs we need to present a single frame for this
// layer
const auto [displayFramesQuot, displayFramesRem] =
@@ -243,9 +285,9 @@
return 1.0f / iter;
}();
- ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f",
- layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
- layerScore);
+ ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
+ layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
+ scores[i].first->name.c_str(), layerScore);
scores[i].second += weight * layerScore;
continue;
}
@@ -259,14 +301,29 @@
? getBestRefreshRate(scores.rbegin(), scores.rend())
: getBestRefreshRate(scores.begin(), scores.end());
+ if (primaryRangeIsSingleRate) {
+ // If we never scored any layers, then choose the rate from the primary
+ // range instead of picking a random score from the app range.
+ if (std::all_of(scores.begin(), scores.end(),
+ [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+ ALOGV("layers not scored - choose %s",
+ getMaxRefreshRateByPolicyLocked().getName().c_str());
+ return getMaxRefreshRateByPolicyLocked();
+ } else {
+ return *bestRefreshRate;
+ }
+ }
+
// Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
// interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
- if (touchActive && explicitDefaultVoteLayers == 0 &&
+
+ if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
bestRefreshRate->fps < touchRefreshRate.fps) {
- *touchConsidered = true;
+ setTouchConsidered();
+ ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate;
}
@@ -340,7 +397,8 @@
RefreshRateConfigs::RefreshRateConfigs(
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId) {
+ HwcConfigIndexType currentConfigId)
+ : mKnownFrameRates(constructKnownFrameRates(configs)) {
LOG_ALWAYS_FATAL_IF(configs.empty());
LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
@@ -500,4 +558,64 @@
&mAppRequestRefreshRates);
}
+std::vector<float> RefreshRateConfigs::constructKnownFrameRates(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ std::vector<float> knownFrameRates = {24.0f, 30.0f, 45.0f, 60.0f, 72.0f};
+ knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+ // Add all supported refresh rates to the set
+ for (const auto& config : configs) {
+ const auto refreshRate = 1e9f / config->getVsyncPeriod();
+ knownFrameRates.emplace_back(refreshRate);
+ }
+
+ // Sort and remove duplicates
+ const auto frameRatesEqual = [](float a, float b) { return std::abs(a - b) <= 0.01f; };
+ std::sort(knownFrameRates.begin(), knownFrameRates.end());
+ knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+ frameRatesEqual),
+ knownFrameRates.end());
+ return knownFrameRates;
+}
+
+float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const {
+ if (frameRate <= *mKnownFrameRates.begin()) {
+ return *mKnownFrameRates.begin();
+ }
+
+ if (frameRate >= *std::prev(mKnownFrameRates.end())) {
+ return *std::prev(mKnownFrameRates.end());
+ }
+
+ auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate);
+
+ const auto distance1 = std::abs(frameRate - *lowerBound);
+ const auto distance2 = std::abs(frameRate - *std::prev(lowerBound));
+ return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
+}
+
+RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
+ std::lock_guard lock(mLock);
+ const auto& deviceMin = getMinRefreshRate();
+ const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
+ const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+
+ // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
+ // the min allowed refresh rate is higher than the device min, we do not want to enable the
+ // timer.
+ if (deviceMin < minByPolicy) {
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+ }
+ if (minByPolicy == maxByPolicy) {
+ // Do not sent the call to toggle off kernel idle timer if the device min and policy min and
+ // max are all the same. This saves us extra unnecessary calls to sysprop.
+ if (deviceMin == minByPolicy) {
+ return RefreshRateConfigs::KernelIdleTimerAction::NoChange;
+ }
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+ }
+ // Turn on the timer in all other cases.
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2657dee..eed8486 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -29,7 +29,6 @@
#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
-class RefreshRateConfigsTest;
using namespace std::chrono_literals;
@@ -83,11 +82,13 @@
return configId != other.configId || hwcConfig != other.hwcConfig;
}
+ bool operator<(const RefreshRate& other) const { return getFps() < other.getFps(); }
+
bool operator==(const RefreshRate& other) const { return !(*this != other); }
private:
friend RefreshRateConfigs;
- friend RefreshRateConfigsTest;
+ friend class RefreshRateConfigsTest;
// The tolerance within which we consider FPS approximately equals.
static constexpr float FPS_EPSILON = 0.001f;
@@ -194,15 +195,22 @@
// Captures the layer requirements for a refresh rate. This will be used to determine the
// display refresh rate.
struct LayerRequirement {
- std::string name; // Layer's name. Used for debugging purposes.
- LayerVoteType vote; // Layer vote type.
- float desiredRefreshRate; // Layer's desired refresh rate, if applicable.
- float weight; // Layer's weight in the range of [0, 1]. The higher the weight the more
- // impact this layer would have on choosing the refresh rate.
+ // Layer's name. Used for debugging purposes.
+ std::string name;
+ // Layer vote type.
+ LayerVoteType vote = LayerVoteType::NoVote;
+ // Layer's desired refresh rate, if applicable.
+ float desiredRefreshRate = 0.0f;
+ // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
+ // would have on choosing the refresh rate.
+ float weight = 0.0f;
+ // Whether layer is in focus or not based on WindowManager's state
+ bool focused = false;
bool operator==(const LayerRequirement& other) const {
return name == other.name && vote == other.vote &&
- desiredRefreshRate == other.desiredRefreshRate && weight == other.weight;
+ desiredRefreshRate == other.desiredRefreshRate && weight == other.weight &&
+ focused == other.focused;
}
bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
@@ -212,14 +220,22 @@
const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
EXCLUDES(mLock);
+ // Global state describing signals that affect refresh rate choice.
+ struct GlobalSignals {
+ // Whether the user touched the screen recently. Used to apply touch boost.
+ bool touch = false;
+ // True if the system hasn't seen any buffers posted to layers recently.
+ bool idle = false;
+ };
+
// Returns the refresh rate that fits best to the given layers.
// layers - The layer requirements to consider.
- // touchActive - Whether the user touched the screen recently. Used to apply touch boost.
- // idle - True if the system hasn't seen any buffers posted to layers recently.
- // touchConsidered - An output param that tells the caller whether the refresh rate was chosen
- // based on touch boost.
+ // globalSignals - global state of touch and idle
+ // outSignalsConsidered - An output param that tells the caller whether the refresh rate was
+ // chosen based on touch boost and/or idle timer.
const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- bool touchActive, bool idle, bool* touchConsidered) const
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
// Returns all the refresh rates supported by the device. This won't change at runtime.
@@ -255,11 +271,37 @@
// Stores the current configId the device operates at
void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
+ // Returns a string that represents the layer vote type
+ static std::string layerVoteTypeString(LayerVoteType vote);
+
+ // Returns a known frame rate that is the closest to frameRate
+ float findClosestKnownFrameRate(float frameRate) const;
+
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
+ // Returns whether switching configs (refresh rate or resolution) is possible.
+ // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
+ // differ in resolution.
+ bool canSwitch() const { return mRefreshRates.size() > 1; }
+
+ // Class to enumerate options around toggling the kernel timer on and off. We have an option
+ // for no change to avoid extra calls to kernel.
+ enum class KernelIdleTimerAction {
+ NoChange, // Do not change the idle timer.
+ TurnOff, // Turn off the idle timer.
+ TurnOn // Turn on the idle timer.
+ };
+ // Checks whether kernel idle timer should be active depending the policy decisions around
+ // refresh rates.
+ KernelIdleTimerAction getIdleTimerAction() const;
+
private:
+ friend class RefreshRateConfigsTest;
+
void constructAvailableRefreshRates() REQUIRES(mLock);
+ static std::vector<float> constructKnownFrameRates(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
void getSortedRefreshRateList(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
@@ -317,6 +359,10 @@
const RefreshRate* mMaxSupportedRefreshRate;
mutable std::mutex mLock;
+
+ // A sorted list of known frame rates that a Heuristic layer will choose
+ // from based on the closest value.
+ const std::vector<float> mKnownFrameRates;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d73fd8b..b53e2dc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -62,60 +62,77 @@
namespace android {
-std::unique_ptr<DispSync> createDispSync() {
- // TODO (140302863) remove this and use the vsync_reactor system.
- if (property_get_bool("debug.sf.vsync_reactor", true)) {
+namespace {
+
+std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
+ // TODO (144707443) tune Predictor tunables.
+ static constexpr int default_rate = 60;
+ static constexpr auto initial_period =
+ std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(1);
+ static constexpr size_t vsyncTimestampHistorySize = 20;
+ static constexpr size_t minimumSamplesForPrediction = 6;
+ static constexpr uint32_t discardOutlierPercent = 20;
+ return std::make_unique<
+ scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
+ initial_period)
+ .count(),
+ vsyncTimestampHistorySize, minimumSamplesForPrediction,
+ discardOutlierPercent);
+}
+
+std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(
+ const std::unique_ptr<scheduler::VSyncTracker>& vSyncTracker) {
+ if (!vSyncTracker) return {};
+
+ // TODO (144707443) tune Predictor tunables.
+ static constexpr auto vsyncMoveThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
+ static constexpr auto timerSlack = std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
+ return std::make_unique<
+ scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *vSyncTracker,
+ timerSlack.count(), vsyncMoveThreshold.count());
+}
+
+std::unique_ptr<DispSync> createDispSync(
+ const std::unique_ptr<scheduler::VSyncTracker>& vSyncTracker,
+ const std::unique_ptr<scheduler::VSyncDispatch>& vSyncDispatch, bool supportKernelTimer) {
+ if (vSyncTracker && vSyncDispatch) {
// TODO (144707443) tune Predictor tunables.
- static constexpr int default_rate = 60;
- static constexpr auto initial_period =
- std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(1);
- static constexpr size_t vsyncTimestampHistorySize = 20;
- static constexpr size_t minimumSamplesForPrediction = 6;
- static constexpr uint32_t discardOutlierPercent = 20;
- auto tracker = std::make_unique<
- scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
- initial_period)
- .count(),
- vsyncTimestampHistorySize, minimumSamplesForPrediction,
- discardOutlierPercent);
-
- static constexpr auto vsyncMoveThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
- static constexpr auto timerSlack =
- std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
- auto dispatch = std::make_unique<
- scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
- timerSlack.count(), vsyncMoveThreshold.count());
-
static constexpr size_t pendingFenceLimit = 20;
return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
- std::move(dispatch), std::move(tracker),
- pendingFenceLimit);
+ *vSyncDispatch, *vSyncTracker,
+ pendingFenceLimit, supportKernelTimer);
} else {
return std::make_unique<impl::DispSync>("SchedulerDispSync",
sysprop::running_without_sync_framework(true));
}
}
+const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
+ if (!useContentDetection) return "off";
+ return useContentDetectionV2 ? "V2" : "V1";
+}
+
+} // namespace
+
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
- const scheduler::RefreshRateConfigs& refreshRateConfig,
+ const scheduler::RefreshRateConfigs& refreshRateConfigs,
ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
bool useContentDetection)
- : mPrimaryDispSync(createDispSync()),
+ : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+ // TODO (140302863) remove this and use the vsync_reactor system.
+ mUseVsyncPredictor(property_get_bool("debug.sf.vsync_reactor", true)),
+ mVSyncTracker(mUseVsyncPredictor ? createVSyncTracker() : nullptr),
+ mVSyncDispatch(createVSyncDispatch(mVSyncTracker)),
+ mPrimaryDispSync(createDispSync(mVSyncTracker, mVSyncDispatch, mSupportKernelTimer)),
mEventControlThread(new impl::EventControlThread(std::move(function))),
- mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+ mLayerHistory(createLayerHistory(refreshRateConfigs, useContentDetectionV2)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(refreshRateConfig),
+ mRefreshRateConfigs(refreshRateConfigs),
mUseContentDetection(useContentDetection),
mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
-
const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
@@ -149,11 +166,14 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+ ISchedulerCallback& schedulerCallback,
+ std::unique_ptr<LayerHistory> layerHistory, bool useContentDetectionV2,
bool useContentDetection)
- : mPrimaryDispSync(std::move(primaryDispSync)),
+ : mSupportKernelTimer(false),
+ mUseVsyncPredictor(true),
+ mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
- mSupportKernelTimer(false),
+ mLayerHistory(std::move(layerHistory)),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs),
mUseContentDetection(useContentDetection),
@@ -166,6 +186,17 @@
mIdleTimer.reset();
}
+std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
+ const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
+ if (!configs.canSwitch()) return nullptr;
+
+ if (useContentDetectionV2) {
+ return std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
+ }
+
+ return std::make_unique<scheduler::impl::LayerHistory>();
+}
+
DispSync& Scheduler::getPrimaryDispSync() {
return *mPrimaryDispSync;
}
@@ -228,8 +259,36 @@
mConnections[handle].thread->onScreenReleased();
}
-void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ // Cache the last reported config for primary display.
+ mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+ onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
+}
+
+void Scheduler::dispatchCachedReportedConfig() {
+ const auto configId = *mFeatures.configId;
+ const auto vsyncPeriod =
+ mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
+
+ // If there is no change from cached config, there is no need to dispatch an event
+ if (configId == mFeatures.cachedConfigChangedParams->configId &&
+ vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) {
+ return;
+ }
+
+ mFeatures.cachedConfigChangedParams->configId = configId;
+ mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod;
+ onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle,
+ mFeatures.cachedConfigChangedParams->displayId,
+ mFeatures.cachedConfigChangedParams->configId,
+ mFeatures.cachedConfigChangedParams->vsyncPeriod);
+}
+
+void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
+ PhysicalDisplayId displayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
RETURN_IF_INVALID_HANDLE(handle);
mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
}
@@ -385,46 +444,48 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- // If the content detection feature is off, all layers are registered at NoVote. We still
- // keep the layer history, since we use it for other features (like Frame Rate API), so layers
- // still need to be registered.
- if (!mUseContentDetection) {
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
- mRefreshRateConfigs.getMaxRefreshRate().getFps(),
+ const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
+ const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
+
+ if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+ mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::NoVote);
- return;
- }
+ } else if (!mUseContentDetection) {
+ // If the content detection feature is off, all layers are registered at Max. We still keep
+ // the layer history, since we use it for other features (like Frame Rate API), so layers
+ // still need to be registered.
+ mLayerHistory->registerLayer(layer, minFps, maxFps,
+ scheduler::LayerHistory::LayerVoteType::Max);
+ } else if (!mUseContentDetectionV2) {
+ // In V1 of content detection, all layers are registered as Heuristic (unless it's
+ // wallpaper).
+ const auto highFps =
+ layer->getWindowType() == InputWindowInfo::Type::WALLPAPER ? minFps : maxFps;
- // In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
- if (!mUseContentDetectionV2) {
- const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
- const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? lowFps
- : mRefreshRateConfigs.getMaxRefreshRate().getFps();
-
- mLayerHistory->registerLayer(layer, lowFps, highFps,
+ mLayerHistory->registerLayer(layer, minFps, highFps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
- if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+ if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
- mRefreshRateConfigs.getMaxRefreshRate().getFps(),
+ mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::Min);
- } else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
- mRefreshRateConfigs.getMaxRefreshRate().getFps(),
- scheduler::LayerHistory::LayerVoteType::NoVote);
} else {
- mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().getFps(),
- mRefreshRateConfigs.getMaxRefreshRate().getFps(),
+ mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
}
}
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+ LayerHistory::LayerUpdateType updateType) {
if (mLayerHistory) {
- mLayerHistory->record(layer, presentTime, systemTime());
+ mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+ }
+}
+
+void Scheduler::setConfigChangePending(bool pending) {
+ if (mLayerHistory) {
+ mLayerHistory->setConfigChangePending(pending);
}
}
@@ -444,13 +505,21 @@
mFeatures.contentDetectionV1 =
!summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
- newConfigId = calculateRefreshRateConfigIndexType();
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+ newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
if (mFeatures.configId == newConfigId) {
+ // We don't need to change the config, but we might need to send an event
+ // about a config change, since it was suppressed due to a previous idleConsidered
+ if (!consideredSignals.idle) {
+ dispatchCachedReportedConfig();
+ }
return;
}
mFeatures.configId = newConfigId;
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
}
}
@@ -461,16 +530,7 @@
}
void Scheduler::notifyTouchEvent() {
- if (!mTouchTimer) return;
-
- // Touch event will boost the refresh rate to performance.
- // Clear Layer History to get fresh FPS detection.
- // NOTE: Instead of checking all the layers, we should be checking the layer
- // that is currently on top. b/142507166 will give us this capability.
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mLayerHistory) {
- // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate
-
+ if (mTouchTimer) {
mTouchTimer->reset();
if (mSupportKernelTimer && mIdleTimer) {
@@ -520,59 +580,72 @@
}
void Scheduler::idleTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+ handleTimerStateChanged(&mFeatures.idleTimer, state);
ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
- handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */);
+ // Touch event will boost the refresh rate to performance.
+ // Clear layer history to get fresh FPS detection.
+ // NOTE: Instead of checking all the layers, we should be checking the layer
+ // that is currently on top. b/142507166 will give us this capability.
+ if (handleTimerStateChanged(&mFeatures.touch, touch)) {
+ if (mLayerHistory) {
+ mLayerHistory->clear();
+ }
+ }
ATRACE_INT("TouchState", static_cast<int>(touch));
}
void Scheduler::displayPowerTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
- true /* eventOnContentDetection */);
+ handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
void Scheduler::dump(std::string& result) const {
using base::StringAppendF;
- const char* const states[] = {"off", "on"};
- StringAppendF(&result, "+ Idle timer: %s\n",
- mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Touch timer: %s\n",
- mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
- StringAppendF(&result, "+ Use content detection: %s\n\n",
- sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
+ mTouchTimer ? mTouchTimer->dump().c_str() : "off");
+ StringAppendF(&result, "+ Content detection: %s %s\n\n",
+ toContentDetectionString(mUseContentDetection, mUseContentDetectionV2),
+ mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
}
template <class T>
-void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
- ConfigEvent event = ConfigEvent::None;
+bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
HwcConfigIndexType newConfigId;
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
- return;
+ return false;
}
*currentState = newState;
- newConfigId = calculateRefreshRateConfigIndexType();
+ newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
if (mFeatures.configId == newConfigId) {
- return;
+ // We don't need to change the config, but we might need to send an event
+ // about a config change, since it was suppressed due to a previous idleConsidered
+ if (!consideredSignals.idle) {
+ dispatchCachedReportedConfig();
+ }
+ return consideredSignals.touch;
}
mFeatures.configId = newConfigId;
- if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) {
- event = ConfigEvent::Changed;
- }
}
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
+ return consideredSignals.touch;
}
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+ scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
ATRACE_CALL();
+ if (consideredSignals) *consideredSignals = {};
// If Display Power is not in normal operation we want to be in performance mode. When coming
// back to normal mode, a grace period is given with DisplayPowerTimer.
@@ -593,6 +666,7 @@
// If timer has expired as it means there is no new content on the screen.
if (idle) {
+ if (consideredSignals) consideredSignals->idle = true;
return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
}
@@ -607,18 +681,10 @@
.getConfigId();
}
- bool touchConsidered;
- const auto& ret = mRefreshRateConfigs
- .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle,
- &touchConsidered)
- .getConfigId();
- if (touchConsidered) {
- // Clear layer history if refresh rate was selected based on touch to allow
- // the hueristic to pick up with the new rate.
- mLayerHistory->clear();
- }
-
- return ret;
+ return mRefreshRateConfigs
+ .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
+ consideredSignals)
+ .getConfigId();
}
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4a0280f..2fe5629 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -39,11 +39,16 @@
namespace android {
using namespace std::chrono_literals;
+using scheduler::LayerHistory;
class DispSync;
class FenceTime;
class InjectVSyncSource;
-struct DisplayStateInfo;
+
+namespace scheduler {
+class VSyncDispatch;
+class VSyncTracker;
+} // namespace scheduler
class ISchedulerCallback {
public:
@@ -54,13 +59,24 @@
virtual void kernelTimerChanged(bool expired) = 0;
};
-class Scheduler {
+class IPhaseOffsetControl {
+public:
+ virtual ~IPhaseOffsetControl() = default;
+ virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
+};
+
+class Scheduler : public IPhaseOffsetControl {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
// Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart { EARLY, NORMAL };
+ enum class TransactionStart {
+ Early, // DEPRECATED. Start the transaction early. Times out on its own
+ EarlyStart, // Start the transaction early and keep this config until EarlyEnd
+ EarlyEnd, // End the early config started at EarlyStart
+ Normal // Start the transaction at the normal time
+ };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
@@ -80,14 +96,16 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId,
- nsecs_t vsyncPeriod);
-
+ void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod)
+ EXCLUDES(mFeatureStateLock);
+ void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
// Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
+ void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
void getDisplayStatInfo(DisplayStatInfo* stats);
@@ -118,7 +136,8 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime);
+ void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
+ void setConfigChangePending(bool pending);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
@@ -159,8 +178,11 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2, bool useContentDetection);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
+ std::unique_ptr<LayerHistory>, bool useContentDetectionV2, bool useContentDetection);
+
+ static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
+ bool useContentDetectionV2);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -177,14 +199,18 @@
// handles various timer features to change the refresh rate.
template <class T>
- void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
+ bool handleTimerStateChanged(T* currentState, T newState);
void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate
// selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
// for the suggested refresh rate.
- HwcConfigIndexType calculateRefreshRateConfigIndexType() REQUIRES(mFeatureStateLock);
+ HwcConfigIndexType calculateRefreshRateConfigIndexType(
+ scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
+ REQUIRES(mFeatureStateLock);
+
+ void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -205,14 +231,17 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
+ // Whether to use idle timer callbacks that support the kernel timer.
+ const bool mSupportKernelTimer;
+
+ const bool mUseVsyncPredictor;
+ const std::unique_ptr<scheduler::VSyncTracker> mVSyncTracker;
+ const std::unique_ptr<scheduler::VSyncDispatch> mVSyncDispatch;
std::unique_ptr<DispSync> mPrimaryDispSync;
std::unique_ptr<EventControlThread> mEventControlThread;
// Used to choose refresh rate if content detection is enabled.
- std::unique_ptr<scheduler::LayerHistory> mLayerHistory;
-
- // Whether to use idle timer callbacks that support the kernel timer.
- const bool mSupportKernelTimer;
+ const std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -234,9 +263,19 @@
TimerState displayPowerTimer = TimerState::Expired;
std::optional<HwcConfigIndexType> configId;
- scheduler::LayerHistory::Summary contentRequirements;
+ LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
+
+ // Used to cache the last parameters of onPrimaryDisplayConfigChanged
+ struct ConfigChangedParams {
+ ConnectionHandle handle;
+ PhysicalDisplayId displayId;
+ HwcConfigIndexType configId;
+ nsecs_t vsyncPeriod;
+ };
+
+ std::optional<ConfigChangedParams> cachedConfigChangedParams;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index cd15617..2a6fd05 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -87,10 +87,26 @@
return ScheduleResult::Scheduled;
}
+void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration,
+ nsecs_t earliestVsync) {
+ mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration};
+}
+
+bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
+ return mWorkloadUpdateInfo.has_value();
+}
+
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
- if (!mArmedInfo) {
+ if (!mArmedInfo && !mWorkloadUpdateInfo) {
return;
}
+
+ if (mWorkloadUpdateInfo) {
+ mEarliestVsync = mWorkloadUpdateInfo->earliestVsync;
+ mWorkDuration = mWorkloadUpdateInfo->duration;
+ mWorkloadUpdateInfo.reset();
+ }
+
auto const nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
@@ -192,7 +208,7 @@
std::optional<std::string_view> nextWakeupName;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
- if (!callback->wakeupTime()) {
+ if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
continue;
}
@@ -227,7 +243,8 @@
std::vector<Invocation> invocations;
{
std::lock_guard<decltype(mMutex)> lk(mMutex);
- mLastTimerCallback = mTimeKeeper->now();
+ auto const now = mTimeKeeper->now();
+ mLastTimerCallback = now;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto& callback = it->second;
auto const wakeupTime = callback->wakeupTime();
@@ -235,7 +252,8 @@
continue;
}
- if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+ auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
+ if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
invocations.emplace_back(
Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
@@ -291,6 +309,15 @@
}
auto& callback = it->second;
auto const now = mTimeKeeper->now();
+
+ /* If the timer thread will run soon, we'll apply this work update via the callback
+ * timer recalculation to avoid cancelling a callback that is about to fire. */
+ auto const rearmImminent = now > mIntendedWakeupTime;
+ if (CC_UNLIKELY(rearmImminent)) {
+ callback->addPendingWorkloadUpdate(workDuration, earliestVsync);
+ return ScheduleResult::Scheduled;
+ }
+
result = callback->schedule(workDuration, earliestVsync, mTracker, now);
if (result == ScheduleResult::CannotSchedule) {
return result;
@@ -313,10 +340,14 @@
}
auto& callback = it->second;
- if (callback->wakeupTime()) {
+ auto const wakeupTime = callback->wakeupTime();
+ if (wakeupTime) {
callback->disarm();
- mIntendedWakeupTime = kInvalidTime;
- rearmTimer(mTimeKeeper->now());
+
+ if (*wakeupTime == mIntendedWakeupTime) {
+ mIntendedWakeupTime = kInvalidTime;
+ rearmTimer(mTimeKeeper->now());
+ }
return CancelResult::Cancelled;
}
return CancelResult::TooLate;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 26a8ec0..957c0d1 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -64,6 +64,13 @@
// This moves the state from armed->running.
// Store the timestamp that this was intended for as the last called timestamp.
nsecs_t executing();
+
+ // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
+ // call to update()
+ void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync);
+
+ // Checks if there is a pending update to the workload, returning true if so.
+ bool hasPendingWorkloadUpdate() const;
// End: functions that are not threadsafe.
// Invoke the callback with the two given timestamps, moving the state from running->disarmed.
@@ -88,6 +95,12 @@
std::optional<ArmingInfo> mArmedInfo;
std::optional<nsecs_t> mLastDispatchTime;
+ struct WorkloadUpdateInfo {
+ nsecs_t duration;
+ nsecs_t earliestVsync;
+ };
+ std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo;
+
mutable std::mutex mRunningMutex;
std::condition_variable mCv;
bool mRunning GUARDED_BY(mRunningMutex) = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 40a992c..2567c04 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -25,16 +25,17 @@
#include <cutils/properties.h>
#include <utils/Trace.h>
+#include <chrono>
#include <cinttypes>
#include <mutex>
namespace android::scheduler {
-VSyncModulator::VSyncModulator(Scheduler& scheduler,
+VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
Scheduler::ConnectionHandle appConnectionHandle,
Scheduler::ConnectionHandle sfConnectionHandle,
const OffsetsConfig& config)
- : mScheduler(scheduler),
+ : mPhaseOffsetControl(phaseOffsetControl),
mAppConnectionHandle(appConnectionHandle),
mSfConnectionHandle(sfConnectionHandle),
mOffsetsConfig(config) {
@@ -50,13 +51,35 @@
}
void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
- if (transactionStart == Scheduler::TransactionStart::EARLY) {
+ switch (transactionStart) {
+ case Scheduler::TransactionStart::EarlyStart:
+ ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
+ mExplicitEarlyWakeup = true;
+ break;
+ case Scheduler::TransactionStart::EarlyEnd:
+ ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
+ mExplicitEarlyWakeup = false;
+ break;
+ case Scheduler::TransactionStart::Normal:
+ case Scheduler::TransactionStart::Early:
+ // Non explicit don't change the explicit early wakeup state
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ }
+
+ if (!mExplicitEarlyWakeup &&
+ (transactionStart == Scheduler::TransactionStart::Early ||
+ transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ mEarlyTxnStartTime = std::chrono::steady_clock::now();
}
// An early transaction stays an early transaction.
if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EARLY) {
+ mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
return;
}
mTransactionStart = transactionStart;
@@ -64,8 +87,9 @@
}
void VSyncModulator::onTransactionHandled() {
- if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
- mTransactionStart = Scheduler::TransactionStart::NORMAL;
+ mTxnAppliedTime = std::chrono::steady_clock::now();
+ if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
+ mTransactionStart = Scheduler::TransactionStart::Normal;
updateOffsets();
}
@@ -87,9 +111,15 @@
void VSyncModulator::onRefreshed(bool usedRenderEngine) {
bool updateOffsetsNeeded = false;
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
+
+ // Apply a margin to account for potential data races
+ // This might make us stay in early offsets for one
+ // additional frame but it's better to be conservative here.
+ if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updateOffsetsNeeded = true;
+ }
}
if (usedRenderEngine) {
mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
@@ -111,8 +141,8 @@
const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
// Early offsets are used if we're in the middle of a refresh rate
// change, or if we recently begin a transaction.
- if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
- mRefreshRateChangePending) {
+ if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
+ mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
return mOffsetsConfig.early;
} else if (mRemainingRenderEngineUsageCount > 0) {
return mOffsetsConfig.earlyGl;
@@ -129,8 +159,8 @@
void VSyncModulator::updateOffsetsLocked() {
const Offsets& offsets = getNextOffsets();
- mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
- mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
+ mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
+ mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
mOffsets = offsets;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 704a5d5..ab678c9 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <mutex>
#include "Scheduler.h"
@@ -37,6 +38,9 @@
// switch in and out of gl composition.
static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+ // Margin used to account for potential data races
+ static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
+
public:
// Wrapper for a collection of surfaceflinger/app offsets for a particular
// configuration.
@@ -61,7 +65,7 @@
bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
};
- VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
+ VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
@@ -90,13 +94,14 @@
Offsets getOffsets() const EXCLUDES(mMutex);
private:
+ friend class VSyncModulatorTest;
// Returns the next offsets that we should be using
const Offsets& getNextOffsets() const REQUIRES(mMutex);
// Updates offsets and persists them into the scheduler framework.
void updateOffsets() EXCLUDES(mMutex);
void updateOffsetsLocked() REQUIRES(mMutex);
- Scheduler& mScheduler;
+ IPhaseOffsetControl& mPhaseOffsetControl;
const ConnectionHandle mAppConnectionHandle;
const ConnectionHandle mSfConnectionHandle;
@@ -106,10 +111,13 @@
Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::NORMAL;
+ Scheduler::TransactionStart::Normal;
std::atomic<bool> mRefreshRateChangePending = false;
+ std::atomic<bool> mExplicitEarlyWakeup = false;
std::atomic<int> mRemainingEarlyFrameCount = 0;
std::atomic<int> mRemainingRenderEngineUsageCount = 0;
+ std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
+ std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
bool mTraceDetailedInfo = false;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index a3cb772..61f3fbb 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -77,7 +77,14 @@
std::lock_guard<std::mutex> lk(mMutex);
if (!validate(timestamp)) {
- ALOGV("timestamp was too far off the last known timestamp");
+ // VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
+ // don't insert this ts into mTimestamps ringbuffer.
+ if (!mTimestamps.empty()) {
+ mKnownTimestamp =
+ std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
+ } else {
+ mKnownTimestamp = timestamp;
+ }
return false;
}
@@ -236,27 +243,21 @@
void VSyncPredictor::clearTimestamps() {
if (!mTimestamps.empty()) {
- mKnownTimestamp = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+ auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+ if (mKnownTimestamp) {
+ mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+ } else {
+ mKnownTimestamp = maxRb;
+ }
+
mTimestamps.clear();
mLastTimestampIndex = 0;
}
}
-bool VSyncPredictor::needsMoreSamples(nsecs_t now) const {
- using namespace std::literals::chrono_literals;
+bool VSyncPredictor::needsMoreSamples() const {
std::lock_guard<std::mutex> lk(mMutex);
- bool needsMoreSamples = true;
- if (mTimestamps.size() >= kMinimumSamplesForPrediction) {
- nsecs_t constexpr aLongTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
- if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) {
- auto const lastTimestamp = mTimestamps[mLastTimestampIndex];
- needsMoreSamples = !((lastTimestamp + aLongTime) > now);
- }
- }
-
- ATRACE_INT("VSP-moreSamples", needsMoreSamples);
- return needsMoreSamples;
+ return mTimestamps.size() < kMinimumSamplesForPrediction;
}
void VSyncPredictor::resetModel() {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3ca878d..5f3c418 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -52,11 +52,10 @@
*/
void setPeriod(nsecs_t period) final;
- /* Query if the model is in need of more samples to make a prediction at timePoint.
- * \param [in] timePoint The timePoint to inquire of.
+ /* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
- bool needsMoreSamples(nsecs_t timePoint) const;
+ bool needsMoreSamples() const final;
std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 5f0c9ce..a2b279b 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -55,15 +55,17 @@
}
};
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncDispatch& dispatch,
+ VSyncTracker& tracker, size_t pendingFenceLimit,
+ bool supportKernelIdleTimer)
: mClock(std::move(clock)),
- mTracker(std::move(tracker)),
- mDispatch(std::move(dispatch)),
+ mTracker(tracker),
+ mDispatch(dispatch),
mPendingLimit(pendingFenceLimit),
mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
- ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
- : nullptr) {}
+ ? std::make_unique<PredictedVsyncTracer>(mDispatch)
+ : nullptr),
+ mSupportKernelIdleTimer(supportKernelIdleTimer) {}
VSyncReactor::~VSyncReactor() = default;
@@ -180,7 +182,7 @@
} else if (time == Fence::SIGNAL_TIME_INVALID) {
it = mUnfiredFences.erase(it);
} else {
- timestampAccepted &= mTracker->addVsyncTimestamp(time);
+ timestampAccepted &= mTracker.addVsyncTimestamp(time);
it = mUnfiredFences.erase(it);
}
@@ -192,7 +194,7 @@
}
mUnfiredFences.push_back(fence);
} else {
- timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+ timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
}
if (!timestampAccepted) {
@@ -222,15 +224,16 @@
}
nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
- auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
- return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+ auto const currentPeriod = periodOffset ? mTracker.currentPeriod() : 0;
+ return mTracker.nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
}
nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
- return mTracker->nextAnticipatedVSyncTimeFrom(now);
+ return mTracker.nextAnticipatedVSyncTimeFrom(now);
}
void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+ ATRACE_CALL();
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
@@ -238,8 +241,7 @@
}
void VSyncReactor::endPeriodTransition() {
- setIgnorePresentFencesInternal(false);
- mMoreSamplesNeeded = false;
+ ATRACE_CALL();
mPeriodTransitioningTo.reset();
mPeriodConfirmationInProgress = false;
mLastHwVsync.reset();
@@ -249,19 +251,22 @@
ATRACE_INT64("VSR-setPeriod", period);
std::lock_guard lk(mMutex);
mLastHwVsync.reset();
- if (period == getPeriod()) {
+
+ if (!mSupportKernelIdleTimer && period == getPeriod()) {
endPeriodTransition();
+ setIgnorePresentFencesInternal(false);
+ mMoreSamplesNeeded = false;
} else {
startPeriodTransition(period);
}
}
nsecs_t VSyncReactor::getPeriod() {
- return mTracker->currentPeriod();
+ return mTracker.currentPeriod();
}
void VSyncReactor::beginResync() {
- mTracker->resetModel();
+ mTracker.resetModel();
}
void VSyncReactor::endResync() {}
@@ -275,6 +280,13 @@
return false;
}
+ const bool periodIsChanging =
+ mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod());
+ if (mSupportKernelIdleTimer && !periodIsChanging) {
+ // Clear out the Composer-provided period and use the allowance logic below
+ HwcVsyncPeriod = {};
+ }
+
auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
static constexpr int allowancePercent = 10;
static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
@@ -293,24 +305,37 @@
std::lock_guard<std::mutex> lk(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
+ ATRACE_NAME("VSR: period confirmed");
if (mPeriodTransitioningTo) {
- mTracker->setPeriod(*mPeriodTransitioningTo);
+ mTracker.setPeriod(*mPeriodTransitioningTo);
for (auto& entry : mCallbacks) {
entry.second->setPeriod(*mPeriodTransitioningTo);
}
*periodFlushed = true;
}
+
+ if (mLastHwVsync) {
+ mTracker.addVsyncTimestamp(*mLastHwVsync);
+ }
+ mTracker.addVsyncTimestamp(timestamp);
+
endPeriodTransition();
+ mMoreSamplesNeeded = mTracker.needsMoreSamples();
} else if (mPeriodConfirmationInProgress) {
+ ATRACE_NAME("VSR: still confirming period");
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
} else {
- mMoreSamplesNeeded = false;
+ ATRACE_NAME("VSR: adding sample");
*periodFlushed = false;
+ mTracker.addVsyncTimestamp(timestamp);
+ mMoreSamplesNeeded = mTracker.needsMoreSamples();
}
- mTracker->addVsyncTimestamp(timestamp);
+ if (!mMoreSamplesNeeded) {
+ setIgnorePresentFencesInternal(false);
+ }
return mMoreSamplesNeeded;
}
@@ -328,9 +353,9 @@
return NO_MEMORY;
}
- auto const period = mTracker->currentPeriod();
- auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
- phase, mClock->now());
+ auto const period = mTracker.currentPeriod();
+ auto repeater = std::make_unique<CallbackRepeater>(mDispatch, callback, name, period, phase,
+ mClock->now());
it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
}
@@ -384,9 +409,9 @@
}
StringAppendF(&result, "VSyncTracker:\n");
- mTracker->dump(result);
+ mTracker.dump(result);
StringAppendF(&result, "VSyncDispatch:\n");
- mDispatch->dump(result);
+ mDispatch.dump(result);
}
void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 31ddf5a..22ceb39 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -35,8 +35,8 @@
// TODO (b/145217110): consider renaming.
class VSyncReactor : public android::DispSync {
public:
- VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+ VSyncReactor(std::unique_ptr<Clock> clock, VSyncDispatch& dispatch, VSyncTracker& tracker,
+ size_t pendingFenceLimit, bool supportKernelIdleTimer);
~VSyncReactor();
bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
@@ -71,8 +71,8 @@
REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
- std::unique_ptr<VSyncTracker> const mTracker;
- std::unique_ptr<VSyncDispatch> const mDispatch;
+ VSyncTracker& mTracker;
+ VSyncDispatch& mDispatch;
size_t const mPendingLimit;
mutable std::mutex mMutex;
@@ -89,6 +89,7 @@
GUARDED_BY(mMutex);
const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+ const bool mSupportKernelIdleTimer = false;
};
class SystemClock : public Clock {
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 05a6fc3..107c540 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
+ virtual bool needsMoreSamples() const = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4ca2074..89ad4ae 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -23,12 +23,15 @@
#include "SurfaceFlinger.h"
+#include <android-base/properties.h>
#include <android/configuration.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/Boost.h>
#include <android/native_window.h>
+#include <android/os/BnSetInputWindowsListener.h>
+#include <android/os/IInputFlinger.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -56,7 +59,7 @@
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
-#include <input/IInputFlinger.h>
+#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -87,6 +90,7 @@
#include <functional>
#include <mutex>
#include <optional>
+#include <type_traits>
#include <unordered_map>
#include "BufferLayer.h"
@@ -101,10 +105,12 @@
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
+#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
+#include "LayerRenderArea.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
@@ -115,6 +121,7 @@
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
@@ -125,6 +132,19 @@
#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
+#define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
+
+#define ON_MAIN_THREAD(expr) \
+ [&] { \
+ LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
+ UnnecessaryLock lock(mStateLock); \
+ return (expr); \
+ }()
+
+#undef NO_THREAD_SAFETY_ANALYSIS
+#define NO_THREAD_SAFETY_ANALYSIS \
+ _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
+
namespace android {
using namespace std::string_literals;
@@ -133,7 +153,7 @@
using namespace android::hardware::configstore::V1_0;
using namespace android::sysprop;
-using android::hardware::power::V1_0::PowerHint;
+using android::hardware::power::Boost;
using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
@@ -173,12 +193,12 @@
#pragma clang diagnostic pop
template <typename Mutex>
-struct ConditionalLockGuard {
- ConditionalLockGuard(Mutex& mutex, bool lock) : mutex(mutex), lock(lock) {
+struct SCOPED_CAPABILITY ConditionalLockGuard {
+ ConditionalLockGuard(Mutex& mutex, bool lock) ACQUIRE(mutex) : mutex(mutex), lock(lock) {
if (lock) mutex.lock();
}
- ~ConditionalLockGuard() {
+ ~ConditionalLockGuard() RELEASE() {
if (lock) mutex.unlock();
}
@@ -188,8 +208,29 @@
using ConditionalLock = ConditionalLockGuard<Mutex>;
+struct SCOPED_CAPABILITY TimedLock {
+ TimedLock(Mutex& mutex, nsecs_t timeout, const char* whence) ACQUIRE(mutex)
+ : mutex(mutex), status(mutex.timedLock(timeout)) {
+ ALOGE_IF(!locked(), "%s timed out locking: %s (%d)", whence, strerror(-status), status);
+ }
+
+ ~TimedLock() RELEASE() {
+ if (locked()) mutex.unlock();
+ }
+
+ bool locked() const { return status == NO_ERROR; }
+
+ Mutex& mutex;
+ const status_t status;
+};
+
+struct SCOPED_CAPABILITY UnnecessaryLock {
+ explicit UnnecessaryLock(Mutex& mutex) ACQUIRE(mutex) {}
+ ~UnnecessaryLock() RELEASE() {}
+};
+
// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
-constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV / 160.f;
+constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
float getDensityFromProperty(const char* property, bool required) {
char value[PROPERTY_VALUE_MAX];
@@ -198,7 +239,7 @@
ALOGE("%s must be defined as a build property", property);
return FALLBACK_DENSITY;
}
- return density / 160.f;
+ return density;
}
// Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
@@ -217,13 +258,28 @@
} // namespace anonymous
+struct SetInputWindowsListener : os::BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {}
+
+ binder::Status onSetInputWindowsFinished() override;
+
+ std::function<void()> mListenerCb;
+};
+
+binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
+ if (mListenerCb != nullptr) {
+ mListenerCb();
+ }
+ return binder::Status::ok();
+}
+
// ---------------------------------------------------------------------------
const String16 sHardwareTest("android.permission.HARDWARE_TEST");
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
-const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
+const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -282,7 +338,9 @@
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
- mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
+ mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
+ mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
+}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
@@ -360,10 +418,6 @@
int debugDdms = atoi(value);
ALOGI_IF(debugDdms, "DDMS debugging not supported");
- property_get("debug.sf.disable_backpressure", value, "0");
- mPropagateBackpressure = !atoi(value);
- ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
-
property_get("debug.sf.enable_gl_backpressure", value, "0");
mPropagateBackpressureClientComposition = atoi(value);
ALOGI_IF(mPropagateBackpressureClientComposition,
@@ -387,6 +441,10 @@
const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+ mGraphicBufferProducerListSizeLogThreshold =
+ std::max(static_cast<int>(0.95 *
+ static_cast<double>(mMaxGraphicBufferProducerListSize)),
+ 1);
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -405,10 +463,15 @@
// deriving the setting from the set service name, but it
// would be brittle if the name that's not 'default' is used
// for production purposes later on.
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::details::setTrebleTestingOverride(true);
}
useFrameRateApi = use_frame_rate_api(true);
+
+ mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
+ base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
+
+ mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
}
SurfaceFlinger::~SurfaceFlinger() = default;
@@ -561,13 +624,6 @@
if (mWindowManager != 0) {
mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
- sp<IBinder> input(defaultServiceManager()->getService(
- String16("inputflinger")));
- if (input == nullptr) {
- ALOGE("Failed to link to input service");
- } else {
- mInputFlinger = interface_cast<IInputFlinger>(input);
- }
if (mVrFlinger) {
mVrFlinger->OnBootFinished();
@@ -582,14 +638,21 @@
LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
- static_cast<void>(schedule([this]() NO_THREAD_SAFETY_ANALYSIS {
+ sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
+
+ static_cast<void>(schedule([=] {
+ if (input == nullptr) {
+ ALOGE("Failed to link to input service");
+ } else {
+ mInputFlinger = interface_cast<os::IInputFlinger>(input);
+ }
+
readPersistentProperties();
mPowerAdvisor.onBootFinished();
mBootStage = BootStage::FINISHED;
if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
- mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+ enableRefreshRateOverlay(true);
}
}));
}
@@ -830,9 +893,10 @@
? mInternalDisplayDensity
: FALLBACK_DENSITY;
}
+ info->density /= ACONFIGURATION_DENSITY_MEDIUM;
info->secure = display->isSecure();
- info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+ info->deviceProductInfo = display->getDeviceProductInfo();
return NO_ERROR;
}
@@ -934,9 +998,8 @@
}
if (isPrimary) {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- if (mDesiredActiveConfigChanged) {
- return mDesiredActiveConfig.configId.value();
+ if (const auto config = getDesiredActiveConfig()) {
+ return config->configId.value();
}
}
@@ -977,6 +1040,7 @@
mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ mScheduler->setConfigChangePending(true);
}
if (mRefreshRateOverlay) {
@@ -992,7 +1056,7 @@
}
auto future = schedule([=]() -> status_t {
- const auto display = getDisplayDeviceLocked(displayToken);
+ const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set allowed display configs for invalid display token %p",
displayToken.get());
@@ -1042,8 +1106,8 @@
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
.getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
- mUpcomingActiveConfig.configId, vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mUpcomingActiveConfig.configId, vsyncPeriod);
}
}
@@ -1057,20 +1121,14 @@
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ mScheduler->setConfigChangePending(false);
}
void SurfaceFlinger::performSetActiveConfig() {
ATRACE_CALL();
ALOGV("performSetActiveConfig");
// Store the local variable to release the lock.
- const auto desiredActiveConfig = [&]() -> std::optional<ActiveConfigInfo> {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- if (mDesiredActiveConfigChanged) {
- return mDesiredActiveConfig;
- }
- return std::nullopt;
- }();
-
+ const auto desiredActiveConfig = getDesiredActiveConfig();
if (!desiredActiveConfig) {
// No desired active config pending to be applied
return;
@@ -1184,7 +1242,7 @@
}
status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
- schedule([=] {
+ schedule([=]() MAIN_THREAD {
Vector<ColorMode> modes;
getDisplayColorModes(displayToken, &modes);
bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
@@ -1230,7 +1288,7 @@
}
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
- static_cast<void>(schedule([=] {
+ static_cast<void>(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
@@ -1261,7 +1319,7 @@
}
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
- static_cast<void>(schedule([=] {
+ static_cast<void>(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
@@ -1305,30 +1363,6 @@
return NO_ERROR;
}
-std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
- const DisplayDevice& display) const {
- // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
- // avoid repetitive HAL IPC and EDID parsing.
- const auto displayId = display.getId();
- LOG_FATAL_IF(!displayId);
-
- const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
- LOG_FATAL_IF(!hwcDisplayId);
-
- uint8_t port;
- DisplayIdentificationData data;
- if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
- ALOGV("%s: No identification data.", __FUNCTION__);
- return {};
- }
-
- const auto info = parseDisplayIdentificationData(port, data);
- if (!info) {
- return {};
- }
- return info->deviceProductInfo;
-}
-
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
@@ -1351,7 +1385,7 @@
status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
- return schedule([=]() -> status_t {
+ return schedule([=]() MAIN_THREAD -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
componentMask,
@@ -1420,22 +1454,14 @@
return mScheduler->injectVSync(when, calculateExpectedPresentTime(when)) ? NO_ERROR : BAD_VALUE;
}
-status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
- NO_THREAD_SAFETY_ANALYSIS {
- // Try to acquire a lock for 1s, fail gracefully
- const status_t err = mStateLock.timedLock(s2ns(1));
- const bool locked = (err == NO_ERROR);
- if (!locked) {
- ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
- return TIMED_OUT;
- }
-
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
outLayers->clear();
- mCurrentState.traverseInZOrder([&](Layer* layer) {
- outLayers->push_back(layer->getLayerDebugInfo());
- });
-
- mStateLock.unlock();
+ schedule([=] {
+ const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ outLayers->push_back(layer->getLayerDebugInfo(display.get()));
+ });
+ }).wait();
return NO_ERROR;
}
@@ -1492,7 +1518,7 @@
return BAD_VALUE;
}
- return promise::chain(schedule([=] {
+ return promise::chain(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().setDisplayBrightness(*displayId, brightness);
} else {
@@ -1504,10 +1530,10 @@
.get();
}
-status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
- PowerHint powerHint = static_cast<PowerHint>(hintId);
+status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+ Boost powerBoost = static_cast<Boost>(boostId);
- if (powerHint == PowerHint::INTERACTION) {
+ if (powerBoost == Boost::INTERACTION) {
mScheduler->notifyTouchEvent();
}
@@ -1658,8 +1684,7 @@
// Enable / Disable HWVsync from the main thread to avoid race conditions with
// display power state.
- static_cast<void>(
- schedule([=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
+ static_cast<void>(schedule([=]() MAIN_THREAD { setPrimaryVsyncEnabledInternal(enabled); }));
}
void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
@@ -1670,12 +1695,11 @@
if (const auto displayId = getInternalDisplayIdLocked()) {
sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
if (display && display->isPoweredOn()) {
- setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
}
}
}
-// Note: it is assumed the caller holds |mStateLock| when this is called
void SurfaceFlinger::resetDisplayState() {
mScheduler->disableHardwareVsync(true);
// Clear the drawing state so that the logic inside of
@@ -1768,7 +1792,7 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
+sp<Fence> SurfaceFlinger::previousFrameFence() {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
@@ -1776,7 +1800,7 @@
: mPreviousPresentFences[1];
}
-bool SurfaceFlinger::previousFramePending(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
ATRACE_CALL();
const sp<Fence>& fence = previousFrameFence();
@@ -1790,7 +1814,7 @@
return status == -ETIME;
}
-nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+nsecs_t SurfaceFlinger::previousFramePresentTime() {
const sp<Fence>& fence = previousFrameFence();
if (fence == Fence::NO_FENCE) {
@@ -1808,8 +1832,7 @@
return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
}
-void SurfaceFlinger::onMessageReceived(int32_t what,
- nsecs_t expectedVSyncTime) NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
@@ -1823,7 +1846,7 @@
}
}
-void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
ATRACE_CALL();
const nsecs_t frameStart = systemTime();
@@ -1837,10 +1860,7 @@
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
const int graceTimeForPresentFenceMs =
- (mPropagateBackpressure &&
- (mPropagateBackpressureClientComposition || !mHadClientComposition))
- ? 1
- : 0;
+ (mPropagateBackpressureClientComposition || !mHadClientComposition) ? 1 : 0;
// Pending frames may trigger backpressure propagation.
const TracedOrdinal<bool> framePending = {"PrevFramePending",
@@ -1896,10 +1916,10 @@
// We received the present fence from the HWC, so we assume it successfully updated
// the config, hence we update SF.
mSetActiveConfigPending = false;
- setActiveConfigInternal();
+ ON_MAIN_THREAD(setActiveConfigInternal());
}
- if (framePending && mPropagateBackpressure) {
+ if (framePending) {
if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
signalLayerUpdate();
return;
@@ -1913,22 +1933,19 @@
// ...but if it's larger than 1s then we missed the trace cutoff.
static constexpr nsecs_t kMaxJankyDuration =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ nsecs_t jankDurationToUpload = -1;
// If we're in a user build then don't push any atoms
if (!mIsUserBuild && mMissedFrameJankCount > 0) {
- const auto displayDevice = getDefaultDisplayDeviceLocked();
+ const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
// Only report jank when the display is on, as displays in DOZE
// power mode may operate at a different frame rate than is
// reported in their config, which causes noticeable (but less
// severe) jank.
- if (displayDevice && displayDevice->getPowerMode() == hal::PowerMode::ON) {
+ if (display && display->getPowerMode() == hal::PowerMode::ON) {
const nsecs_t currentTime = systemTime();
const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
- ATRACE_NAME("Jank detected");
- ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
- const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
- android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
- jankyDurationMillis, mMissedFrameJankCount);
+ jankDurationToUpload = jankDuration;
}
// We either reported a jank event or we missed the trace
@@ -1950,6 +1967,12 @@
mTracingEnabledChanged = false;
}
+ if (mRefreshRateOverlaySpinner) {
+ if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
+ mRefreshRateOverlay->onInvalidate();
+ }
+ }
+
bool refreshNeeded;
{
ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
@@ -1974,13 +1997,14 @@
mScheduler->chooseRefreshRateForContent();
}
- performSetActiveConfig();
+ ON_MAIN_THREAD(performSetActiveConfig());
updateCursorAsync();
updateInputFlinger();
refreshNeeded |= mRepaintEverything;
if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
+ mLastJankDuration = jankDurationToUpload;
// Signal a refresh if a transaction modified the window state,
// a new buffer was latched, or if HWC has requested a full
// repaint
@@ -2001,8 +2025,10 @@
bool flushedATransaction = flushTransactionQueues();
- bool runHandleTransaction = transactionFlags &&
- ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+ bool runHandleTransaction =
+ (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
+ flushedATransaction ||
+ mForceTraversal;
if (runHandleTransaction) {
handleTransaction(eTransactionMask);
@@ -2023,8 +2049,9 @@
mRefreshPending = false;
compositionengine::CompositionRefreshArgs refreshArgs;
- refreshArgs.outputs.reserve(mDisplays.size());
- for (const auto& [_, display] : mDisplays) {
+ const auto& displays = ON_MAIN_THREAD(mDisplays);
+ refreshArgs.outputs.reserve(displays.size());
+ for (const auto& [_, display] : displays) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
@@ -2047,6 +2074,7 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
refreshArgs.blursAreExpensive = mBlursAreExpensive;
+ refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -2078,21 +2106,18 @@
const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
- mHadClientComposition =
- std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
- auto& displayDevice = tokenDisplayPair.second;
- return displayDevice->getCompositionDisplay()->getState().usesClientComposition &&
- !displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
- });
- mHadDeviceComposition =
- std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
- auto& displayDevice = tokenDisplayPair.second;
- return displayDevice->getCompositionDisplay()->getState().usesDeviceComposition;
- });
+ mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+ const auto& state = pair.second->getCompositionDisplay()->getState();
+ return state.usesClientComposition && !state.reusedClientComposition;
+ });
+ mHadDeviceComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+ const auto& state = pair.second->getCompositionDisplay()->getState();
+ return state.usesDeviceComposition;
+ });
mReusedClientComposition =
- std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
- auto& displayDevice = tokenDisplayPair.second;
- return displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
+ std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+ const auto& state = pair.second->getCompositionDisplay()->getState();
+ return state.reusedClientComposition;
});
// Only report a strategy change if we move in and out of composition with hw overlays
@@ -2100,7 +2125,8 @@
mTimeStats->incrementCompositionStrategyChanges();
}
- mVSyncModulator->onRefreshed(mHadClientComposition);
+ // TODO: b/160583065 Enable skip validation when SF caches all client composition layers
+ mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition);
mLayersWithQueuedFrames.clear();
if (mVisibleRegionsDirty) {
@@ -2198,14 +2224,14 @@
for (auto& layer : mLayersWithQueuedFrames) {
layer->releasePendingBuffer(dequeueReadyTime);
}
- // |mStateLock| not needed as we are on the main thread
- const auto displayDevice = getDefaultDisplayDeviceLocked();
+
+ const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
getBE().mGlCompositionDoneTimeline.updateSignalTimes();
std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (displayDevice && displayDevice->getCompositionDisplay()->getState().usesClientComposition) {
+ if (display && display->getCompositionDisplay()->getState().usesClientComposition) {
glCompositionDoneFenceTime =
- std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+ std::make_shared<FenceTime>(display->getCompositionDisplay()
->getRenderSurface()
->getClientTargetAcquireFence());
getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
@@ -2215,9 +2241,8 @@
getBE().mDisplayTimeline.updateSignalTimes();
mPreviousPresentFences[1] = mPreviousPresentFences[0];
- mPreviousPresentFences[0] = displayDevice
- ? getHwComposer().getPresentFence(*displayDevice->getId())
- : Fence::NO_FENCE;
+ mPreviousPresentFences[0] =
+ display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
getBE().mDisplayTimeline.push(presentFenceTime);
@@ -2236,8 +2261,8 @@
}
mDrawingState.traverse([&](Layer* layer) {
- bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
- presentFenceTime, compositorTiming);
+ const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
+ presentFenceTime, compositorTiming);
if (frameLatched) {
recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
}
@@ -2246,14 +2271,15 @@
mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
mTransactionCompletedThread.sendCallbacks();
- if (displayDevice && displayDevice->isPrimary() &&
- displayDevice->getPowerMode() == hal::PowerMode::ON && presentFenceTime->isValid()) {
+ if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
+ presentFenceTime->isValid()) {
mScheduler->addPresentFence(presentFenceTime);
}
+ const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+
if (!hasSyncFramework) {
- if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
- displayDevice->isPoweredOn()) {
+ if (isDisplayConnected && display->isPoweredOn()) {
mScheduler->enableHardwareVsync();
}
}
@@ -2264,11 +2290,10 @@
if (presentFenceTime->isValid()) {
mAnimFrameTracker.setActualPresentFence(
std::move(presentFenceTime));
- } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
+ } else if (isDisplayConnected) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
- const nsecs_t presentTime =
- getHwComposer().getRefreshTimestamp(*displayDevice->getId());
+ const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
mAnimFrameTracker.setActualPresentTime(presentTime);
}
mAnimFrameTracker.advanceFrame();
@@ -2289,8 +2314,15 @@
const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
- if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
- !displayDevice->isPoweredOn()) {
+ if (mLastJankDuration > 0) {
+ ATRACE_NAME("Jank detected");
+ const int32_t jankyDurationMillis = mLastJankDuration / (1000 * 1000);
+ android::util::stats_write(android::util::DISPLAY_JANK_REPORTED, jankyDurationMillis,
+ mMissedFrameJankCount);
+ mLastJankDuration = -1;
+ }
+
+ if (isDisplayConnected && !display->isPoweredOn()) {
return;
}
@@ -2346,7 +2378,7 @@
}
void SurfaceFlinger::computeLayerBounds() {
- for (const auto& pair : mDisplays) {
+ for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
const auto& displayDevice = pair.second;
const auto display = displayDevice->getCompositionDisplay();
for (const auto& layer : mDrawingState.layersSortedByZ) {
@@ -2361,10 +2393,8 @@
}
}
-void SurfaceFlinger::postFrame()
-{
- // |mStateLock| not needed as we are on the main thread
- const auto display = getDefaultDisplayDeviceLocked();
+void SurfaceFlinger::postFrame() {
+ const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
if (display && getHwComposer().isConnected(*display->getId())) {
uint32_t flipCount = display->getPageFlipCount();
if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
@@ -2422,8 +2452,10 @@
}
DisplayDeviceState state;
- state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
- event.hwcDisplayId};
+ state.physical = {.id = displayId,
+ .type = getHwComposer().getDisplayConnectionType(displayId),
+ .hwcDisplayId = event.hwcDisplayId,
+ .deviceProductInfo = info->deviceProductInfo};
state.isSecure = true; // All physical displays are currently considered secure.
state.displayName = info->name;
@@ -2539,6 +2571,7 @@
LOG_ALWAYS_FATAL_IF(!displayId);
auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
display->setActiveConfig(activeConfigId);
+ display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
display->setLayerStack(state.layerStack);
@@ -2660,7 +2693,8 @@
}
processDisplayAdded(displayToken, currentState);
if (currentState.physical) {
- initializeDisplays();
+ const auto display = getDisplayDeviceLocked(displayToken);
+ setPowerModeInternal(display, hal::PowerMode::ON);
}
return;
}
@@ -2678,9 +2712,14 @@
if (currentState.width != drawingState.width ||
currentState.height != drawingState.height) {
display->setDisplaySize(currentState.width, currentState.height);
+
if (display->isPrimary()) {
mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height);
}
+
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->setViewport(display->getSize());
+ }
}
}
}
@@ -2739,7 +2778,8 @@
* (perform the transaction for each of them if needed)
*/
- if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
+ if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
+ mForceTraversal = false;
mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2752,7 +2792,6 @@
mInputInfoChanged = true;
}
});
- mTraversalNeededMainThread = false;
}
/*
@@ -2764,7 +2803,7 @@
processDisplayHotplugEventsLocked();
}
- if (transactionFlags & (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) {
+ if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
// The transform hint might have changed for some layers
// (either because a display has changed, or because a layer
// as changed).
@@ -2787,7 +2826,7 @@
sp<const DisplayDevice> hintDisplay;
uint32_t currentlayerStack = 0;
bool first = true;
- mCurrentState.traverse([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
// NOTE: we rely on the fact that layers are sorted by
// layerStack first (so we don't have to traverse the list
// of displays for every layer).
@@ -2825,14 +2864,13 @@
// could be null if there is no display available at all to get
// the transform hint from.
if (hintDisplay) {
- layer->updateTransformHint(hintDisplay);
+ layer->updateTransformHint(hintDisplay->getTransformHint());
}
first = false;
});
}
-
/*
* Perform our own transaction if needed
*/
@@ -2881,19 +2919,23 @@
}
void SurfaceFlinger::updateInputWindowInfo() {
- std::vector<InputWindowInfo> inputHandles;
+ std::vector<InputWindowInfo> inputInfos;
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
- if (layer->hasInput()) {
+ if (layer->needsInputInfo()) {
// When calculating the screen bounds we ignore the transparent region since it may
// result in an unwanted offset.
- inputHandles.push_back(layer->fillInputInfo());
+ inputInfos.push_back(layer->fillInputInfo());
}
});
- mInputFlinger->setInputWindows(inputHandles,
- mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
- : nullptr);
+ mInputFlinger->setInputWindows(inputInfos,
+ mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
+ : nullptr);
+ for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
+ mInputFlinger->setFocusedWindow(focusRequest);
+ }
+ mInputWindowCommands.focusRequests.clear();
}
void SurfaceFlinger::commitInputWindowCommands() {
@@ -2901,10 +2943,9 @@
mPendingInputWindowCommands.clear();
}
-void SurfaceFlinger::updateCursorAsync()
-{
+void SurfaceFlinger::updateCursorAsync() {
compositionengine::CompositionRefreshArgs refreshArgs;
- for (const auto& [_, display] : mDisplays) {
+ for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
if (display->getId()) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
@@ -2914,7 +2955,7 @@
}
void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
- Scheduler::ConfigEvent event) NO_THREAD_SAFETY_ANALYSIS {
+ Scheduler::ConfigEvent event) {
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
// Sheduler::chooseRefreshRateForContent
@@ -2972,8 +3013,8 @@
// anyway since there are no connected apps at this point.
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig,
- vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value,
+ currentConfig, vsyncPeriod);
}
void SurfaceFlinger::commitTransaction()
@@ -3040,7 +3081,7 @@
}
void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
- for (const auto& [token, displayDevice] : mDisplays) {
+ for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
auto display = displayDevice->getCompositionDisplay();
if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
display->editState().dirtyRegion.orSelf(dirty);
@@ -3137,7 +3178,8 @@
status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
const sp<IBinder>& parentHandle,
- const sp<Layer>& parentLayer, bool addToCurrentState) {
+ const sp<Layer>& parentLayer, bool addToCurrentState,
+ uint32_t* outTransformHint) {
// add this layer to the current state list
{
Mutex::Autolock _l(mStateLock);
@@ -3177,7 +3219,20 @@
"Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
mGraphicBufferProducerList.size(),
mMaxGraphicBufferProducerListSize, mNumLayers.load());
+ if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
+ ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+ mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+ mNumLayers.load());
+ }
}
+
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ lbc->updateTransformHint(display->getTransformHint());
+ }
+ if (outTransformHint) {
+ *outTransformHint = lbc->getTransformHint();
+ }
+
mLayersAdded = true;
}
@@ -3203,7 +3258,7 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
+ return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
@@ -3216,6 +3271,10 @@
return old;
}
+void SurfaceFlinger::setTraversalNeeded() {
+ mForceTraversal = true;
+}
+
bool SurfaceFlinger::flushTransactionQueues() {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
@@ -3375,6 +3434,12 @@
for (const ComposerState& state : states) {
clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
listenerCallbacksWithSurfaces);
+ if ((flags & eAnimation) && state.state.surface) {
+ if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+ mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+ LayerHistory::LayerUpdateType::AnimationTX);
+ }
+ }
}
for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
@@ -3407,18 +3472,39 @@
// so we don't have to wake up again next frame to preform an uneeded traversal.
if (isMainThread && (transactionFlags & eTraversalNeeded)) {
transactionFlags = transactionFlags & (~eTraversalNeeded);
- mTraversalNeededMainThread = true;
+ mForceTraversal = true;
}
+ const auto transactionStart = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) {
+ return Scheduler::TransactionStart::Early;
+ }
+ if (flags & eExplicitEarlyWakeupEnd) {
+ return Scheduler::TransactionStart::EarlyEnd;
+ }
+ if (flags & eExplicitEarlyWakeupStart) {
+ return Scheduler::TransactionStart::EarlyStart;
+ }
+ return Scheduler::TransactionStart::Normal;
+ }(flags);
+
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
}
+ // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
+ if (flags & eEarlyWakeup) {
+ ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+ }
+
+ if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+ flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ }
+
// this triggers the transaction
- const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
- : Scheduler::TransactionStart::NORMAL;
- setTransactionFlags(transactionFlags, start);
+ setTransactionFlags(transactionFlags, transactionStart);
if (flags & eAnimation) {
mAnimTransactionPending = true;
@@ -3455,6 +3541,13 @@
break;
}
}
+ } else {
+ // even if a transaction is not needed, we need to update VsyncModulator
+ // about explicit early indications
+ if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
+ transactionStart == Scheduler::TransactionStart::EarlyEnd) {
+ mVSyncModulator->setTransactionStart(transactionStart);
+ }
}
}
@@ -3659,7 +3752,7 @@
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) {
+ if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eLayerStackChanged) {
@@ -3679,7 +3772,7 @@
mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
// AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded|eDisplayLayerStackChanged;
+ flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
}
}
if (what & layer_state_t::eDeferTransaction_legacy) {
@@ -3745,7 +3838,7 @@
}
if (what & layer_state_t::eInputInfoChanged) {
if (privileged) {
- layer->setInputInfo(s.inputInfo);
+ layer->setInputInfo(*s.inputHandle->getInfo());
flags |= eTraversalNeeded;
} else {
ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
@@ -3776,6 +3869,11 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eFixedTransformHintChanged) {
+ if (layer->setFixedTransformHint(s.fixedTransformHint)) {
+ flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
+ }
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
@@ -3828,13 +3926,8 @@
}
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
- uint32_t flags = 0;
- if (inputWindowCommands.syncInputWindows) {
- flags |= eTraversalNeeded;
- }
-
- mPendingInputWindowCommands.merge(inputWindowCommands);
- return flags;
+ const bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+ return hasChanges ? eTraversalNeeded : 0;
}
status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
@@ -3863,7 +3956,8 @@
mirrorLayer->mClonedChild = mirrorFrom->createClone();
}
- return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false);
+ return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
+ nullptr /* outTransformHint */);
}
status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
@@ -3895,7 +3989,12 @@
if (metadata.has(METADATA_WINDOW_TYPE)) {
int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
if (windowType == 441731) {
- metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL);
+ using U = std::underlying_type_t<InputWindowInfo::Type>;
+ // TODO(b/129481165): This static assert can be safely removed once conversion warnings
+ // are re-enabled.
+ static_assert(std::is_same_v<U, int32_t>);
+ metadata.setInt32(METADATA_WINDOW_TYPE,
+ static_cast<U>(InputWindowInfo::Type::NAVIGATION_BAR_PANEL));
primaryDisplayOnly = true;
}
}
@@ -3908,7 +4007,7 @@
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
- std::move(metadata), handle, outTransformHint, &layer);
+ std::move(metadata), handle, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceEffect:
// check if buffer size is set for color layer.
@@ -3946,7 +4045,7 @@
bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
- addToCurrentState);
+ addToCurrentState, outTransformHint);
if (result != NO_ERROR) {
return result;
}
@@ -4023,14 +4122,10 @@
status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
uint32_t w, uint32_t h, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
- uint32_t* outTransformHint, sp<Layer>* outLayer) {
+ sp<Layer>* outLayer) {
LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
- args.displayDevice = getDefaultDisplayDevice();
args.textureName = getNewTexture();
sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
- if (outTransformHint) {
- *outTransformHint = layer->getTransformHint();
- }
*handle = layer->getHandle();
*outLayer = layer;
@@ -4125,14 +4220,7 @@
void SurfaceFlinger::initializeDisplays() {
// Async since we may be called from the main thread.
- static_cast<void>(schedule([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
-}
-
-void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled) {
- if (mHWCVsyncState != enabled) {
- getHwComposer().setVsyncEnabled(displayId, enabled);
- mHWCVsyncState = enabled;
- }
+ static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
}
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4163,7 +4251,7 @@
}
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
- setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
@@ -4182,7 +4270,7 @@
}
// Make sure HWVsync is disabled before turning off the display
- setVsyncEnabledInHWC(*displayId, hal::Vsync::DISABLE);
+ getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
@@ -4216,7 +4304,7 @@
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
+ schedule([=]() MAIN_THREAD {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4229,8 +4317,7 @@
}).wait();
}
-status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
- bool asProto) NO_THREAD_SAFETY_ANALYSIS {
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
std::string result;
IPCThreadState* ipc = IPCThreadState::self();
@@ -4242,18 +4329,6 @@
StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
pid, uid);
} else {
- // Try to get the main lock, but give up after one second
- // (this would indicate SF is stuck, but we want to be able to
- // print something in dumpsys).
- status_t err = mStateLock.timedLock(s2ns(1));
- bool locked = (err == NO_ERROR);
- if (!locked) {
- StringAppendF(&result,
- "SurfaceFlinger appears to be unresponsive (%s [%d]), dumping anyways "
- "(no locks held)\n",
- strerror(-err), err);
- }
-
static const std::unordered_map<std::string, Dumper> dumpers = {
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
{"--dispsync"s,
@@ -4271,18 +4346,23 @@
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
- const auto it = dumpers.find(flag);
- if (it != dumpers.end()) {
- (it->second)(args, asProto, result);
- } else if (!asProto) {
- dumpAllLocked(args, result);
+ bool dumpLayers = true;
+ {
+ TimedLock lock(mStateLock, s2ns(1), __FUNCTION__);
+ if (!lock.locked()) {
+ StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+ strerror(-lock.status), lock.status);
+ }
+
+ if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+ (it->second)(args, asProto, result);
+ dumpLayers = false;
+ } else if (!asProto) {
+ dumpAllLocked(args, result);
+ }
}
- if (locked) {
- mStateLock.unlock();
- }
-
- if (it == dumpers.end()) {
+ if (dumpLayers) {
const LayersProto layersProto = dumpProtoFromMainThread();
if (asProto) {
result.append(layersProto.SerializeAsString());
@@ -4556,11 +4636,11 @@
LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
// If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
- const auto display = getDefaultDisplayDeviceLocked();
+ const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
LayersProto layersProto;
for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
- layer->writeToProto(layersProto, traceFlags, display);
+ layer->writeToProto(layersProto, traceFlags, display.get());
}
return layersProto;
@@ -4669,7 +4749,7 @@
StringAppendF(&result, "Composition layers\n");
mDrawingState.traverseInZOrder([&](Layer* layer) {
auto* compositionState = layer->getCompositionState();
- if (!compositionState) return;
+ if (!compositionState || !compositionState->isVisible) return;
android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
layer->getDebugName() ? layer->getDebugName()
@@ -4749,9 +4829,9 @@
StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
Layer::miniDumpHeader(result);
- const sp<DisplayDevice> displayDevice = display;
- mCurrentState.traverseInZOrder(
- [&](Layer* layer) { layer->miniDump(result, displayDevice); });
+
+ const DisplayDevice& ref = *display;
+ mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
result.append("\n");
}
@@ -4833,7 +4913,7 @@
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
- case NOTIFY_POWER_HINT:
+ case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
@@ -5228,22 +5308,23 @@
return NO_ERROR;
}
case 1034: {
- n = data.readInt32();
- if (n == 1 && !mRefreshRateOverlay) {
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
- auto& current = mRefreshRateConfigs->getCurrentRefreshRate();
- mRefreshRateOverlay->changeRefreshRate(current);
- } else if (n == 0) {
- mRefreshRateOverlay.reset();
- } else {
- reply->writeBool(mRefreshRateOverlay != nullptr);
+ switch (n = data.readInt32()) {
+ case 0:
+ case 1:
+ enableRefreshRateOverlay(static_cast<bool>(n));
+ break;
+ default: {
+ Mutex::Autolock lock(mStateLock);
+ reply->writeBool(mRefreshRateOverlay != nullptr);
+ }
}
return NO_ERROR;
}
case 1035: {
n = data.readInt32();
mDebugDisplayConfigSetByBackdoor = false;
- if (n >= 0) {
+ const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size();
+ if (n >= 0 && n < numConfigs) {
const auto displayToken = getInternalDisplayToken();
status_t result = setActiveConfig(displayToken, n);
if (result != NO_ERROR) {
@@ -5284,33 +5365,58 @@
void SurfaceFlinger::kernelTimerChanged(bool expired) {
static bool updateOverlay =
property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
- if (!updateOverlay || !mRefreshRateOverlay) return;
+ if (!updateOverlay) return;
+ if (Mutex::Autolock lock(mStateLock); !mRefreshRateOverlay) return;
// Update the overlay on the main thread to avoid race conditions with
// mRefreshRateConfigs->getCurrentRefreshRate()
- static_cast<void>(schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
- if (mRefreshRateOverlay) {
- const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
- const bool timerExpired = kernelTimerEnabled && expired;
- const auto& current = [this]() -> const RefreshRate& {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- if (mDesiredActiveConfigChanged) {
- return mRefreshRateConfigs->getRefreshRateFromConfigId(
- mDesiredActiveConfig.configId);
- }
+ static_cast<void>(schedule([=] {
+ const auto desiredActiveConfig = getDesiredActiveConfig();
+ const auto& current = desiredActiveConfig
+ ? mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId)
+ : mRefreshRateConfigs->getCurrentRefreshRate();
+ const auto& min = mRefreshRateConfigs->getMinRefreshRate();
- return mRefreshRateConfigs->getCurrentRefreshRate();
- }();
- const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+ if (current != min) {
+ const bool timerExpired = mKernelIdleTimerEnabled && expired;
- if (current != min) {
+ if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
- mEventQueue->invalidate();
}
+ mEventQueue->invalidate();
}
}));
}
+void SurfaceFlinger::toggleKernelIdleTimer() {
+ using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+ // If the support for kernel idle timer is disabled in SF code, don't do anything.
+ if (!mSupportKernelIdleTimer) {
+ return;
+ }
+ const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();
+
+ switch (action) {
+ case KernelIdleTimerAction::TurnOff:
+ if (mKernelIdleTimerEnabled) {
+ ATRACE_INT("KernelIdleTimer", 0);
+ base::SetProperty(KERNEL_IDLE_TIMER_PROP, "false");
+ mKernelIdleTimerEnabled = false;
+ }
+ break;
+ case KernelIdleTimerAction::TurnOn:
+ if (!mKernelIdleTimerEnabled) {
+ ATRACE_INT("KernelIdleTimer", 1);
+ base::SetProperty(KERNEL_IDLE_TIMER_PROP, "true");
+ mKernelIdleTimerEnabled = true;
+ }
+ break;
+ case KernelIdleTimerAction::NoChange:
+ break;
+ }
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -5340,27 +5446,33 @@
renderAreaRotation = ui::Transform::ROT_0;
}
- sp<DisplayDevice> display;
+ wp<DisplayDevice> displayWeak;
+ ui::LayerStack layerStack;
+ ui::Size reqSize(reqWidth, reqHeight);
{
Mutex::Autolock lock(mStateLock);
-
- display = getDisplayDeviceLocked(displayToken);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(displayToken);
if (!display) return NAME_NOT_FOUND;
+ displayWeak = display;
+ layerStack = display->getLayerStack();
// set the requested width/height to the logical display viewport size
// by default
if (reqWidth == 0 || reqHeight == 0) {
- reqWidth = uint32_t(display->getViewport().width());
- reqHeight = uint32_t(display->getViewport().height());
+ reqSize = display->getViewport().getSize();
}
}
- DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
- renderAreaRotation, captureSecureLayers);
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
- useIdentityTransform, outCapturedSecureLayers);
+ RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, sourceCrop, reqSize, reqDataspace,
+ renderAreaRotation, captureSecureLayers);
+ });
+
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, visitor);
+ };
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, outBuffer,
+ reqPixelFormat, useIdentityTransform, outCapturedSecureLayers);
}
static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
@@ -5395,7 +5507,7 @@
return NO_ERROR;
}
-const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
+sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
if (displayToken) {
return getDisplayDeviceLocked(displayToken);
@@ -5405,7 +5517,7 @@
return getDisplayByLayerStack(displayOrLayerStack);
}
-const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
+sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
for (const auto& [token, display] : mDisplays) {
if (display->getLayerStack() == layerStack) {
return display;
@@ -5416,19 +5528,20 @@
status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
sp<GraphicBuffer>* outBuffer) {
- sp<DisplayDevice> display;
- uint32_t width;
- uint32_t height;
+ ui::LayerStack layerStack;
+ wp<DisplayDevice> displayWeak;
+ ui::Size size;
ui::Transform::RotationFlags captureOrientation;
{
Mutex::Autolock lock(mStateLock);
- display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
if (!display) {
return NAME_NOT_FOUND;
}
+ layerStack = display->getLayerStack();
+ displayWeak = display;
- width = uint32_t(display->getViewport().width());
- height = uint32_t(display->getViewport().height());
+ size = display->getViewport().getSize();
const auto orientation = display->getOrientation();
captureOrientation = ui::Transform::toRotationFlags(orientation);
@@ -5454,14 +5567,19 @@
pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
}
- DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
- false /* captureSecureLayers */);
+ RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, Rect(), size, *outDataspace,
+ captureOrientation, false /* captureSecureLayers */);
+ });
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, visitor);
+ };
+
bool ignored = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
- false /* useIdentityTransform */,
+
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size, outBuffer,
+ ui::PixelFormat::RGBA_8888, false /* useIdentityTransform */,
ignored /* outCapturedSecureLayers */);
}
@@ -5472,88 +5590,7 @@
float frameScale, bool childrenOnly) {
ATRACE_CALL();
- class LayerRenderArea : public RenderArea {
- public:
- LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
- int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly, const Rect& displayViewport)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
- mLayer(layer),
- mCrop(crop),
- mNeedsFiltering(false),
- mFlinger(flinger),
- mChildrenOnly(childrenOnly) {}
- const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); }
- int getHeight() const override {
- return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
- }
- int getWidth() const override {
- return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
- }
- bool isSecure() const override { return false; }
- bool needsFiltering() const override { return mNeedsFiltering; }
- sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
- Rect getSourceCrop() const override {
- if (mCrop.isEmpty()) {
- return getBounds();
- } else {
- return mCrop;
- }
- }
- class ReparentForDrawing {
- public:
- const sp<Layer>& oldParent;
- const sp<Layer>& newParent;
-
- ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
- const Rect& drawingBounds)
- : oldParent(oldParent), newParent(newParent) {
- // Compute and cache the bounds for the new parent layer.
- newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
- 0.f /* shadowRadius */);
- oldParent->setChildrenDrawingParent(newParent);
- }
- ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
- };
-
- void render(std::function<void()> drawLayers) override {
- const Rect sourceCrop = getSourceCrop();
- // no need to check rotation because there is none
- mNeedsFiltering = sourceCrop.width() != getReqWidth() ||
- sourceCrop.height() != getReqHeight();
-
- if (!mChildrenOnly) {
- mTransform = mLayer->getTransform().inverse();
- drawLayers();
- } else {
- uint32_t w = static_cast<uint32_t>(getWidth());
- uint32_t h = static_cast<uint32_t>(getHeight());
- // In the "childrenOnly" case we reparent the children to a screenshot
- // layer which has no properties set and which does not draw.
- sp<ContainerLayer> screenshotParentLayer =
- mFlinger->getFactory().createContainerLayer({mFlinger, nullptr,
- "Screenshot Parent"s, w, h, 0,
- LayerMetadata()});
-
- ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
- drawLayers();
- }
- }
-
- private:
- const sp<Layer> mLayer;
- const Rect mCrop;
-
- ui::Transform mTransform;
- bool mNeedsFiltering;
-
- SurfaceFlinger* mFlinger;
- const bool mChildrenOnly;
- };
-
- int reqWidth = 0;
- int reqHeight = 0;
+ ui::Size reqSize;
sp<Layer> parent;
Rect crop(sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
@@ -5590,8 +5627,7 @@
// crop was not specified, or an invalid frame scale was provided.
return BAD_VALUE;
}
- reqWidth = crop.width() * frameScale;
- reqHeight = crop.height() * frameScale;
+ reqSize = ui::Size(crop.width() * frameScale, crop.height() * frameScale);
for (const auto& handle : excludeHandles) {
sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
@@ -5612,15 +5648,18 @@
} // mStateLock
// really small crop or frameScale
- if (reqWidth <= 0) {
- reqWidth = 1;
+ if (reqSize.width <= 0) {
+ reqSize.width = 1;
}
- if (reqHeight <= 0) {
- reqHeight = 1;
+ if (reqSize.height <= 0) {
+ reqSize.height = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
- displayViewport);
+ RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
+ return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, reqDataspace,
+ childrenOnly, displayViewport);
+ });
+
auto traverseLayers = [parent, childrenOnly,
&excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5643,14 +5682,14 @@
};
bool outCapturedSecureLayers = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
- outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, outBuffer,
+ reqPixelFormat, false, outCapturedSecureLayers);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer,
- const ui::PixelFormat reqPixelFormat,
+ ui::Size bufferSize, sp<GraphicBuffer>* outBuffer,
+ ui::PixelFormat reqPixelFormat,
bool useIdentityTransform,
bool& outCapturedSecureLayers) {
ATRACE_CALL();
@@ -5658,16 +5697,16 @@
// TODO(b/116112787) Make buffer usage a parameter.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- *outBuffer =
- getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- static_cast<android_pixel_format>(reqPixelFormat), 1,
- usage, "screenshot");
+ *outBuffer = getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1, usage, "screenshot");
- return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
- false /* regionSampling */, outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, *outBuffer,
+ useIdentityTransform, false /* regionSampling */,
+ outCapturedSecureLayers);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
const sp<GraphicBuffer>& buffer,
bool useIdentityTransform, bool regionSampling,
@@ -5680,23 +5719,28 @@
do {
std::tie(result, syncFd) =
- schedule([&] {
+ schedule([&]() -> std::pair<status_t, int> {
if (mRefreshPending) {
- ATRACE_NAME("Skipping screenshot for now");
- return std::make_pair(EAGAIN, -1);
+ ALOGW("Skipping screenshot for now");
+ return {EAGAIN, -1};
+ }
+ std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ return {NO_MEMORY, -1};
}
status_t result = NO_ERROR;
int fd = -1;
Mutex::Autolock lock(mStateLock);
- renderArea.render([&] {
- result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
+ renderArea->render([&] {
+ result = captureScreenImplLocked(*renderArea, traverseLayers, buffer.get(),
useIdentityTransform, forSystem, &fd,
regionSampling, outCapturedSecureLayers);
});
- return std::make_pair(result, fd);
+ return {result, fd};
}).get();
} while (result == EAGAIN);
@@ -5710,8 +5754,9 @@
void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool regionSampling, int* outSyncFd) {
+ const sp<GraphicBuffer>& buffer,
+ bool useIdentityTransform, bool regionSampling,
+ int* outSyncFd) {
ATRACE_CALL();
const auto reqWidth = renderArea.getReqWidth();
@@ -5743,6 +5788,7 @@
fillLayer.alpha = half(alpha);
clientCompositionLayers.push_back(fillLayer);
+ const auto display = renderArea.getDisplayDevice();
std::vector<Layer*> renderedLayers;
Region clearRegion = Region::INVALID_REGION;
traverseLayers([&](Layer* layer) {
@@ -5751,7 +5797,7 @@
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
useIdentityTransform,
- layer->needsFilteringForScreenshots(renderArea.getDisplayDevice(), transform) ||
+ layer->needsFilteringForScreenshots(display.get(), transform) ||
renderArea.needsFiltering(),
renderArea.isSecure(),
supportProtectedContent,
@@ -5808,7 +5854,7 @@
status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer,
+ const sp<GraphicBuffer>& buffer,
bool useIdentityTransform, bool forSystem,
int* outSyncFd, bool regionSampling,
bool& outCapturedSecureLayers) {
@@ -5853,17 +5899,17 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack,
+ const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(layerStack, false)) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(layerStack, false)) {
return;
}
if (!layer->isVisible()) {
@@ -5909,8 +5955,8 @@
const nsecs_t vsyncPeriod = getHwComposer()
.getConfigs(*displayId)[policy->defaultConfig.value()]
->getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
- policy->defaultConfig, vsyncPeriod);
+ mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ policy->defaultConfig, vsyncPeriod);
return NO_ERROR;
}
@@ -5936,14 +5982,14 @@
currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
currentPolicy.appRequestRange.max);
- // TODO(b/140204874): This hack triggers a notification that something has changed, so
- // that listeners that care about a change in allowed configs can get the notification.
- // Giving current ActiveConfig so that most other listeners would just drop the event
+ // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
+ // be depending in this callback.
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
.getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
- display->getActiveConfig(), vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ display->getActiveConfig(), vsyncPeriod);
+ toggleKernelIdleTimer();
auto configId = mScheduler->getPreferredConfigId();
auto& preferredRefreshRate = configId
@@ -5979,7 +6025,7 @@
}
auto future = schedule([=]() -> status_t {
- const auto display = getDisplayDeviceLocked(displayToken);
+ const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set desired display configs for invalid display token %p",
displayToken.get());
@@ -6045,10 +6091,6 @@
}
}
-void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
- mFlinger->setInputWindowsFinished();
-}
-
wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
Mutex::Autolock _l(mStateLock);
return fromHandleLocked(handle);
@@ -6134,7 +6176,7 @@
return BAD_VALUE;
}
- static_cast<void>(schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
+ static_cast<void>(schedule([=] {
Mutex::Autolock lock(mStateLock);
if (authenticateSurfaceTextureLocked(surface)) {
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
@@ -6163,8 +6205,7 @@
sp<IBinder> token;
if (mFrameRateFlexibilityTokenCount == 0) {
- // |mStateLock| not needed as we are on the main thread
- const auto display = getDefaultDisplayDeviceLocked();
+ const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
// This is a little racy, but not in a way that hurts anything. As we grab the
// defaultConfig from the display manager policy, we could be setting a new display
@@ -6214,8 +6255,7 @@
mFrameRateFlexibilityTokenCount--;
ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
if (mFrameRateFlexibilityTokenCount == 0) {
- // |mStateLock| not needed as we are on the main thread
- const auto display = getDefaultDisplayDeviceLocked();
+ const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
constexpr bool kOverridePolicy = true;
status_t result = setDesiredDisplayConfigSpecsInternal(display, {}, kOverridePolicy);
LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
@@ -6223,6 +6263,29 @@
}));
}
+void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
+ static_cast<void>(schedule([=] {
+ std::unique_ptr<RefreshRateOverlay> overlay;
+ if (enable) {
+ overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner);
+ }
+
+ {
+ Mutex::Autolock lock(mStateLock);
+
+ // Destroy the layer of the current overlay, if any, outside the lock.
+ mRefreshRateOverlay.swap(overlay);
+ if (!mRefreshRateOverlay) return;
+
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ mRefreshRateOverlay->setViewport(display->getSize());
+ }
+
+ mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+ }
+ }));
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f3984ed..d56321d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -33,7 +33,6 @@
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
#include <gui/OccupancyTracker.h>
-#include <input/ISetInputWindowsListener.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
#include <renderengine/LayerSettings.h>
@@ -89,15 +88,20 @@
class Client;
class EventThread;
class HWComposer;
+struct SetInputWindowsListener;
class IGraphicBufferProducer;
-class IInputFlinger;
class Layer;
class MessageBase;
class RefreshRateOverlay;
class RegionSamplingThread;
+class RenderArea;
class TimeStats;
class FrameTracer;
+namespace os {
+ class IInputFlinger;
+}
+
namespace compositionengine {
class DisplaySurface;
class OutputLayer;
@@ -117,7 +121,7 @@
eTransactionNeeded = 0x01,
eTraversalNeeded = 0x02,
eDisplayTransactionNeeded = 0x04,
- eDisplayLayerStackChanged = 0x08,
+ eTransformHintUpdateNeeded = 0x08,
eTransactionFlushNeeded = 0x10,
eTransactionMask = 0x1f,
};
@@ -289,12 +293,6 @@
// The CompositionEngine encapsulates all composition related interfaces and actions.
compositionengine::CompositionEngine& getCompositionEngine() const;
- // returns the default Display
- sp<const DisplayDevice> getDefaultDisplayDevice() {
- Mutex::Autolock _l(mStateLock);
- return getDefaultDisplayDeviceLocked();
- }
-
// Obtains a name from the texture pool, or, if the pool is empty, posts a
// synchronous message to the main thread to obtain one on the fly
uint32_t getNewTexture();
@@ -307,8 +305,7 @@
void setPrimaryVsyncEnabled(bool enabled);
// main thread function to enable/disable h/w composer event
- void setPrimaryVsyncEnabledInternal(bool enabled);
- void setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled);
+ void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
// called on the main thread by MessageQueue when an internal message
// is received
@@ -342,6 +339,7 @@
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
bool mDisableClientCompositionCache = false;
+ void setInputWindowsFinished();
private:
friend class BufferLayer;
@@ -472,7 +470,7 @@
HdrCapabilities* outCapabilities) const override;
status_t enableVSyncInjections(bool enable) override;
status_t injectVSync(nsecs_t when) override;
- status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const override;
+ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
status_t getColorManagement(bool* outGetColorManagement) const override;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
ui::Dataspace* outWideColorGamutDataspace,
@@ -505,7 +503,7 @@
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
- status_t notifyPowerHint(int32_t hintId) override;
+ status_t notifyPowerBoost(int32_t boostId) override;
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
@@ -542,6 +540,15 @@
void repaintEverythingForHWC() override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
+ // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
+ void toggleKernelIdleTimer();
+ // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
+ // make calls to sys prop each time.
+ bool mKernelIdleTimerEnabled = false;
+ // Keeps track of whether the kernel timer is supported on the SF side.
+ bool mSupportKernelIdleTimer = false;
+ // Show spinner with refresh rate overlay
+ bool mRefreshRateOverlaySpinner = false;
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -605,7 +612,6 @@
void updateInputFlinger();
void updateInputWindowInfo();
void commitInputWindowCommands() REQUIRES(mStateLock);
- void setInputWindowsFinished();
void updateCursorAsync();
void initScheduler(DisplayId primaryDisplayId);
@@ -634,6 +640,12 @@
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
+ // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
+ // where there are still pending transactions but we know they won't be ready until a frame
+ // arrives from a different layer. So we need to ensure we performTransaction from invalidate
+ // but there is no need to try and wake up immediately to do it. Rather we rely on
+ // onFrameAvailable or another layer update to wake us up.
+ void setTraversalNeeded();
uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
@@ -675,8 +687,7 @@
status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* outHandle, uint32_t* outTransformHint,
- sp<Layer>* outLayer);
+ sp<IBinder>* outHandle, sp<Layer>* outLayer);
status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
@@ -701,7 +712,7 @@
status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
- bool addToCurrentState);
+ bool addToCurrentState, uint32_t* outTransformHint);
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
@@ -713,25 +724,25 @@
void startBootAnim();
using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool regionSampling, int* outSyncFd);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+ sp<GraphicBuffer>* outBuffer, ui::PixelFormat,
bool useIdentityTransform, bool& outCapturedSecureLayers);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
- bool regionSampling, bool& outCapturedSecureLayers);
- const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
- const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&,
+ bool useIdentityTransform, bool regionSampling,
+ bool& outCapturedSecureLayers);
+ sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
+ sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool forSystem, int* outSyncFd, bool regionSampling,
bool& outCapturedSecureLayers);
- void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor);
+ void traverseLayersInLayerStack(ui::LayerStack, const LayerVector::Visitor&);
sp<StartPropertySetThread> mStartPropertySetThread;
@@ -752,29 +763,31 @@
// called when starting, or restarting after system_server death
void initializeDisplays();
- // NOTE: can only be called from the main thread or with mStateLock held
- sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const {
+ sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
+ REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken);
}
- // NOTE: can only be called from the main thread or with mStateLock held
- sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) {
+ sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) REQUIRES(mStateLock) {
const auto it = mDisplays.find(displayToken);
return it == mDisplays.end() ? nullptr : it->second;
}
- sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const {
+ sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
}
- sp<DisplayDevice> getDefaultDisplayDeviceLocked() {
+ sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
if (const auto token = getInternalDisplayTokenLocked()) {
return getDisplayDeviceLocked(token);
}
return nullptr;
}
- std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
+ sp<const DisplayDevice> getDefaultDisplayDevice() EXCLUDES(mStateLock) {
+ Mutex::Autolock lock(mStateLock);
+ return getDefaultDisplayDeviceLocked();
+ }
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
@@ -828,14 +841,15 @@
std::shared_ptr<compositionengine::Display> compositionDisplay,
const DisplayDeviceState& state,
const sp<compositionengine::DisplaySurface>& displaySurface,
- const sp<IGraphicBufferProducer>& producer);
- void processDisplayChangesLocked();
- void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state);
- void processDisplayRemoved(const wp<IBinder>& displayToken);
+ const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
+ void processDisplayChangesLocked() REQUIRES(mStateLock);
+ void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
+ REQUIRES(mStateLock);
+ void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
void processDisplayChanged(const wp<IBinder>& displayToken,
const DisplayDeviceState& currentState,
- const DisplayDeviceState& drawingState);
- void processDisplayHotplugEventsLocked();
+ const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
+ void processDisplayHotplugEventsLocked() REQUIRES(mStateLock);
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
@@ -874,12 +888,13 @@
/*
* Display identification
*/
- sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const {
+ sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const REQUIRES(mStateLock) {
const auto it = mPhysicalDisplayTokens.find(displayId);
return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
}
- std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const {
+ std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
+ REQUIRES(mStateLock) {
for (const auto& [id, token] : mPhysicalDisplayTokens) {
if (token == displayToken) {
return id;
@@ -889,12 +904,12 @@
}
// TODO(b/74619554): Remove special cases for primary display.
- sp<IBinder> getInternalDisplayTokenLocked() const {
+ sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
const auto displayId = getInternalDisplayIdLocked();
return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
}
- std::optional<DisplayId> getInternalDisplayIdLocked() const {
+ std::optional<DisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
}
@@ -946,9 +961,9 @@
void recordBufferingStats(const std::string& layerName,
std::vector<OccupancyTracker::Segment>&& history);
void dumpBufferingStats(std::string& result) const;
- void dumpDisplayIdentificationData(std::string& result) const;
+ void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
- void dumpWideColorInfo(std::string& result) const;
+ void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
@@ -975,7 +990,7 @@
/* ------------------------------------------------------------------------
* VrFlinger
*/
- void resetDisplayState();
+ void resetDisplayState() REQUIRES(mStateLock);
// Check to see if we should handoff to vr flinger.
void updateVrFlinger();
@@ -996,7 +1011,7 @@
bool mTransactionPending = false;
bool mAnimTransactionPending = false;
SortedVector<sp<Layer>> mLayersPendingRemoval;
- bool mTraversalNeededMainThread = false;
+ bool mForceTraversal = false;
// global color transform states
Daltonizer mDaltonizer;
@@ -1006,6 +1021,10 @@
// Can't be unordered_set because wp<> isn't hashable
std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+ // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
+ // this threshold, then begin logging.
+ size_t mGraphicBufferProducerListSizeLogThreshold =
+ static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
void removeGraphicBufferProducerAsync(const wp<IBinder>&);
@@ -1054,16 +1073,14 @@
hal::HWDisplayId hwcDisplayId;
hal::Connection connection = hal::Connection::INVALID;
};
- // protected by mStateLock
- std::vector<HotplugEvent> mPendingHotplugEvents;
+ std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
- std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
- std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
+ std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+ std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens GUARDED_BY(mStateLock);
- // protected by mStateLock
- std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken;
+ std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
// don't use a lock for these, we don't care
int mDebugRegion = 0;
@@ -1071,7 +1088,6 @@
bool mDebugDisableTransformHint = false;
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
- bool mPropagateBackpressure = true;
bool mPropagateBackpressureClientComposition = false;
std::unique_ptr<SurfaceInterceptor> mInterceptor;
@@ -1206,6 +1222,7 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
std::atomic<nsecs_t> mExpectedPresentTime = 0;
+ hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
/* ------------------------------------------------------------------------
* Generic Layer Metadata
@@ -1216,6 +1233,12 @@
* Misc
*/
+ std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
+ return std::nullopt;
+ }
+
std::mutex mActiveConfigLock;
// This bit is set once we start setting the config. We read from this bit during the
// process. If at the end, this bit is different than mDesiredActiveConfig, we restart
@@ -1236,21 +1259,12 @@
const float mInternalDisplayDensity;
const float mEmulatedDisplayDensity;
- sp<IInputFlinger> mInputFlinger;
+ sp<os::IInputFlinger> mInputFlinger;
InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
// Should only be accessed by the main thread.
InputWindowCommands mInputWindowCommands;
- struct SetInputWindowsListener : BnSetInputWindowsListener {
- explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger)
- : mFlinger(std::move(flinger)) {}
-
- void onSetInputWindowsFinished() override;
-
- const sp<SurfaceFlinger> mFlinger;
- };
-
- const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
@@ -1258,7 +1272,8 @@
// This should only be accessed on the main thread.
nsecs_t mFrameStartTime = 0;
- std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+ void enableRefreshRateOverlay(bool enable);
+ std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay GUARDED_BY(mStateLock);
// Flag used to set override allowed display configs from backdoor
bool mDebugDisplayConfigSetByBackdoor = false;
@@ -1269,14 +1284,12 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- // Flags to capture the state of Vsync in HWC
- hal::Vsync mHWCVsyncState = hal::Vsync::DISABLE;
- hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
-
// Fields tracking the current jank event: when it started and how many
// janky frames there are.
nsecs_t mMissedFrameJankStart = 0;
int32_t mMissedFrameJankCount = 0;
+ // Positive if jank should be uploaded in postComposition
+ nsecs_t mLastJankDuration = -1;
int mFrameRateFlexibilityTokenCount = 0;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index c90b1b8..894ee6d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -87,7 +87,9 @@
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
- StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
+ const float averageTime = iter->second.averageTime();
+ const float averageFPS = averageTime < 1.0f ? 0.0f : 1000.0f / averageTime;
+ StringAppendF(&result, "averageFPS = %.3f\n", averageFPS);
}
for (const auto& ele : deltas) {
StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 0cdff8f..ca24493 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -154,6 +154,9 @@
status_t TransactionCompletedThread::finalizePendingCallbackHandles(
const std::deque<sp<CallbackHandle>>& handles) {
+ if (handles.empty()) {
+ return NO_ERROR;
+ }
std::lock_guard lock(mMutex);
if (!mRunning) {
ALOGE("cannot add presented callback handle because the callback thread isn't running");
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 7f1f542..8458d54 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -196,12 +196,13 @@
bool has_wallpaper = 9;
float global_scale_factor = 10;
- float window_x_scale = 11;
- float window_y_scale = 12;
+ float window_x_scale = 11 [deprecated=true];
+ float window_y_scale = 12 [deprecated=true];
uint32 crop_layer_id = 13;
bool replace_touchable_region_with_crop = 14;
RectProto touchable_region_crop = 15;
+ TransformProto transform = 16;
}
message ColorTransformProto {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index cfc301b..7b1f0fb 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -328,7 +328,7 @@
prop {
api_name: "refresh_rate_switching"
type: Boolean
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.refresh_rate_switching"
deprecated: true
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 94ebe89..c96dfc7 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -21,7 +21,9 @@
"CommonTypes_test.cpp",
"Credentials_test.cpp",
"DereferenceSurfaceControl_test.cpp",
+ "DetachChildren_test.cpp",
"DisplayConfigs_test.cpp",
+ "EffectLayer_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
diff --git a/services/surfaceflinger/tests/DetachChildren_test.cpp b/services/surfaceflinger/tests/DetachChildren_test.cpp
new file mode 100644
index 0000000..b6c2fe2
--- /dev/null
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class DetachChildren : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+
+ mMainSurface = createLayer(String8("Main Test Surface"), mMainSurfaceBounds.width(),
+ mMainSurfaceBounds.height(), 0, mBlackBgSurface.get());
+
+ ASSERT_TRUE(mMainSurface != nullptr);
+ ASSERT_TRUE(mMainSurface->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(mMainSurface, mMainSurfaceColor);
+
+ asTransaction([&](Transaction& t) {
+ t.setLayer(mMainSurface, INT32_MAX - 1)
+ .setPosition(mMainSurface, mMainSurfaceBounds.left, mMainSurfaceBounds.top)
+ .show(mMainSurface);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mMainSurface = 0;
+ }
+
+ sp<SurfaceControl> mMainSurface;
+ Color mMainSurfaceColor = {195, 63, 63, 255};
+ Rect mMainSurfaceBounds = Rect(64, 64, 128, 128);
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(DetachChildren, RelativesAreNotDetached) {
+ Color relativeColor = {10, 10, 10, 255};
+ Rect relBounds = Rect(64, 64, 74, 74);
+
+ sp<SurfaceControl> relative =
+ createLayer(String8("relativeTestSurface"), relBounds.width(), relBounds.height(), 0);
+ TransactionUtils::fillSurfaceRGBA8(relative, relativeColor);
+
+ Transaction{}
+ .setRelativeLayer(relative, mMainSurface->getHandle(), 1)
+ .setPosition(relative, relBounds.left, relBounds.top)
+ .apply();
+
+ {
+ // The relative should be on top of the FG control.
+ mCapture = screenshot();
+ mCapture->expectColor(relBounds, relativeColor);
+ }
+ Transaction{}.detachChildren(mMainSurface).apply();
+
+ {
+ // Nothing should change at this point.
+ mCapture = screenshot();
+ mCapture->expectColor(relBounds, relativeColor);
+ }
+
+ Transaction{}.hide(relative).apply();
+
+ {
+ // Ensure that the relative was actually hidden, rather than
+ // being left in the detached but visible state.
+ mCapture = screenshot();
+ mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenSameClient) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 84, 84);
+ sp<SurfaceControl> child = createLayer(String8("Child surface"), childBounds.width(),
+ childBounds.height(), 0, mMainSurface.get());
+ ASSERT_TRUE(child->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(child, childColor);
+
+ asTransaction([&](Transaction& t) {
+ t.show(child);
+ t.setPosition(child, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top);
+ });
+
+ {
+ mCapture = screenshot();
+ // Expect main color around the child surface
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); });
+
+ asTransaction([&](Transaction& t) { t.hide(child); });
+
+ // Since the child has the same client as the parent, it will not get
+ // detached and will be hidden.
+ {
+ mCapture = screenshot();
+ mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenDifferentClient) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 84, 84);
+
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+ childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+ ASSERT_TRUE(childNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+ asTransaction([&](Transaction& t) {
+ t.show(childNewClient);
+ t.setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top);
+ });
+
+ {
+ mCapture = screenshot();
+ // Expect main color around the child surface
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); });
+
+ asTransaction([&](Transaction& t) { t.hide(childNewClient); });
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenThenAttach) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 84, 84);
+
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+ childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+ ASSERT_TRUE(childNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+ Transaction()
+ .show(childNewClient)
+ .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ // Expect main color around the child surface
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ Transaction().detachChildren(mMainSurface).apply();
+ Transaction().hide(childNewClient).apply();
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ Color newParentColor = Color::RED;
+ Rect newParentBounds = Rect(20, 20, 52, 52);
+ sp<SurfaceControl> newParentSurface =
+ createLayer(String8("New Parent Surface"), newParentBounds.width(),
+ newParentBounds.height(), 0);
+ TransactionUtils::fillSurfaceRGBA8(newParentSurface, newParentColor);
+ Transaction()
+ .setLayer(newParentSurface, INT32_MAX - 1)
+ .show(newParentSurface)
+ .setPosition(newParentSurface, newParentBounds.left, newParentBounds.top)
+ .reparent(childNewClient, newParentSurface->getHandle())
+ .apply();
+ {
+ mCapture = screenshot();
+ // Child is now hidden.
+ mCapture->expectColor(newParentBounds, newParentColor);
+ }
+}
+
+TEST_F(DetachChildren, DetachChildrenWithDeferredTransaction) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 84, 84);
+
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+ childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+ ASSERT_TRUE(childNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+ Transaction()
+ .show(childNewClient)
+ .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ Transaction()
+ .deferTransactionUntil_legacy(childNewClient, mMainSurface->getHandle(),
+ mMainSurface->getSurface()->getNextFrameNumber())
+ .apply();
+ Transaction().detachChildren(mMainSurface).apply();
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED,
+ mMainSurfaceBounds.width(),
+ mMainSurfaceBounds.height()));
+
+ // BufferLayer can still dequeue buffers even though there's a detached layer with a
+ // deferred transaction.
+ {
+ SCOPED_TRACE("new buffer");
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, Color::RED);
+ mCapture->expectColor(childBounds, childColor);
+ }
+}
+
+TEST_F(DetachChildren, ReparentParentLayerOfDetachedChildren) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 94, 94);
+ Color grandchildColor = Color::RED;
+ Rect grandchildBounds = Rect(80, 80, 90, 90);
+
+ sp<SurfaceComposerClient> newClient1 = new SurfaceComposerClient;
+ sp<SurfaceComposerClient> newClient2 = new SurfaceComposerClient;
+
+ sp<SurfaceControl> childSurface =
+ createSurface(newClient1, "Child surface", childBounds.width(), childBounds.height(),
+ PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+ sp<SurfaceControl> grandchildSurface =
+ createSurface(newClient2, "Grandchild Surface", grandchildBounds.width(),
+ grandchildBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, childSurface.get());
+
+ TransactionUtils::fillSurfaceRGBA8(childSurface, childColor);
+ TransactionUtils::fillSurfaceRGBA8(grandchildSurface, grandchildColor);
+
+ Transaction()
+ .show(childSurface)
+ .show(grandchildSurface)
+ .setPosition(childSurface, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top)
+ .setPosition(grandchildSurface, grandchildBounds.left - childBounds.left,
+ grandchildBounds.top - childBounds.top)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectBorder(grandchildBounds, childColor);
+ mCapture->expectColor(grandchildBounds, grandchildColor);
+ }
+
+ Transaction().detachChildren(childSurface).apply();
+
+ // Remove main surface offscreen
+ Transaction().reparent(mMainSurface, nullptr).apply();
+ {
+ mCapture = screenshot();
+ mCapture->expectColor(mMainSurfaceBounds, Color::BLACK);
+ }
+
+ Transaction().reparent(mMainSurface, mBlackBgSurface->getHandle()).apply();
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectBorder(grandchildBounds, childColor);
+ mCapture->expectColor(grandchildBounds, grandchildColor);
+ }
+
+ Transaction().hide(grandchildSurface).apply();
+
+ // grandchild is still detached so it will not hide
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectBorder(grandchildBounds, childColor);
+ mCapture->expectColor(grandchildBounds, grandchildColor);
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
new file mode 100644
index 0000000..3dca391
--- /dev/null
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class EffectLayerTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ mParentLayer = createColorLayer("Parent layer", Color::RED);
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+ t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+ t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mParentLayer = 0;
+ }
+
+ sp<SurfaceControl> mParentLayer;
+};
+
+TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) {
+ sp<SurfaceControl> effectLayer =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
+ mParentLayer.get());
+
+ EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.show(effectLayer);
+ });
+
+ {
+ SCOPED_TRACE("Default effect Layer has solid black fill");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 400, 400), Color::BLACK);
+ }
+}
+
+TEST_F(EffectLayerTest, EffectLayerWithNoFill) {
+ sp<SurfaceControl> effectLayer =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ mParentLayer.get());
+
+ EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.show(effectLayer);
+ });
+
+ {
+ SCOPED_TRACE("Effect layer with nofill option is transparent");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 400, 400), Color::RED);
+ }
+}
+
+TEST_F(EffectLayerTest, EffectLayerCanSetColor) {
+ sp<SurfaceControl> effectLayer =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ mParentLayer.get());
+
+ EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setColor(effectLayer,
+ half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
+ Color::GREEN.b / 255.0f});
+ t.show(effectLayer);
+ });
+
+ {
+ SCOPED_TRACE("Effect Layer can set color");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 400, 400), Color::GREEN);
+ }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index d666d7e..7d4314f 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -220,6 +220,51 @@
shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+ // Solid center
+ shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+ size / 2 + testArea / 2, size / 2 + testArea / 2),
+ Color::RED);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ auto transaction = Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop_legacy(parent, Rect(0, 0, size, size))
+ .reparent(child, parent->getHandle())
+ .setPosition(child, 0, size)
+ // Rotate by half PI
+ .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
+ if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+ transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+ } else {
+ transaction.setFrame(parent, Rect(0, 0, size, size));
+ }
+ transaction.apply();
+
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Edges are transparent
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom - testArea), Color::BLACK);
+ shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+ // Solid center
+ shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+ size / 2 + testArea / 2, size / 2 + testArea / 2),
+ Color::GREEN);
}
}
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index cdd9d92..84823f5 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -103,41 +103,6 @@
sp<SurfaceControl> mSyncSurfaceControl;
};
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
- std::unique_ptr<ScreenCapture> sc;
-
- sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
- TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
- waitForPostedBuffers();
-
- Transaction{}
- .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
- .setPosition(relative, 64, 64)
- .apply();
-
- {
- // The relative should be on top of the FG control.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
- Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
- {
- // Nothing should change at this point.
- ScreenCapture::captureScreen(&sc);
- sc->checkPixel(64, 64, 10, 10, 10);
- }
-
- Transaction{}.hide(relative).apply();
-
- {
- // Ensure that the relative was actually hidden, rather than
- // being left in the detached but visible state.
- ScreenCapture::captureScreen(&sc);
- sc->expectFGColor(64, 64);
- }
-}
-
class GeometryLatchingTest : public LayerUpdateTest {
protected:
void EXPECT_INITIAL_STATE(const char* trace) {
@@ -588,174 +553,6 @@
}
}
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // 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);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChild); });
-
- // Since the child has the same client as the parent, it will not get
- // detached and will be hidden.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectFGColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
- sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> mChildNewClient =
- createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(mChildNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
- asTransaction([&](Transaction& t) {
- t.hide(mChild);
- t.show(mChildNewClient);
- t.setPosition(mChildNewClient, 10, 10);
- t.setPosition(mFGSurfaceControl, 64, 64);
- });
-
- {
- mCapture = screenshot();
- // 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);
- }
-
- asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
- asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- // 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);
- }
-
- Transaction().detachChildren(mFGSurfaceControl).apply();
- Transaction().hide(childNewClient).apply();
-
- // Nothing should have changed.
- {
- mCapture = screenshot();
- mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
- mCapture->expectFGColor(84, 84);
- }
-
- sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
- fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
- 32);
- Transaction()
- .setLayer(newParentSurface, INT32_MAX - 1)
- .show(newParentSurface)
- .setPosition(newParentSurface, 20, 20)
- .reparent(childNewClient, newParentSurface->getHandle())
- .apply();
- {
- mCapture = screenshot();
- // Child is now hidden.
- mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
- }
-}
-TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
-
- TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
- Transaction()
- .hide(mChild)
- .show(childNewClient)
- .setPosition(childNewClient, 10, 10)
- .setPosition(mFGSurfaceControl, 64, 64)
- .apply();
-
- {
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color{195, 63, 63, 255});
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-
- Transaction()
- .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber())
- .apply();
- Transaction().detachChildren(mFGSurfaceControl).apply();
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
- // BufferLayer can still dequeue buffers even though there's a detached layer with a
- // deferred transaction.
- {
- SCOPED_TRACE("new buffer");
- mCapture = screenshot();
- Rect rect = Rect(74, 74, 84, 84);
- mCapture->expectBorder(rect, Color::RED);
- mCapture->expectColor(rect, Color{200, 200, 200, 255});
- }
-}
-
TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
asTransaction([&](Transaction& t) {
t.show(mChild);
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 1180cac..3e0b3c6 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -207,6 +207,71 @@
sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
}
}
+
+// Preserve the relative z order when a layer is reparented to a layer that's already offscreen
+TEST_F(RelativeZTest, LayerWithRelativeReparentedToOffscreen) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ Color testLayerColor = {255, 100, 0, 255};
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1a (testLayerColor) (relative to child level 2b)
+ // child level 1b (WHITE)
+ // child level 2a (BLUE)
+ // child level 2b (BLACK)
+ sp<SurfaceControl> childLevel1a =
+ createColorLayer("child level 1a", testLayerColor, mForegroundLayer.get());
+ sp<SurfaceControl> childLevel1b =
+ createColorLayer("child level 1b", Color::WHITE, mForegroundLayer.get());
+ sp<SurfaceControl> childLevel2a =
+ createColorLayer("child level 2a", Color::BLUE, childLevel1b.get());
+ sp<SurfaceControl> childLevel2b =
+ createColorLayer("child level 2b", Color::BLACK, childLevel1b.get());
+
+ Transaction{}
+ .setRelativeLayer(childLevel1a, childLevel2b->getHandle(), 1)
+ .show(childLevel1a)
+ .show(childLevel1b)
+ .show(childLevel2a)
+ .show(childLevel2b)
+ .apply();
+
+ {
+ // The childLevel1a should be in front of childLevel2b.
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), testLayerColor);
+ }
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1a (testLayerColor) (relative to child level 2b)
+ Transaction{}.reparent(childLevel1b, nullptr).apply();
+
+ // // Background layer (RED)
+ // // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLevel1a, childLevel2a->getHandle()).apply();
+
+ {
+ // The childLevel1a and childLevel1b are no longer on screen
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::GREEN);
+ }
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1b (WHITE)
+ // child level 2a (BLUE)
+ // child level 1a (testLayerColor) (relative to child level 2b)
+ // child level 2b (BLACK)
+ Transaction{}.reparent(childLevel1b, mForegroundLayer->getHandle()).apply();
+
+ {
+ // Nothing should change at this point since relative z info was preserved.
+ ScreenCapture::captureScreen(&sc);
+ sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), testLayerColor);
+ }
+}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
index 96a7541..1cea25a 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -29,8 +29,8 @@
#include "SurfaceFlinger.h" // Get the name of the service...
#include <binder/IServiceManager.h>
-
#include <cutils/properties.h>
+#include <hidl/ServiceManagement.h>
#include <iomanip>
#include <thread>
@@ -173,7 +173,7 @@
property_set("debug.sf.hwc_service_name", "mock");
// This allows tests/SF to register/load a HIDL service not listed in manifest files.
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::details::setTrebleTestingOverride(true);
property_set("debug.sf.treble_testing_override", "true");
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7574ff1..3c4a791 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -62,6 +62,7 @@
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
+ "VSyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 8713b2b..9add6a5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -35,6 +35,7 @@
#include <utils/String8.h>
#include "BufferQueueLayer.h"
+#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
@@ -149,9 +150,14 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+ constexpr bool kHasMultipleConfigs = true;
mFlinger.setupScheduler(std::move(primaryDispSync),
std::make_unique<mock::EventControlThread>(),
- std::move(eventThread), std::move(sfEventThread));
+ std::move(eventThread), std::move(sfEventThread),
+ kHasMultipleConfigs);
+
+ // Layer history should be created if there are multiple configs.
+ ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
@@ -233,24 +239,24 @@
constexpr bool forSystem = true;
constexpr bool regionSampling = false;
- DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
- ui::Transform::ROT_0);
+ auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
+ ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
- return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
+ return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(), visitor);
};
// TODO: Eliminate expensive/real allocation if possible.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+ mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(),
HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
int fd = -1;
status_t result =
- mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
- useIdentityTransform, forSystem, &fd, regionSampling);
+ mFlinger.captureScreenImplLocked(*renderArea, traverseLayers,
+ mCaptureScreenBuffer.get(), useIdentityTransform,
+ forSystem, &fd, regionSampling);
if (fd >= 0) {
close(fd);
}
@@ -348,7 +354,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -397,7 +403,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -648,7 +654,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -697,7 +703,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -775,7 +781,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -1073,7 +1079,8 @@
struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
- const auto outputLayer = layer->findOutputLayerForDisplay(test->mDisplay);
+ const auto outputLayer =
+ TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay);
LOG_FATAL_IF(!outputLayer);
outputLayer->editState().forceClientComposition = true;
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 2a0e913..cc6a60c 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -22,6 +22,8 @@
#include "DisplayHardware/DisplayIdentification.h"
+using ::testing::ElementsAre;
+
namespace android {
namespace {
@@ -312,86 +314,85 @@
using ManufactureYear = DeviceProductInfo::ManufactureYear;
using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
using ModelYear = DeviceProductInfo::ModelYear;
- using RelativeAddress = DeviceProductInfo::RelativeAddress;
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("", info.name.data());
+ EXPECT_EQ("", info.name);
EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("12610", info.productId.data());
+ EXPECT_EQ("12610", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("HP ZR30w", info.name.data());
+ EXPECT_EQ("HP ZR30w", info.name);
EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
- EXPECT_STREQ("10348", info.productId.data());
+ EXPECT_EQ("10348", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2012, date.year);
EXPECT_EQ(2, date.week);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("SAMSUNG", info.name.data());
+ EXPECT_EQ("SAMSUNG", info.name);
EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
- EXPECT_STREQ("2302", info.productId.data());
+ EXPECT_EQ("2302", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2011, date.year);
EXPECT_EQ(41, date.week);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Panasonic-TV", info.name.data());
+ EXPECT_EQ("Panasonic-TV", info.name);
EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
- EXPECT_STREQ("41622", info.productId.data());
+ EXPECT_EQ("41622", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Hisense", info.name.data());
+ EXPECT_EQ("Hisense", info.name);
EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("0", info.productId.data());
+ EXPECT_EQ("0", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
EXPECT_EQ(18, date.week);
- EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("LP2361", info.name.data());
+ EXPECT_EQ("LP2361", info.name);
EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
- EXPECT_STREQ("9373", info.productId.data());
+ EXPECT_EQ("9373", info.productId);
ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index ce5f35c..9130b04 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -23,6 +23,7 @@
#include <type_traits>
+#include <android/hardware/power/Boost.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/impl/Display.h>
@@ -57,6 +58,8 @@
namespace hal = android::hardware::graphics::composer::hal;
+using android::hardware::power::Boost;
+
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
@@ -1347,6 +1350,30 @@
}
/* ------------------------------------------------------------------------
+ * SurfaceFlinger::notifyPowerBoost
+ */
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ mFlinger.scheduler()->replaceTouchTimer(100);
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+ std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+/* ------------------------------------------------------------------------
* DisplayDevice::GetBestColorMode
*/
class GetBestColorModeTest : public DisplayTransactionTest {
@@ -1794,7 +1821,7 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- state.physical = {*displayId, *connectionType, *hwcDisplayId};
+ state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1970,7 +1997,9 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
+ expectedPhysical = {.id = *displayId,
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
}
// The display should have been set up in the current display state
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index 68cb52f..a119e27 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -77,6 +77,22 @@
return tracingSession;
}
+ std::vector<perfetto::protos::TracePacket> readGraphicsFramePacketsBlocking(
+ perfetto::TracingSession* tracingSession) {
+ std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+ perfetto::protos::Trace trace;
+ EXPECT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+
+ std::vector<perfetto::protos::TracePacket> packets;
+ for (const auto& packet : trace.packet()) {
+ if (!packet.has_graphics_frame_event()) {
+ continue;
+ }
+ packets.emplace_back(packet);
+ }
+ return packets;
+ }
+
std::unique_ptr<FrameTracer> mFrameTracer;
FenceToFenceTimeMap fenceFactory;
};
@@ -142,40 +158,29 @@
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
-
mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
// Create second trace packet to finalize the previous one.
mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- EXPECT_EQ(raw_trace.size(), 0);
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 0);
}
{
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
-
mFrameTracer->traceNewLayer(layerId, layerName);
mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
// Create second trace packet to finalize the previous one.
mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- ASSERT_GT(raw_trace.size(), 0);
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 1);
- perfetto::protos::Trace trace;
- ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
- ASSERT_FALSE(trace.packet().empty());
- EXPECT_EQ(trace.packet().size(), 1);
-
- const auto& packet = trace.packet().Get(0);
+ const auto& packet = packets[0];
ASSERT_TRUE(packet.has_timestamp());
EXPECT_EQ(packet.timestamp(), timestamp);
ASSERT_TRUE(packet.has_graphics_frame_event());
@@ -205,24 +210,21 @@
fenceFactory.signalAllForTest(Fence::NO_FENCE, Fence::SIGNAL_TIME_PENDING);
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
// Trace.
mFrameTracer->traceNewLayer(layerId, layerName);
mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
// Create extra trace packet to (hopefully not) trigger and finalize the fence packet.
mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- EXPECT_EQ(raw_trace.size(), 0);
+
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 0);
}
{
auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
mFrameTracer->traceNewLayer(layerId, layerName);
mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
const nsecs_t timestamp = systemTime();
@@ -231,15 +233,10 @@
mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- ASSERT_GT(raw_trace.size(), 0);
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above.
- perfetto::protos::Trace trace;
- ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
- ASSERT_FALSE(trace.packet().empty());
- EXPECT_EQ(trace.packet().size(), 2); // Two packets because of the extra trace made above.
-
- const auto& packet = trace.packet().Get(1);
+ const auto& packet = packets[1];
ASSERT_TRUE(packet.has_timestamp());
EXPECT_EQ(packet.timestamp(), timestamp);
ASSERT_TRUE(packet.has_graphics_frame_event());
@@ -266,8 +263,6 @@
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
mFrameTracer->traceNewLayer(layerId, layerName);
// traceFence called after fence signalled.
@@ -288,22 +283,17 @@
mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- ASSERT_GT(raw_trace.size(), 0);
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 2);
- perfetto::protos::Trace trace;
- ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
- ASSERT_FALSE(trace.packet().empty());
- EXPECT_EQ(trace.packet().size(), 2);
-
- const auto& packet1 = trace.packet().Get(0);
+ const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
EXPECT_EQ(packet1.timestamp(), signalTime1);
ASSERT_TRUE(packet1.has_graphics_frame_event());
ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
- const auto& packet2 = trace.packet().Get(1);
+ const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
EXPECT_EQ(packet2.timestamp(), signalTime2);
ASSERT_TRUE(packet2.has_graphics_frame_event());
@@ -323,8 +313,6 @@
auto fence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
mFrameTracer->traceNewLayer(layerId, layerName);
mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type);
fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime);
@@ -332,8 +320,8 @@
mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- EXPECT_EQ(raw_trace.size(), 0);
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 0);
}
TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
@@ -347,8 +335,6 @@
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- // Clean up irrelevant traces.
- tracingSession->ReadTraceBlocking();
mFrameTracer->traceNewLayer(layerId, layerName);
// traceFence called after fence signalled.
@@ -369,15 +355,10 @@
mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- ASSERT_GT(raw_trace.size(), 0);
+ auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+ EXPECT_EQ(packets.size(), 2);
- perfetto::protos::Trace trace;
- ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
- ASSERT_FALSE(trace.packet().empty());
- EXPECT_EQ(trace.packet().size(), 2);
-
- const auto& packet1 = trace.packet().Get(0);
+ const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
EXPECT_EQ(packet1.timestamp(), startTime1);
ASSERT_TRUE(packet1.has_graphics_frame_event());
@@ -386,7 +367,7 @@
const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event1.duration_ns(), duration);
- const auto& packet2 = trace.packet().Get(1);
+ const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
EXPECT_EQ(packet2.timestamp(), startTime2);
ASSERT_TRUE(packet2.has_graphics_frame_event());
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 71d17a9..3ab22d3 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -49,6 +49,8 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
@@ -96,14 +98,14 @@
// no layers are returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, mTime);
+ history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
ASSERT_TRUE(history().summarize(mTime).empty());
EXPECT_EQ(1, activeLayerCount());
}
// High FPS is returned once enough history has been recorded.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, mTime);
+ history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, history().summarize(mTime).size());
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
@@ -121,7 +123,7 @@
nsecs_t time = mTime;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -155,7 +157,7 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -166,12 +168,12 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
@@ -182,7 +184,7 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -196,10 +198,10 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
}
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -209,7 +211,7 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
@@ -228,7 +230,7 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -246,7 +248,7 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 6fca673..1705835 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "LayerHistoryTestV2"
+#include <Layer.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -37,6 +38,9 @@
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
+ static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
+ static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+ LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
static constexpr float LO_FPS = 30.f;
static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -46,6 +50,8 @@
LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
@@ -59,6 +65,13 @@
[now](const auto& pair) { return pair.second->isFrequent(now); });
}
+ auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+ [now](const auto& pair) { return pair.second->isAnimating(now); });
+ }
+
void setLayerInfoVote(Layer* layer,
LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
for (auto& [weak, info] : history().mLayerInfos) {
@@ -71,6 +84,26 @@
}
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+ auto createLayer(std::string name) {
+ return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+ }
+
+ void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
+ float desiredRefreshRate, int numFrames) {
+ const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
+ impl::LayerHistoryV2::Summary summary;
+ for (int i = 0; i < numFrames; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += framePeriod;
+
+ summary = history().summarize(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
+ << "Frame rate is " << frameRate;
+ }
Hwc2::mock::Display mDisplay;
RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
@@ -105,7 +138,7 @@
// Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -113,7 +146,7 @@
// Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -130,7 +163,7 @@
nsecs_t time = systemTime();
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
auto summary = history().summarize(time);
ASSERT_EQ(1, history().summarize(time).size());
// Layer is still considered inactive so we expect to get Min
@@ -154,7 +187,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -177,7 +210,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -204,7 +237,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -232,7 +265,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -260,7 +293,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -292,7 +325,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -334,43 +367,48 @@
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
+ impl::LayerHistoryV2::Summary summary;
+
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -379,38 +417,41 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
}
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, time);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer1 expires.
layer1.clear();
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -418,39 +459,43 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer2 expires.
layer2.clear();
- EXPECT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
// layer3 becomes active and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer3.get(), time, time);
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer3 expires.
layer3.clear();
- EXPECT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(0, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -466,7 +511,7 @@
// the very first updates makes the layer frequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -477,7 +522,7 @@
}
// the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -491,7 +536,7 @@
// Now event if we post a quick few frame we should stay infrequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -502,7 +547,7 @@
}
// More quick frames will get us to frequent again
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -512,5 +557,194 @@
EXPECT_EQ(1, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
+ auto explicitVisiblelayer = createLayer();
+ auto explicitInvisiblelayer = createLayer();
+
+ EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+
+ EXPECT_EQ(2, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // another update with the same cadence keep in infrequent
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // an update as animation will immediately vote for Max
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+ recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ }
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+class LayerHistoryTestV2Parameterized
+ : public LayerHistoryTestV2,
+ public testing::WithParamInterface<std::chrono::nanoseconds> {};
+
+TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
+ std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+ auto heuristicLayer = createLayer("HeuristicLayer");
+
+ EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ auto infrequentLayer = createLayer("InfrequentLayer");
+ EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ const nsecs_t startTime = systemTime();
+
+ const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+ history().record(heuristicLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+
+ nsecs_t time = startTime;
+ nsecs_t lastInfrequentUpdate = startTime;
+ const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+ int infrequentLayerUpdates = 0;
+ while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+ time += heuristicUpdateDelta.count();
+ history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+ if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+ ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+ totalInfrequentLayerUpdates);
+ lastInfrequentUpdate = time;
+ history().record(infrequentLayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ infrequentLayerUpdates++;
+ }
+
+ if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+ ASSERT_NE(0, history().summarize(time).size());
+ ASSERT_GE(2, history().summarize(time).size());
+
+ bool max = false;
+ bool min = false;
+ float heuristic = 0;
+ for (const auto& layer : history().summarize(time)) {
+ if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+ heuristic = layer.desiredRefreshRate;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+ max = true;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+ min = true;
+ }
+ }
+
+ if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+ EXPECT_FLOAT_EQ(24.0f, heuristic);
+ EXPECT_FALSE(max);
+ if (history().summarize(time).size() == 2) {
+ EXPECT_TRUE(min);
+ }
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
+ ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 2b168b2..1f6f166 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -43,6 +43,14 @@
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
+ float findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, float frameRate) {
+ return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
+ }
+
+ std::vector<float> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
+ return refreshRateConfigs.mKnownFrameRates;
+ }
+
// Test config IDs
static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
@@ -284,7 +292,8 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
- return {{"testLayer", LayerVoteType::Heuristic, refreshRate, 1.0f}};
+ return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f,
+ /*focused*/ false}};
};
EXPECT_EQ(mExpected90Config,
@@ -335,27 +344,22 @@
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
HWC_CONFIG_ID_72);
- // If there are not layers, there is not content detection, so return the current
- // refresh rate.
+ // If there are no layers we select the default frame rate, which is the max of the primary
+ // range.
auto layers = std::vector<LayerRequirement>{};
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
- false, /*idle*/ false, &ignored));
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- // Current refresh rate can always be changed.
- refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
- false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -366,163 +370,134 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
lr.name = "45Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
lr.name = "30Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
lr.name = "24Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.name = "";
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -532,43 +507,35 @@
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -583,28 +550,24 @@
lr2.desiredRefreshRate = 60.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -621,8 +584,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -631,8 +593,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -641,8 +602,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -651,8 +611,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -661,8 +620,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -671,8 +629,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
@@ -681,8 +638,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -691,8 +647,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -701,12 +656,10 @@
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -716,43 +669,35 @@
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -763,71 +708,57 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 45.0f;
lr.name = "45Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 30.0f;
lr.name = "30Hz Heuristic";
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 24.0f;
lr.name = "24Hz Heuristic";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 24.0f;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -840,56 +771,48 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 15.0f;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -901,10 +824,8 @@
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored);
- printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
- EXPECT_EQ(mExpected60Config, refreshRate);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
}
}
@@ -932,7 +853,6 @@
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -947,24 +867,21 @@
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 90.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -976,7 +893,6 @@
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -988,15 +904,12 @@
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored);
- printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
- EXPECT_EQ(mExpected90Config, refreshRate);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
}
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -1013,8 +926,7 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1023,8 +935,7 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1032,8 +943,7 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30.0f;
@@ -1042,8 +952,7 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30.0f;
@@ -1051,12 +960,10 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -1072,7 +979,7 @@
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1080,7 +987,7 @@
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1088,7 +995,7 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1096,7 +1003,7 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1106,20 +1013,20 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, touchConsidered) {
- bool touchConsidered;
+ RefreshRateConfigs::GlobalSignals consideredSignals;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- refreshRateConfigs->getBestRefreshRate({}, false, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false}, &consideredSignals);
+ EXPECT_EQ(false, consideredSignals.touch);
- refreshRateConfigs->getBestRefreshRate({}, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(true, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = false}, &consideredSignals);
+ EXPECT_EQ(true, consideredSignals.touch);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
LayerRequirement{.weight = 1.0f}};
@@ -1132,8 +1039,9 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(true, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 60.0f;
@@ -1141,8 +1049,9 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(false, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1150,8 +1059,9 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(true, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 60.0f;
@@ -1159,12 +1069,12 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(false, consideredSignals.touch);
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_60);
@@ -1200,12 +1110,132 @@
lr.name = ss.str();
const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
EXPECT_FLOAT_EQ(refreshRate.getFps(), test.second)
<< "Expecting " << test.first << "fps => " << test.second << "Hz";
}
}
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+ 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ RefreshRateConfigs::GlobalSignals consideredSignals;
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitDefault";
+ lr.focused = true;
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
+ &consideredSignals));
+ EXPECT_EQ(false, consideredSignals.touch);
+}
+
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+ 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 90.0f;
+ lr.name = "90Hz ExplicitDefault";
+ lr.focused = true;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
+}
+
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+ 0);
+
+ RefreshRateConfigs::GlobalSignals consideredSignals;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false},
+ &consideredSignals));
+ EXPECT_EQ(false, consideredSignals.touch);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitExactOrMultiple";
+ lr.focused = false;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.focused = true;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitDefault";
+ lr.focused = false;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.focused = true;
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.vote = LayerVoteType::Heuristic;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz Heuristic";
+ lr.focused = false;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.focused = true;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.vote = LayerVoteType::Max;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz Max";
+ lr.focused = false;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.focused = true;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.vote = LayerVoteType::Min;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz Min";
+ lr.focused = false;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ lr.focused = true;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
TEST_F(RefreshRateConfigsTest, groupSwitching) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
@@ -1217,10 +1247,8 @@
layer.desiredRefreshRate = 90.0f;
layer.name = "90Hz ExplicitDefault";
- bool touchConsidered;
ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs
- ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getConfigId());
RefreshRateConfigs::Policy policy;
@@ -1228,14 +1256,13 @@
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs
- ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getConfigId());
}
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
+ std::make_unique<RefreshRateConfigs>(m30_60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
@@ -1243,38 +1270,42 @@
// Return the config ID from calling getBestRefreshRate() for a single layer with the
// given voteType and fps.
- auto getFrameRate = [&](LayerVoteType voteType, float fps,
- bool touchActive = false) -> HwcConfigIndexType {
+ auto getFrameRate = [&](LayerVoteType voteType, float fps, bool touchActive = false,
+ bool focused = true) -> HwcConfigIndexType {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
- bool touchConsidered;
- return refreshRateConfigs
- ->getBestRefreshRate(layers, touchActive, /*idle*/ false, &touchConsidered)
+ layers[0].focused = focused;
+ return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
.getConfigId();
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+ {HWC_CONFIG_ID_60, {30.f, 60.f}, {30.f, 90.f}}),
0);
- bool touchConsidered;
EXPECT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs
- ->getBestRefreshRate({}, /*touchActive=*/false, /*idle*/ false,
- &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
.getConfigId());
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90.f));
+ EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+
+ // Layers not focused are not allowed to override primary config
+ EXPECT_EQ(HWC_CONFIG_ID_60,
+ getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/false,
+ /*focused=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60,
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/false,
+ /*focused=*/false));
// Touch boost should be restricted to the primary range.
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f, /*touch=*/true));
// When we're higher than the primary range max due to a layer frame rate setting, touch boost
// shouldn't drag us back down to the primary range max.
EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/true));
- EXPECT_EQ(HWC_CONFIG_ID_90,
+ EXPECT_EQ(HWC_CONFIG_ID_60,
getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/true));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
@@ -1296,13 +1327,19 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].name = "Test layer";
- auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> HwcConfigIndexType {
+ const auto getIdleFrameRate = [&](LayerVoteType voteType,
+ bool touchActive) -> HwcConfigIndexType {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90.f;
- bool touchConsidered;
- return refreshRateConfigs
- ->getBestRefreshRate(layers, touchActive, /*idle=*/true, &touchConsidered)
- .getConfigId();
+ RefreshRateConfigs::GlobalSignals consideredSignals;
+ const auto configId =
+ refreshRateConfigs
+ ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
+ &consideredSignals)
+ .getConfigId();
+ // Refresh rate will be chosen by either touch state or idle state
+ EXPECT_EQ(!touchActive, consideredSignals.idle);
+ return configId;
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
@@ -1310,38 +1347,130 @@
0);
// Idle should be lower priority than touch boost.
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitDefault, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90,
+ getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90,
+ getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/true));
// With no layers, idle should still be lower priority than touch boost.
- bool touchConsidered;
EXPECT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs
- ->getBestRefreshRate({}, /*touchActive=*/true, /*idle=*/true,
- &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
.getConfigId());
// Idle should be higher precedence than other layer frame rate considerations.
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitDefault, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60,
+ getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60,
+ getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/false));
// Idle should be applied rather than the current config when there are no layers.
EXPECT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs
- ->getBestRefreshRate({}, /*touchActive=*/false, /*idle=*/true,
- &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
.getConfigId());
}
+TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
+ const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, fps);
+ float expectedFrameRate;
+ if (fps < 26.91f) {
+ expectedFrameRate = 24.0f;
+ } else if (fps < 37.51f) {
+ expectedFrameRate = 30.0f;
+ } else if (fps < 52.51f) {
+ expectedFrameRate = 45.0f;
+ } else if (fps < 66.01f) {
+ expectedFrameRate = 60.0f;
+ } else if (fps < 81.01f) {
+ expectedFrameRate = 72.0f;
+ } else {
+ expectedFrameRate = 90.0f;
+ }
+ EXPECT_FLOAT_EQ(expectedFrameRate, knownFrameRate)
+ << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ struct ExpectedRate {
+ float rate;
+ const RefreshRate& expected;
+ };
+
+ /* clang-format off */
+ std::vector<ExpectedRate> knownFrameRatesExpectations = {
+ {24.0f, mExpected60Config},
+ {30.0f, mExpected60Config},
+ {45.0f, mExpected90Config},
+ {60.0f, mExpected60Config},
+ {72.0f, mExpected90Config},
+ {90.0f, mExpected90Config},
+ };
+ /* clang-format on */
+
+ // Make sure the test tests all the known frame rate
+ const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
+ const auto equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+ knownFrameRatesExpectations.begin(),
+ [](float a, const ExpectedRate& b) { return a == b.rate; });
+ EXPECT_TRUE(equal);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::Heuristic;
+ for (const auto& expectedRate : knownFrameRatesExpectations) {
+ layer.desiredRefreshRate = expectedRate.rate;
+ const auto& refreshRate =
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(expectedRate.expected, refreshRate);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
+ EXPECT_TRUE(mExpected60Config < mExpected90Config);
+ EXPECT_FALSE(mExpected60Config < mExpected60Config);
+ EXPECT_FALSE(mExpected90Config < mExpected90Config);
+}
+
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
+ using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+ // SetPolicy(60, 90), current 90Hz => TurnOn.
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(60, 90), current 60Hz => TurnOn.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 90}}), 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(60, 60), current 60Hz => NoChange, avoid extra calls.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+ EXPECT_EQ(KernelIdleTimerAction::NoChange, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(90, 90), current 90Hz => TurnOff.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1aa7320..382199d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -31,8 +31,10 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockEventThread.h"
+#include "mock/MockLayer.h"
using testing::_;
using testing::Return;
@@ -58,13 +60,18 @@
SchedulerTest();
~SchedulerTest() override;
- std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
- std::unique_ptr<TestableScheduler> mScheduler;
+ Hwc2::mock::Display mDisplay;
+ const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+ .setVsyncPeriod(16'666'667)
+ .setConfigGroup(0)
+ .build()},
+ HwcConfigIndexType(0)};
+
+ TestableScheduler mScheduler{mConfigs, false};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
- Hwc2::mock::Display mDisplay;
};
SchedulerTest::SchedulerTest() {
@@ -72,16 +79,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
- .setConfigGroup(0)
- .build()};
- mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
-
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
-
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -93,7 +90,7 @@
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+ mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
}
@@ -103,7 +100,6 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
@@ -111,67 +107,84 @@
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(handle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(handle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler->getEventConnection(handle));
+ EXPECT_FALSE(mScheduler.getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
+ mScheduler.onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
+ mScheduler.onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+ mScheduler.dump(handle, output);
EXPECT_TRUE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
+ mScheduler.setPhaseOffset(handle, 10);
}
TEST_F(SchedulerTest, validConnectionHandle) {
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(mConnectionHandle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
ASSERT_EQ(mEventThreadConnection, connection);
- EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
+ EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- ASSERT_NO_FATAL_FAILURE(
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+ mScheduler.onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+ mScheduler.onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+ mScheduler.dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+ mScheduler.setPhaseOffset(mConnectionHandle, 10);
static constexpr size_t kEventConnections = 5;
ON_CALL(*mEventThread, getEventThreadConnectionCount())
.WillByDefault(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
+ EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
}
-} // namespace
+TEST_F(SchedulerTest, noLayerHistory) {
+ // Layer history should not be created if there is a single config.
+ ASSERT_FALSE(mScheduler.hasLayerHistory());
+
+ TestableSurfaceFlinger flinger;
+ mock::MockLayer layer(flinger.flinger());
+
+ // Content detection should be no-op.
+ mScheduler.registerLayer(&layer);
+ mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler.setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ mScheduler.chooseRefreshRateForContent();
+ EXPECT_EQ(0, mScheduler.refreshRateChangeCount());
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 41b5d49..c463294 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -29,41 +29,20 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ createLayerHistory(configs, useContentDetectionV2), useContentDetectionV2,
+ true) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
}
- size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- if (mUseContentDetectionV2) {
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
- ->mLayerInfos.size();
- } else {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
- ->mLayerInfos.size();
- }
- }
-
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -72,13 +51,43 @@
auto& mutableEventControlThread() { return mEventControlThread; }
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto mutableLayerHistory() {
+
+ size_t refreshRateChangeCount() const { return mRefreshRateChangeCount; }
+
+ bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
+
+ auto* mutableLayerHistory() {
+ LOG_ALWAYS_FATAL_IF(mUseContentDetectionV2);
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
- auto mutableLayerHistoryV2() {
+
+ auto* mutableLayerHistoryV2() {
+ LOG_ALWAYS_FATAL_IF(!mUseContentDetectionV2);
return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
}
+ size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mUseContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
+ : mutableLayerHistory()->mLayerInfos.size();
+ }
+
+ void replaceTouchTimer(int64_t millis) {
+ if (mTouchTimer) {
+ mTouchTimer.reset();
+ }
+ mTouchTimer.emplace(
+ std::chrono::milliseconds(millis),
+ [this] { touchTimerCallback(TimerState::Reset); },
+ [this] { touchTimerCallback(TimerState::Expired); });
+ mTouchTimer->start();
+ }
+
+ bool isTouchActive() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return mFeatures.touch == Scheduler::TouchState::Active;
+ }
+
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the Scheduler instance may
@@ -90,9 +99,12 @@
}
private:
- void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+ void changeRefreshRate(const RefreshRate&, ConfigEvent) override { mRefreshRateChangeCount++; }
+
void repaintEverythingForHWC() override {}
void kernelTimerChanged(bool /*expired*/) override {}
+
+ size_t mRefreshRateChangeCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index add3327..256e048 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -177,6 +177,8 @@
class TestableSurfaceFlinger {
public:
+ using HotplugEvent = SurfaceFlinger::HotplugEvent;
+
SurfaceFlinger* flinger() { return mFlinger.get(); }
TestableScheduler* scheduler() { return mScheduler; }
@@ -200,13 +202,20 @@
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- bool useContentDetectionV2 = false) {
+ bool hasMultipleConfigs = false) {
std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
+ .setVsyncPeriod(16'666'667)
.setConfigGroup(0)
.build()};
+ if (hasMultipleConfigs) {
+ configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1)
+ .setVsyncPeriod(11'111'111)
+ .setConfigGroup(0)
+ .build());
+ }
+
mFlinger->mRefreshRateConfigs = std::make_unique<
scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
mFlinger->mRefreshRateStats = std::make_unique<
@@ -216,9 +225,10 @@
mFlinger->mPhaseConfiguration =
mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ constexpr bool kUseContentDetectionV2 = false;
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+ *mFlinger->mRefreshRateConfigs, kUseContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -246,28 +256,32 @@
memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
}
- using HotplugEvent = SurfaceFlinger::HotplugEvent;
-
- auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
- auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
+ static auto& mutableLayerCurrentState(const sp<Layer>& layer) { return layer->mCurrentState; }
+ static auto& mutableLayerDrawingState(const sp<Layer>& layer) { return layer->mDrawingState; }
auto& mutableStateLock() { return mFlinger->mStateLock; }
- void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+ static auto findOutputLayerForDisplay(const sp<Layer>& layer,
+ const sp<const DisplayDevice>& display) {
+ return layer->findOutputLayerForDisplay(display.get());
+ }
+
+ static void setLayerSidebandStream(const sp<Layer>& layer,
+ const sp<NativeHandle>& sidebandStream) {
layer->mDrawingState.sidebandStream = sidebandStream;
layer->mSidebandStream = sidebandStream;
layer->editCompositionState()->sidebandStream = sidebandStream;
}
- void setLayerCompositionType(sp<Layer> layer, hal::Composition type) {
- auto outputLayer = layer->findOutputLayerForDisplay(mFlinger->getDefaultDisplayDevice());
+ void setLayerCompositionType(const sp<Layer>& layer, hal::Composition type) {
+ auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
LOG_ALWAYS_FATAL_IF(!outputLayer);
auto& state = outputLayer->editState();
LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc);
(*state.hwc).hwcCompositionType = type;
- };
+ }
- void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) {
+ static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) {
layer->mPotentialCursor = potentialCursor;
}
@@ -283,14 +297,14 @@
return mFlinger->destroyDisplay(displayToken);
}
- auto resetDisplayState() { return mFlinger->resetDisplayState(); }
+ auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); }
auto setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
const DisplayDeviceState& state,
const sp<compositionengine::DisplaySurface>& dispSurface,
- const sp<IGraphicBufferProducer>& producer) {
+ const sp<IGraphicBufferProducer>& producer) NO_THREAD_SAFETY_ANALYSIS {
return mFlinger->setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
dispSurface, producer);
}
@@ -315,6 +329,8 @@
return mFlinger->onInitializeDisplays();
}
+ auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
// Allow reading display state without locking, as if called on the SF main thread.
auto setPowerModeInternal(const sp<DisplayDevice>& display,
hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -325,7 +341,7 @@
auto captureScreenImplLocked(const RenderArea& renderArea,
SurfaceFlinger::TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool forSystem, int* outSyncFd, bool regionSampling) {
bool ignored;
return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
@@ -333,9 +349,9 @@
regionSampling, ignored);
}
- auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
- return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+ auto traverseLayersInLayerStack(ui::LayerStack layerStack,
+ const LayerVector::Visitor& visitor) {
+ return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, visitor);
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -627,7 +643,7 @@
if (const auto type = mCreationArgs.connectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {*displayId, *type, *mHwcDisplayId};
+ state.physical = {.id = *displayId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
}
state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7a1c7c6..63a34af 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -833,6 +833,15 @@
ASSERT_EQ(0, globalProto.stats_size());
}
+TEST_F(TimeStatsTest, noInfInAverageFPS) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 1000000);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ EXPECT_THAT(result, HasSubstr("averageFPS = 0.000"));
+}
+
namespace {
std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
const std::vector<int32_t>& frameCounts) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index be49ef3..c2a7752 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ bool needsMoreSamples() const final { return false; }
void dump(std::string&) const final {}
private:
@@ -86,6 +87,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ bool needsMoreSamples() const final { return false; }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 1899bed..373f6a5 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -89,15 +90,18 @@
void advanceBy(nsecs_t advancement) {
mCurrentTime += advancement;
- if (mCurrentTime >= mNextCallbackTime && mCallback) {
+ if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) {
mCallback();
}
};
+ void setLag(nsecs_t lag) { mLag = lag; }
+
private:
std::function<void()> mCallback;
nsecs_t mNextCallbackTime = 0;
nsecs_t mCurrentTime = 0;
+ nsecs_t mLag = 0;
};
class CountingCallback {
@@ -112,11 +116,15 @@
operator VSyncDispatch::CallbackToken() const { return mToken; }
- void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); }
+ void counter(nsecs_t time, nsecs_t wakeup_time) {
+ mCalls.push_back(time);
+ mWakeupTime.push_back(wakeup_time);
+ }
VSyncDispatch& mDispatch;
VSyncDispatch::CallbackToken mToken;
std::vector<nsecs_t> mCalls;
+ std::vector<nsecs_t> mWakeupTime;
};
class PausingCallback {
@@ -658,6 +666,117 @@
cb1.cancel();
}
+// b/154303580
+TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(80);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+// b/154303580.
+// If the same callback tries to reschedule itself after it's too late, timer opts to apply the
+// update later, as opposed to blocking the calling thread.
+TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq);
+ CountingCallback cb(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(80);
+
+ EXPECT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// b/154303580.
+TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+
+ mMockClock.advanceBy(80);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1280)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+ mMockClock.advanceToNextCallback();
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) {
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ Sequence seq;
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 390, 1000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(700);
+
+ ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb1.mWakeupTime[0], Eq(600));
+ ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb2.mWakeupTime[0], Eq(610));
+}
+
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
@@ -817,6 +936,19 @@
EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
}
+TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
+ static constexpr auto effectualOffset = 200;
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
+ entry.addPendingWorkloadUpdate(100, 400);
+ entry.addPendingWorkloadUpdate(effectualOffset, 700);
+ EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
+ entry.update(mStubTracker, 0);
+ EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
+ EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
new file mode 100644
index 0000000..9c1ec07
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncModulator.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class MockScheduler : public IPhaseOffsetControl {
+public:
+ void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+ mPhaseOffset[handle] = phaseOffset;
+ }
+
+ nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
+
+private:
+ std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
+};
+
+class VSyncModulatorTest : public testing::Test {
+protected:
+ static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
+ VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ // Add a 1ms slack to avoid strange timer race conditions.
+ static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
+
+ // Used to enumerate the different offsets we have
+ enum {
+ SF_LATE,
+ APP_LATE,
+ SF_EARLY,
+ APP_EARLY,
+ SF_EARLY_GL,
+ APP_EARLY_GL,
+ };
+
+ std::unique_ptr<VSyncModulator> mVSyncModulator;
+ MockScheduler mMockScheduler;
+ ConnectionHandle mAppConnection{1};
+ ConnectionHandle mSfConnection{2};
+ VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
+ {SF_EARLY_GL, APP_EARLY_GL},
+ {SF_LATE, APP_LATE}};
+
+ void SetUp() override {
+ mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
+ mSfConnection, mOffsets);
+ mVSyncModulator->setPhaseOffsets(mOffsets);
+
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+ };
+
+ void TearDown() override { mVSyncModulator.reset(); }
+};
+
+TEST_F(VSyncModulatorTest, Normal) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+ }
+}
+
+TEST_F(VSyncModulatorTest, EarlyEnd) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStart) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index bf2a889..d4cd11d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -73,27 +73,27 @@
TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
- EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod));
- tracker.addVsyncTimestamp(mNow);
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ tracker.addVsyncTimestamp(mNow += mPeriod);
}
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+ EXPECT_FALSE(tracker.needsMoreSamples());
}
TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) {
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
tracker.addVsyncTimestamp(mNow += mPeriod);
}
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+ EXPECT_FALSE(tracker.needsMoreSamples());
auto const changedPeriod = mPeriod * 2;
tracker.setPeriod(changedPeriod);
- EXPECT_TRUE(tracker.needsMoreSamples(mNow));
+ EXPECT_TRUE(tracker.needsMoreSamples());
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
- EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod));
- tracker.addVsyncTimestamp(mNow);
+ EXPECT_TRUE(tracker.needsMoreSamples());
+ tracker.addVsyncTimestamp(mNow += changedPeriod);
}
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+ EXPECT_FALSE(tracker.needsMoreSamples());
}
TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) {
@@ -124,6 +124,38 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod));
}
+// b/159882858
+TEST_F(VSyncPredictorTest, updatesTimebaseForSyntheticAfterIdleTime) {
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += mPeriod));
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+
+ auto const halfPeriod = mPeriod >> 2;
+ nsecs_t relativelyLongGapWithDrift = mPeriod * 100 + halfPeriod;
+
+ EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += relativelyLongGapWithDrift));
+
+ tracker.resetModel();
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+}
+
+TEST_F(VSyncPredictorTest, uponBadVsyncWillSwitchToSyntheticWhileRecalibrating) {
+ auto const slightlyMorePeriod = mPeriod + 10;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += slightlyMorePeriod));
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyMorePeriod));
+
+ auto const halfPeriod = mPeriod >> 2;
+ EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += halfPeriod));
+
+ tracker.resetModel();
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+}
+
TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) {
// these are precomputed simulated 16.6s vsyncs with uniform distribution +/- 1.6ms error
std::vector<nsecs_t> const simulatedVsyncs{
@@ -288,20 +320,6 @@
EXPECT_THAT(intercept, Eq(0));
}
-TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) {
- auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0);
-
- for (auto const& timestamp : simulatedVsyncs) {
- tracker.addVsyncTimestamp(timestamp);
- }
- auto const mNow = *simulatedVsyncs.rbegin();
- EXPECT_FALSE(tracker.needsMoreSamples(mNow));
-
- // TODO: would be better to decay this as a result of the variance of the samples
- static auto constexpr aLongTimeOut = 1000000000;
- EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut));
-}
-
TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
auto const simulatedVsyncs =
generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0);
@@ -411,7 +429,7 @@
// When VsyncPredictor returns the period it means that it doesn't know how to predict and
// it needs to get more samples
if (slope == mPeriod && intercept == 0) {
- EXPECT_TRUE(tracker.needsMoreSamples(now));
+ EXPECT_TRUE(tracker.needsMoreSamples());
}
}
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 3f14d65..c5cddf3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,28 +41,10 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class VSyncTrackerWrapper : public VSyncTracker {
-public:
- VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
-
- bool addVsyncTimestamp(nsecs_t timestamp) final {
- return mTracker->addVsyncTimestamp(timestamp);
- }
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
- return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
- }
- nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
- void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
- void resetModel() final { mTracker->resetModel(); }
- void dump(std::string& result) const final { mTracker->dump(result); }
-
-private:
- std::shared_ptr<VSyncTracker> const mTracker;
-};
-
class MockClock : public Clock {
public:
MOCK_CONST_METHOD0(now, nsecs_t());
@@ -88,29 +70,6 @@
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class VSyncDispatchWrapper : public VSyncDispatch {
-public:
- VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
- CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
- std::string callbackName) final {
- return mDispatch->registerCallback(callbackFn, callbackName);
- }
-
- void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
-
- ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) final {
- return mDispatch->schedule(token, workDuration, earliestVsync);
- }
-
- CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
-
- void dump(std::string& result) const final { return mDispatch->dump(result); }
-
-private:
- std::shared_ptr<VSyncDispatch> const mDispatch;
-};
-
std::shared_ptr<FenceTime> generateInvalidFence() {
sp<Fence> fence = new Fence();
return std::make_shared<FenceTime>(fence);
@@ -155,9 +114,8 @@
: mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
- mReactor(std::make_unique<ClockWrapper>(mMockClock),
- std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+ mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockDispatch, *mMockTracker,
+ kPendingLimit, false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
@@ -454,6 +412,83 @@
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
+TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+ mReactor.setPeriod(newPeriod);
+
+ static auto constexpr numSamplesWithNewPeriod = 4;
+ Sequence seq;
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(numSamplesWithNewPeriod - 2)
+ .InSequence(seq)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod);
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+}
+
+TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+ mReactor.setPeriod(newPeriod);
+
+ Sequence seq;
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+}
+
+TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod1 = 4000;
+ nsecs_t const newPeriod2 = 7000;
+
+ mReactor.setPeriod(newPeriod1);
+
+ Sequence seq;
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(4)
+ .InSequence(seq)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mMockTracker, needsMoreSamples())
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7);
+
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+
+ mReactor.setPeriod(newPeriod2);
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
+}
+
static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
return period - phase;
}
@@ -647,7 +682,7 @@
TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
@@ -663,6 +698,42 @@
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
+TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
+ // Create a reactor which supports the kernel idle timer
+ auto idleReactor =
+ VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockDispatch, *mMockTracker,
+ kPendingLimit, true /* supportKernelIdleTimer */);
+
+ bool periodFlushed = true;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
+ idleReactor.setIgnorePresentFences(true);
+
+ // First, set the same period, which should only be confirmed when we receive two
+ // matching callbacks
+ idleReactor.setPeriod(10000);
+ EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ // Correct period but incorrect timestamp delta
+ EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ // Correct period and correct timestamp delta
+ EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ // Then, set a new period, which should be confirmed as soon as we receive a callback
+ // reporting the new period
+ nsecs_t const newPeriod = 5000;
+ idleReactor.setPeriod(newPeriod);
+ // Incorrect timestamp delta and period
+ EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ // Incorrect timestamp delta but correct period
+ EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
using VSyncReactorDeathTest = VSyncReactorTest;
TEST_F(VSyncReactorDeathTest, invalidRemoval) {
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2a31078..c2c5072 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -140,6 +140,7 @@
const std::vector<uint8_t>&));
MOCK_METHOD1(getLayerGenericMetadataKeys,
V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
+ MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index dade9fc..fe99e77 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -93,6 +93,7 @@
MOCK_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
+ MOCK_METHOD1(getClientTargetProperty, hal::Error(hal::ClientTargetProperty*));
MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 50eb390..054aaf8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -40,7 +40,7 @@
status_t(const sp<android::EventThreadConnection> &));
MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
- MOCK_METHOD0(requestLatestConfig, void());
+ MOCK_METHOD1(requestLatestConfig, void(const sp<android::EventThreadConnection> &));
MOCK_METHOD1(pauseVsyncCallback, void(bool));
MOCK_METHOD0(getEventThreadConnectionCount, size_t());
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 119f580..078d8e0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -24,8 +24,9 @@
class MockLayer : public Layer {
public:
- explicit MockLayer(SurfaceFlinger* flinger)
- : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
+ MockLayer(SurfaceFlinger* flinger, std::string name)
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+ explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
new file mode 100644
index 0000000..c45a1a1
--- /dev/null
+++ b/services/vibratorservice/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libvibratorservice",
+
+ srcs: [
+ "VibratorCallbackScheduler.cpp",
+ "VibratorHalController.cpp",
+ "VibratorHalWrapper.cpp",
+ ],
+
+ aidl: {
+ local_include_dirs: ["include"],
+ include_dirs: [
+ "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
+ ],
+ export_aidl_headers: true
+ },
+
+ shared_libs: [
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ local_include_dirs: ["include"],
+
+ export_include_dirs: ["include"],
+}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
new file mode 100644
index 0000000..3f8cd67
--- /dev/null
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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 <chrono>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+bool DelayedCallback::isExpired() const {
+ return mExpiration <= std::chrono::steady_clock::now();
+}
+
+DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
+ return mExpiration;
+}
+
+void DelayedCallback::run() const {
+ mCallback();
+}
+
+bool DelayedCallback::operator<(const DelayedCallback& other) const {
+ return mExpiration < other.mExpiration;
+}
+
+bool DelayedCallback::operator>(const DelayedCallback& other) const {
+ return mExpiration > other.mExpiration;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+CallbackScheduler::~CallbackScheduler() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFinished = true;
+ }
+ mCondition.notify_all();
+ if (mCallbackThread && mCallbackThread->joinable()) {
+ mCallbackThread->join();
+ }
+}
+
+void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mCallbackThread == nullptr) {
+ mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
+ }
+ mQueue.emplace(DelayedCallback(callback, delay));
+ }
+ mCondition.notify_all();
+}
+
+void CallbackScheduler::loop() {
+ while (true) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mFinished) {
+ // Destructor was called, so let the callback thread die.
+ break;
+ }
+ while (!mQueue.empty() && mQueue.top().isExpired()) {
+ mQueue.top().run();
+ mQueue.pop();
+ }
+ if (mQueue.empty()) {
+ // Wait until a new callback is scheduled.
+ mCondition.wait(mMutex);
+ } else {
+ // Wait until next callback expires, or a new one is scheduled.
+ mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
new file mode 100644
index 0000000..03e51ae
--- /dev/null
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalController"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+static constexpr int MAX_RETRIES = 1;
+
+std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
+ static bool gHalExists = true;
+ if (!gHalExists) {
+ // We already tried to connect to all of the vibrator HAL versions and none was available.
+ return nullptr;
+ }
+
+ sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>();
+ if (aidlHal) {
+ ALOGV("Successfully connected to Vibrator HAL AIDL service.");
+ return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
+ }
+
+ sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
+ if (halV1_0 == nullptr) {
+ ALOGV("Vibrator HAL service not available.");
+ gHalExists = false;
+ return nullptr;
+ }
+
+ sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
+ if (halV1_3) {
+ ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
+ return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
+ }
+ sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
+ if (halV1_2) {
+ ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
+ return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
+ }
+ sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
+ if (halV1_1) {
+ ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
+ return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
+ }
+ ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
+ return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
+ if (result.isFailed()) {
+ ALOGE("%s failed: Vibrator HAL not available", functionName);
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ mConnectedHal->tryReconnect();
+ }
+ return result;
+}
+
+template <typename T>
+HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
+ std::shared_ptr<HalWrapper> hal = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ // Init was never called, so connect to HAL for the first time during this call.
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+
+ if (mConnectedHal == nullptr) {
+ ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+ return HalResult<T>::unsupported();
+ }
+ }
+ hal = mConnectedHal;
+ }
+
+ HalResult<T> ret = processHalResult(halFn(hal), functionName);
+ for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
+ ret = processHalResult(halFn(hal), functionName);
+ }
+
+ return ret;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void HalController::init() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ }
+}
+
+HalResult<void> HalController::ping() {
+ hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+ return apply(pingFn, "ping");
+}
+
+void HalController::tryReconnect() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ } else {
+ mConnectedHal->tryReconnect();
+ }
+}
+
+HalResult<void> HalController::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->on(timeout, completionCallback);
+ };
+ return apply(onFn, "on");
+}
+
+HalResult<void> HalController::off() {
+ hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); };
+ return apply(offFn, "off");
+}
+
+HalResult<void> HalController::setAmplitude(int32_t amplitude) {
+ hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->setAmplitude(amplitude);
+ };
+ return apply(setAmplitudeFn, "setAmplitude");
+}
+
+HalResult<void> HalController::setExternalControl(bool enabled) {
+ hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->setExternalControl(enabled);
+ };
+ return apply(setExternalControlFn, "setExternalControl");
+}
+
+HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->alwaysOnEnable(id, effect, strength);
+ };
+ return apply(alwaysOnEnableFn, "alwaysOnEnable");
+}
+
+HalResult<void> HalController::alwaysOnDisable(int32_t id) {
+ hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->alwaysOnDisable(id);
+ };
+ return apply(alwaysOnDisableFn, "alwaysOnDisable");
+}
+
+HalResult<Capabilities> HalController::getCapabilities() {
+ hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getCapabilities();
+ };
+ return apply(getCapabilitiesFn, "getCapabilities");
+}
+
+HalResult<std::vector<Effect>> HalController::getSupportedEffects() {
+ hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getSupportedEffects();
+ };
+ return apply(getSupportedEffectsFn, "getSupportedEffects");
+}
+
+HalResult<milliseconds> HalController::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->performEffect(effect, strength, completionCallback);
+ };
+ return apply(performEffectFn, "performEffect");
+}
+
+HalResult<void> HalController::performComposedEffect(
+ const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) {
+ hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+ return hal->performComposedEffect(primitiveEffects, completionCallback);
+ };
+ return apply(performComposedEffectFn, "performComposedEffect");
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
new file mode 100644
index 0000000..9d219ff
--- /dev/null
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapper"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace Aidl = android::hardware::vibrator;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+template <class T>
+HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
+ if (cache.has_value()) {
+ // Return copy of cached value.
+ return HalResult<T>::ok(*cache);
+ }
+ HalResult<T> ret = loadFn();
+ if (ret.isOk()) {
+ // Cache copy of returned value.
+ cache.emplace(ret.value());
+ }
+ return ret;
+}
+
+template <class T>
+bool isStaticCastValid(Effect effect) {
+ T castEffect = static_cast<T>(effect);
+ auto iter = hardware::hidl_enum_range<T>();
+ return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed();
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<T>::ok(data);
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<T>::unsupported();
+ default:
+ return HalResult<T>::failed();
+ }
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed();
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data) : HalResult<T>::failed();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed();
+}
+
+HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<void>::ok();
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<void>::unsupported();
+ default:
+ return HalResult<void>::failed();
+ }
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class HalCallbackWrapper : public Aidl::BnVibratorCallback {
+public:
+ HalCallbackWrapper(std::function<void()> completionCallback)
+ : mCompletionCallback(completionCallback) {}
+
+ binder::Status onComplete() override {
+ mCompletionCallback();
+ return binder::Status::ok();
+ }
+
+private:
+ const std::function<void()> mCompletionCallback;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> AidlHalWrapper::ping() {
+ return IInterface::asBinder(getHal())->pingBinder() ? HalResult<void>::ok()
+ : HalResult<void>::failed();
+}
+
+void AidlHalWrapper::tryReconnect() {
+ sp<Aidl::IVibrator> newHandle = checkVintfService<Aidl::IVibrator>();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+HalResult<void> AidlHalWrapper::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb));
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::off() {
+ return HalResult<void>::fromStatus(getHal()->off());
+}
+
+HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
+ float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
+ return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude));
+}
+
+HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
+ return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
+ return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
+ mCapabilities);
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
+ std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
+ return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
+ this),
+ mSupportedEffects);
+}
+
+HalResult<milliseconds> AidlHalWrapper::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ int32_t lengthMs;
+ auto result = getHal()->perform(effect, strength, cb, &lengthMs);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromStatus(result, length);
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::performComposedEffect(
+ const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = new HalCallbackWrapper(completionCallback);
+ return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
+ int32_t capabilities = 0;
+ auto result = getHal()->getCapabilities(&capabilities);
+ return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
+ std::vector<Effect> supportedEffects;
+ auto result = getHal()->getSupportedEffects(&supportedEffects);
+ return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+}
+
+sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::ping() {
+ auto result = getHal()->ping();
+ return HalResult<void>::fromReturn(result);
+}
+
+template <typename I>
+void HidlHalWrapper<I>::tryReconnect() {
+ sp<I> newHandle = I::tryGetService();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ auto result = getHal()->on(timeout.count());
+ auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+ return ret;
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::off() {
+ auto result = getHal()->off();
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) {
+ auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
+ ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+ ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
+ ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
+ mCapabilities);
+}
+
+template <typename I>
+HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
+ ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
+ return HalResult<std::vector<Effect>>::unsupported();
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
+ const std::function<void()>&) {
+ ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
+ hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
+ Capabilities capabilities =
+ result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
+ return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
+template <typename I>
+template <typename T>
+HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
+ const std::function<void()>& completionCallback) {
+ V1_0::Status status;
+ int32_t lengthMs;
+ auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+ status = retStatus;
+ lengthMs = retLengthMs;
+ };
+
+ V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
+ auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+template <typename I>
+sp<I> HidlHalWrapper<I>::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_0::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_1::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
+ auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_3::Effect>(effect)) {
+ return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
+ static_cast<V1_3::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+ sp<V1_3::IVibrator> hal = getHal();
+ auto amplitudeResult = hal->supportsAmplitudeControl();
+ if (!amplitudeResult.isOk()) {
+ return HalResult<Capabilities>::failed();
+ }
+
+ auto externalControlResult = hal->supportsExternalControl();
+ Capabilities capabilities = Capabilities::NONE;
+
+ if (amplitudeResult.withDefault(false)) {
+ capabilities |= Capabilities::AMPLITUDE_CONTROL;
+ }
+ if (externalControlResult.withDefault(false)) {
+ capabilities |= Capabilities::EXTERNAL_CONTROL;
+ }
+
+ return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
new file mode 100644
index 0000000..2c194b5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 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_VIBRATOR_CALLBACK_SCHEDULER_H
+#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+
+#include <android-base/thread_annotations.h>
+#include <chrono>
+#include <condition_variable>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for a callback to be executed after a delay.
+class DelayedCallback {
+public:
+ using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
+
+ DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
+ : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
+ ~DelayedCallback() = default;
+
+ void run() const;
+ bool isExpired() const;
+ Timestamp getExpiration() const;
+
+ // Compare by expiration time, where A < B when A expires first.
+ bool operator<(const DelayedCallback& other) const;
+ bool operator>(const DelayedCallback& other) const;
+
+private:
+ std::function<void()> mCallback;
+ Timestamp mExpiration;
+};
+
+// Schedules callbacks to be executed after a delay.
+class CallbackScheduler {
+public:
+ CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {}
+ virtual ~CallbackScheduler();
+
+ virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay);
+
+private:
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+
+ // Lazily instantiated only at the first time this scheduler is used.
+ std::unique_ptr<std::thread> mCallbackThread;
+
+ // Used to quit the callback thread when this instance is being destroyed.
+ bool mFinished GUARDED_BY(mMutex);
+
+ // Priority queue with reverse comparator, so tasks that expire first will be on top.
+ std::priority_queue<DelayedCallback, std::vector<DelayedCallback>,
+ std::greater<DelayedCallback>>
+ mQueue GUARDED_BY(mMutex);
+
+ void loop();
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
new file mode 100644
index 0000000..daf2c8c
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 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_OS_VIBRATORHALCONTROLLER_H
+#define ANDROID_OS_VIBRATORHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+// Handles the connection to he underlying HAL implementation available.
+class HalConnector {
+public:
+ HalConnector() = default;
+ virtual ~HalConnector() = default;
+
+ virtual std::shared_ptr<HalWrapper> connect(std::shared_ptr<CallbackScheduler> scheduler);
+};
+
+// Controller for Vibrator HAL handle.
+// This relies on HalConnector to connect to the underlying Vibrator HAL service and reconnects to
+// it after each failed api call. This also ensures connecting to the service is thread-safe.
+class HalController : public HalWrapper {
+public:
+ HalController()
+ : HalController(std::make_unique<HalConnector>(), std::make_shared<CallbackScheduler>()) {
+ }
+ HalController(std::unique_ptr<HalConnector> halConnector,
+ std::shared_ptr<CallbackScheduler> callbackScheduler)
+ : HalWrapper(std::move(callbackScheduler)),
+ mHalConnector(std::move(halConnector)),
+ mConnectedHal(nullptr) {}
+ virtual ~HalController() = default;
+
+ void init();
+
+ HalResult<void> ping() final override;
+ void tryReconnect() final override;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) final override;
+ HalResult<void> off() final override;
+
+ HalResult<void> setAmplitude(int32_t amplitude) final override;
+ HalResult<void> setExternalControl(bool enabled) final override;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) final override;
+ HalResult<void> alwaysOnDisable(int32_t id) final override;
+
+ HalResult<Capabilities> getCapabilities() final override;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) final override;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) final override;
+
+private:
+ std::unique_ptr<HalConnector> mHalConnector;
+ std::mutex mConnectedHalMutex;
+ // Shared pointer to allow local copies to be used by different threads.
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
+
+ template <typename T>
+ HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
+
+ template <typename T>
+ using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>;
+
+ template <typename T>
+ HalResult<T> apply(hal_fn<T>& halFn, const char* functionName);
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
new file mode 100644
index 0000000..a4fa869
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2020 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_OS_VIBRATORHALWRAPPER_H
+#define ANDROID_OS_VIBRATORHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+// Result of a call to the Vibrator HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T value) { return HalResult(value); }
+ static HalResult<T> failed() { return HalResult(/* unsupported= */ false); }
+ static HalResult<T> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(binder::Status status, T data);
+ static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret,
+ hardware::vibrator::V1_0::Status status, T data);
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+
+private:
+ std::optional<T> mValue;
+ bool mUnsupported;
+
+ explicit HalResult(T value) : mValue(std::make_optional(value)), mUnsupported(false) {}
+ explicit HalResult(bool unsupported) : mValue(), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Vibrator HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed() { return HalResult(/* failed= */ true); }
+ static HalResult<void> unsupported() {
+ return HalResult(/* failed= */ false, /* unsupported= */ true);
+ }
+
+ static HalResult<void> fromStatus(binder::Status status);
+ static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+
+private:
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool failed = false, bool unsupported = false)
+ : mFailed(failed), mUnsupported(unsupported) {}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Vibrator HAL capabilities.
+enum class Capabilities : int32_t {
+ NONE = 0,
+ ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
+ PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
+ AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
+ EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
+ EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
+ COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
+ ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+};
+
+inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs | rhs;
+}
+
+inline Capabilities operator&(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs & rhs;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// Wrapper for Vibrator HAL handlers.
+class HalWrapper {
+public:
+ explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
+ : mCallbackScheduler(std::move(scheduler)) {}
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult<void> ping() = 0;
+ virtual void tryReconnect() = 0;
+
+ virtual HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> off() = 0;
+
+ virtual HalResult<void> setAmplitude(int32_t amplitude) = 0;
+ virtual HalResult<void> setExternalControl(bool enabled) = 0;
+
+ virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) = 0;
+ virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
+
+ virtual HalResult<Capabilities> getCapabilities() = 0;
+ virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) = 0;
+
+ virtual HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) = 0;
+
+protected:
+ // Shared pointer to allow CallbackScheduler to outlive this wrapper.
+ const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+};
+
+// Wrapper for the AIDL Vibrator HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::IVibrator> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
+
+ HalResult<void> setAmplitude(int32_t amplitude) override final;
+ HalResult<void> setExternalControl(bool enabled) override final;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+ HalResult<Capabilities> getCapabilities() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) override final;
+
+private:
+ std::mutex mHandleMutex;
+ std::mutex mCapabilitiesMutex;
+ std::mutex mSupportedEffectsMutex;
+ sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
+ std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+ std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
+ GUARDED_BY(mSupportedEffectsMutex);
+
+ // Loads directly from IVibrator handle, skipping caches.
+ HalResult<Capabilities> getCapabilitiesInternal();
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+ sp<hardware::vibrator::IVibrator> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HALs.
+template <typename I>
+class HidlHalWrapper : public HalWrapper {
+public:
+ HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~HidlHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
+
+ HalResult<void> setAmplitude(int32_t amplitude) override final;
+ virtual HalResult<void> setExternalControl(bool enabled) override;
+
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
+
+ HalResult<Capabilities> getCapabilities() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
+
+ HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) override final;
+
+protected:
+ std::mutex mHandleMutex;
+ std::mutex mCapabilitiesMutex;
+ sp<I> mHandle GUARDED_BY(mHandleMutex);
+ std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+
+ // Loads directly from IVibrator handle, skipping the mCapabilities cache.
+ virtual HalResult<Capabilities> getCapabilitiesInternal();
+
+ template <class T>
+ using perform_fn =
+ hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
+ hardware::vibrator::V1_0::IVibrator::perform_cb);
+
+ template <class T>
+ HalResult<std::chrono::milliseconds> performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect,
+ hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+
+ sp<I> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.0.
+class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
+public:
+ HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
+public:
+ HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_1::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
+public:
+ HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_2::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_2() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
+public:
+ HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_3::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_3() = default;
+
+ HalResult<void> setExternalControl(bool enabled) override final;
+
+ HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override final;
+
+protected:
+ HalResult<Capabilities> getCapabilitiesInternal() override final;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALWRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
new file mode 100644
index 0000000..9033124
--- /dev/null
+++ b/services/vibratorservice/test/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "libvibratorservice_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "VibratorCallbackSchedulerTest.cpp",
+ "VibratorHalControllerTest.cpp",
+ "VibratorHalWrapperAidlTest.cpp",
+ "VibratorHalWrapperHidlV1_0Test.cpp",
+ "VibratorHalWrapperHidlV1_1Test.cpp",
+ "VibratorHalWrapperHidlV1_2Test.cpp",
+ "VibratorHalWrapperHidlV1_3Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libvibratorservice",
+ "libutils",
+ "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
new file mode 100644
index 0000000..aaeb8f9
--- /dev/null
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapperAidlTest"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <condition_variable>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::chrono::time_point;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorCallbackSchedulerTest : public Test {
+public:
+ void SetUp() override {
+ mScheduler = std::make_unique<vibrator::CallbackScheduler>();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.clear();
+ }
+
+protected:
+ std::mutex mMutex;
+ std::condition_variable_any mCondition;
+ std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr;
+ std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex);
+
+ std::function<void()> createCallback(int32_t id) {
+ return [=]() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.push_back(id);
+ }
+ mCondition.notify_all();
+ };
+ }
+
+ std::vector<int32_t> getExpiredCallbacks() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return std::vector<int32_t>(mExpiredCallbacks);
+ }
+
+ bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
+ time_point<steady_clock> expiration = steady_clock::now() + timeout;
+ while (steady_clock::now() < expiration) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (callbackCount <= mExpiredCallbacks.size()) {
+ return true;
+ }
+ mCondition.wait_until(mMutex, expiration);
+ }
+ return false;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
+ mScheduler->schedule(createCallback(1), 15ms);
+
+ // Not triggered before delay.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+
+ ASSERT_TRUE(waitForCallbacks(1, 10ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
+ mScheduler->schedule(createCallback(1), 10ms);
+ mScheduler->schedule(createCallback(2), 5ms);
+ mScheduler->schedule(createCallback(3), 1ms);
+
+ ASSERT_TRUE(waitForCallbacks(3, 15ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 5; i++) {
+ threads.push_back(std::thread(
+ [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ ASSERT_TRUE(waitForCallbacks(5, 25ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
+ mScheduler->schedule(createCallback(1), 5ms);
+ mScheduler.reset(nullptr);
+
+ // Should time out waiting for callback to run.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+}
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
new file mode 100644
index 0000000..2d55549
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalControllerTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+#include <cutils/atomic.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalController.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+static constexpr int MAX_ATTEMPTS = 2;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalWrapper : public vibrator::HalWrapper {
+public:
+ MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler)
+ : HalWrapper(scheduler) {}
+ virtual ~MockHalWrapper() = default;
+
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, on,
+ (milliseconds timeout, const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, off, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable,
+ (int32_t id, Effect effect, EffectStrength strength), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
+ MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
+ (Effect effect, EffectStrength strength,
+ const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect,
+ (const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback),
+ (override));
+
+ vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); }
+};
+
+class TestHalConnector : public vibrator::HalConnector {
+public:
+ TestHalConnector(int32_t* connectCounter, std::shared_ptr<MockHalWrapper> mockHal)
+ : mConnectCounter(connectCounter), mMockHal(std::move(mockHal)) {}
+ ~TestHalConnector() = default;
+
+ std::shared_ptr<vibrator::HalWrapper> connect(
+ std::shared_ptr<vibrator::CallbackScheduler>) override final {
+ android_atomic_inc(mConnectCounter);
+ return mMockHal;
+ }
+
+private:
+ int32_t* mConnectCounter;
+ std::shared_ptr<MockHalWrapper> mMockHal;
+};
+
+class FailingHalConnector : public vibrator::HalConnector {
+public:
+ FailingHalConnector(int32_t* connectCounter) : mConnectCounter(connectCounter) {}
+ ~FailingHalConnector() = default;
+
+ std::shared_ptr<vibrator::HalWrapper> connect(
+ std::shared_ptr<vibrator::CallbackScheduler>) override final {
+ android_atomic_inc(mConnectCounter);
+ return nullptr;
+ }
+
+private:
+ int32_t* mConnectCounter;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mConnectCounter = 0;
+ auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>();
+ mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler);
+ auto halConnector = std::make_unique<TestHalConnector>(&mConnectCounter, mMockHal);
+ mController = std::make_unique<vibrator::HalController>(std::move(halConnector),
+ std::move(callbackScheduler));
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ int32_t mConnectCounter;
+ std::shared_ptr<MockHalWrapper> mMockHal;
+ std::unique_ptr<vibrator::HalController> mController;
+
+ void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects,
+ vibrator::HalResult<void> voidResult,
+ vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
+ vibrator::HalResult<std::vector<Effect>> effectsResult,
+ vibrator::HalResult<milliseconds> durationResult) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1)))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), getCapabilities())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(capabilitiesResult));
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(effectsResult));
+ EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(durationResult));
+ EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
+
+ if (cardinality > 1) {
+ // One reconnection call after each failure.
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(11 * cardinality));
+ }
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalControllerTest, TestInit) {
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+
+ // Noop when wrapper was already initialized.
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+ std::vector<CompositeEffect> compositeEffects;
+ compositeEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ compositeEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
+ vibrator::HalResult<vibrator::Capabilities>::ok(
+ vibrator::Capabilities::ON_CALLBACK),
+ vibrator::HalResult<std::vector<Effect>>::ok(supportedEffects),
+ vibrator::HalResult<milliseconds>::ok(100ms));
+
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isOk());
+ ASSERT_TRUE(mController->off().isOk());
+ ASSERT_TRUE(mController->setAmplitude(255).isOk());
+ ASSERT_TRUE(mController->setExternalControl(true).isOk());
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isOk());
+
+ auto getCapabilitiesResult = mController->getCapabilities();
+ ASSERT_TRUE(getCapabilitiesResult.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value());
+
+ auto getSupportedEffectsResult = mController->getSupportedEffects();
+ ASSERT_TRUE(getSupportedEffectsResult.isOk());
+ ASSERT_EQ(supportedEffects, getSupportedEffectsResult.value());
+
+ auto performEffectResult =
+ mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
+ ASSERT_TRUE(performEffectResult.isOk());
+ ASSERT_EQ(100ms, performEffectResult.value());
+
+ ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+ setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(),
+ vibrator::HalResult<void>::unsupported(),
+ vibrator::HalResult<vibrator::Capabilities>::unsupported(),
+ vibrator::HalResult<std::vector<Effect>>::unsupported(),
+ vibrator::HalResult<milliseconds>::unsupported());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->off().isUnsupported());
+ ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+ ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(
+ mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+ .isUnsupported());
+ ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+ .isUnsupported());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
+ setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(),
+ vibrator::HalResult<void>::failed(),
+ vibrator::HalResult<vibrator::Capabilities>::failed(),
+ vibrator::HalResult<std::vector<Effect>>::failed(),
+ vibrator::HalResult<milliseconds>::failed());
+
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isFailed());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isFailed());
+ ASSERT_TRUE(mController->off().isFailed());
+ ASSERT_TRUE(mController->setAmplitude(255).isFailed());
+ ASSERT_TRUE(mController->setExternalControl(true).isFailed());
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed());
+ ASSERT_TRUE(mController->getCapabilities().isFailed());
+ ASSERT_TRUE(mController->getSupportedEffects().isFailed());
+ ASSERT_TRUE(
+ mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
+ ASSERT_TRUE(
+ mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
+
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+ }
+
+ ASSERT_EQ(0, mConnectCounter);
+ ASSERT_TRUE(mController->ping().isOk());
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ ASSERT_EQ(0, mConnectCounter);
+
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(10))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // Connector was called only by the first thread to use the api.
+ ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) {
+ auto failingHalConnector = std::make_unique<FailingHalConnector>(&mConnectCounter);
+ mController =
+ std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
+ ASSERT_EQ(0, mConnectCounter);
+
+ ASSERT_TRUE(mController->ping().isUnsupported());
+ ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->off().isUnsupported());
+ ASSERT_TRUE(mController->setAmplitude(255).isUnsupported());
+ ASSERT_TRUE(mController->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(
+ mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+ ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported());
+ ASSERT_TRUE(mController->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
+ .isUnsupported());
+ ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
+ .isUnsupported());
+
+ // One connection attempt per api call.
+ ASSERT_EQ(11, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) {
+ mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout);
+ return vibrator::HalResult<void>::ok();
+ });
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mController->on(10ms, callback).isOk());
+ ASSERT_TRUE(mController->ping().isFailed());
+ mMockHal.reset();
+ ASSERT_EQ(0, *callbackCounter.get());
+
+ // Callback triggered even after HalWrapper was reconnected.
+ std::this_thread::sleep_for(15ms);
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..0f2d7bc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapperAidlTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::binder::Status;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorCallback;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockBinder : public BBinder {
+public:
+ MOCK_METHOD(status_t, linkToDeath,
+ (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
+ MOCK_METHOD(status_t, unlinkToDeath,
+ (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient),
+ (override));
+ MOCK_METHOD(status_t, pingBinder, (), (override));
+};
+
+class MockIVibrator : public IVibrator {
+public:
+ MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(Status, off, (), (override));
+ MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, perform,
+ (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
+ (override));
+ MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+ (override));
+ MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
+ MOCK_METHOD(Status, compose,
+ (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
+ MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override {
+ mMockBinder = new StrictMock<MockBinder>();
+ mMockHal = new StrictMock<MockIVibrator>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibrator>> mMockHal = nullptr;
+ sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+ACTION(TriggerCallbackInArg1) {
+ if (arg1 != nullptr) {
+ arg1->onComplete();
+ }
+}
+
+ACTION(TriggerCallbackInArg2) {
+ if (arg2 != nullptr) {
+ arg2->onComplete();
+ }
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPing) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), onAsBinder())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(mMockBinder.get()));
+ EXPECT_CALL(*mMockBinder.get(), pingBinder()).Times(Exactly(1));
+ }
+
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(100), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status()));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(3))
+ .WillOnce(Return(Status()))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
+
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1000ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status())));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performComposedEffect(emptyEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(singleEffect, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..7eb4059
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_0Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_0 : public V1_0::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<void>, ping, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_0>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<void>(); })
+ .WillRepeatedly([]() {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(4))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillOnce([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ })
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(1).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed());
+ ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(2))
+ .WillOnce([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ })
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(false);
+ });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(true);
+ });
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TICK that is only available in v1.1
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..d887efc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_1Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_1 : public V1_1::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_1>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using THUD that is only available in v1.2
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
new file mode 100644
index 0000000..26d9350
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_2Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_2 : public V1_2::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_2Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_2>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TEXTURE_TICK that is only available in v1.3
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
new file mode 100644
index 0000000..5de6257
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2020 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 "VibratorHalWrapperHidlV1_3Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_3 : public V1_3::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_3,
+ (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_3Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_3>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+ result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ }
+
+ // Call to supportsAmplitudeControl failed.
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isFailed());
+
+ // Call to supportsExternalControl failed.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isFailed());
+
+ // Returns successful result from third call.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+
+ // Returns cached successful result.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
+ _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
+ _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
new file mode 100644
index 0000000..8d0b22e
--- /dev/null
+++ b/services/vibratorservice/test/test_utils.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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 VIBRATORSERVICE_UNITTEST_UTIL_H_
+#define VIBRATORSERVICE_UNITTEST_UTIL_H_
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockCallbackScheduler : public vibrator::CallbackScheduler {
+public:
+ MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
+ (override));
+};
+
+ACTION(TriggerSchedulerCallback) {
+ arg0();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class TestFactory {
+public:
+ static CompositeEffect createCompositeEffect(CompositePrimitive primitive,
+ std::chrono::milliseconds delay, float scale) {
+ CompositeEffect effect;
+ effect.primitive = primitive;
+ effect.delayMs = delay.count();
+ effect.scale = scale;
+ return effect;
+ }
+
+ static std::function<void()> createCountingCallback(int32_t* counter) {
+ return [counter]() { *counter += 1; };
+ }
+
+private:
+ TestFactory() = delete;
+ ~TestFactory() = delete;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace vibrator
+
+} // namespace android
+
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
index dcaa663..9cf4905 100644
--- a/services/vr/virtual_touchpad/Android.bp
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -14,6 +14,7 @@
]
header_libraries = [
+ "jni_headers",
"libdvr_headers",
]
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 5b9affd..80166c8 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1256,7 +1256,7 @@
ATRACE_CALL();
if (!EnsureInitialized())
- return VK_ERROR_INITIALIZATION_FAILED;
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
uint32_t count = GetLayerCount();
@@ -1280,7 +1280,7 @@
ATRACE_CALL();
if (!EnsureInitialized())
- return VK_ERROR_INITIALIZATION_FAILED;
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
if (pLayerName) {
const Layer* layer = FindLayer(pLayerName);
@@ -1456,6 +1456,11 @@
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
ATRACE_CALL();
+ // Load the driver here if not done yet. This api will be used in Zygote
+ // for Vulkan driver pre-loading because of the minimum overhead.
+ if (!EnsureInitialized())
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
*pApiVersion = VK_API_VERSION_1_1;
return VK_SUCCESS;
}
diff --git a/vulkan/libvulkan/api.h b/vulkan/libvulkan/api.h
index 416cba0..2a215d7 100644
--- a/vulkan/libvulkan/api.h
+++ b/vulkan/libvulkan/api.h
@@ -24,17 +24,34 @@
namespace vulkan {
namespace api {
-// clang-format off
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
+ VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
+ uint32_t* pPropertyCount,
+ VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion);
-// clang-format on
inline InstanceData& GetData(VkInstance instance) {
return driver::GetData(instance).opaque_api_data;
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 37b5368..d9a9427 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -621,6 +621,7 @@
// global functions
if (instance == VK_NULL_HANDLE) {
if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+ if (strcmp(pName, "vkGetInstanceProcAddr") == 0) return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr);
if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion);
if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceLayerProperties);
if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties);
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index a5f0c9f..e3fc67e 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -95,15 +95,15 @@
class CreateInfoWrapper {
public:
CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator);
CreateInfoWrapper(VkPhysicalDevice physical_dev,
const VkDeviceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator);
~CreateInfoWrapper();
VkResult Validate();
- void DowngradeApiVersion();
- void UpgradeDeviceCoreApiVersion(uint32_t api_version);
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const;
const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const;
@@ -120,8 +120,8 @@
uint32_t name_count;
};
+ VkResult SanitizeApiVersion();
VkResult SanitizePNext();
-
VkResult SanitizeLayers();
VkResult SanitizeExtensions();
@@ -133,6 +133,8 @@
const bool is_instance_;
const VkAllocationCallbacks& allocator_;
+ const uint32_t loader_api_version_;
+ const uint32_t icd_api_version_;
VkPhysicalDevice physical_dev_;
@@ -161,7 +163,7 @@
}
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
- "ro.hardware." HWVULKAN_HARDWARE_MODULE_ID,
+ "ro.hardware.vulkan",
"ro.board.platform",
}};
@@ -252,18 +254,6 @@
result = LoadUpdatedDriver(&module);
if (result == -ENOENT) {
result = LoadBuiltinDriver(&module);
- if (result != 0) {
- // -ENOENT means the sphal namespace doesn't exist, not that there
- // is a problem with the driver.
- ALOGW_IF(
- result != -ENOENT,
- "Failed to load Vulkan driver into sphal namespace. This "
- "usually means the driver has forbidden library dependencies."
- "Please fix, this will soon stop working.");
- result =
- hw_get_module(HWVULKAN_HARDWARE_MODULE_ID,
- reinterpret_cast<const hw_module_t**>(&module));
- }
}
if (result != 0) {
android::GraphicsEnv::getInstance().setDriverLoaded(
@@ -336,32 +326,27 @@
}
CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator)
: is_instance_(true),
allocator_(allocator),
+ loader_api_version_(VK_API_VERSION_1_1),
+ icd_api_version_(icd_api_version),
physical_dev_(VK_NULL_HANDLE),
instance_info_(create_info),
- extension_filter_() {
- // instance core versions need to match the loader api version
- for (uint32_t i = ProcHook::EXTENSION_CORE_1_0;
- i != ProcHook::EXTENSION_COUNT; ++i) {
- hook_extensions_.set(i);
- hal_extensions_.set(i);
- }
-}
+ extension_filter_() {}
CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev,
const VkDeviceCreateInfo& create_info,
+ uint32_t icd_api_version,
const VkAllocationCallbacks& allocator)
: is_instance_(false),
allocator_(allocator),
+ loader_api_version_(VK_API_VERSION_1_1),
+ icd_api_version_(icd_api_version),
physical_dev_(physical_dev),
dev_info_(create_info),
- extension_filter_() {
- // initialize with baseline core API version
- hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
- hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
-}
+ extension_filter_() {}
CreateInfoWrapper::~CreateInfoWrapper() {
allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts);
@@ -369,7 +354,9 @@
}
VkResult CreateInfoWrapper::Validate() {
- VkResult result = SanitizePNext();
+ VkResult result = SanitizeApiVersion();
+ if (result == VK_SUCCESS)
+ result = SanitizePNext();
if (result == VK_SUCCESS)
result = SanitizeLayers();
if (result == VK_SUCCESS)
@@ -396,6 +383,22 @@
return &dev_info_;
}
+VkResult CreateInfoWrapper::SanitizeApiVersion() {
+ if (!is_instance_ || !instance_info_.pApplicationInfo)
+ return VK_SUCCESS;
+
+ if (icd_api_version_ > VK_API_VERSION_1_0 ||
+ instance_info_.pApplicationInfo->apiVersion < VK_API_VERSION_1_1)
+ return VK_SUCCESS;
+
+ // override apiVersion to avoid error return from 1.0 icd
+ application_info_ = *instance_info_.pApplicationInfo;
+ application_info_.apiVersion = VK_API_VERSION_1_0;
+ instance_info_.pApplicationInfo = &application_info_;
+
+ return VK_SUCCESS;
+}
+
VkResult CreateInfoWrapper::SanitizePNext() {
const struct StructHeader {
VkStructureType type;
@@ -443,15 +446,33 @@
: dev_info_.ppEnabledExtensionNames;
auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount
: dev_info_.enabledExtensionCount;
- if (!ext_count)
- return VK_SUCCESS;
VkResult result = InitExtensionFilter();
if (result != VK_SUCCESS)
return result;
- for (uint32_t i = 0; i < ext_count; i++)
- FilterExtension(ext_names[i]);
+ if (is_instance_ && icd_api_version_ < loader_api_version_) {
+ for (uint32_t i = 0; i < ext_count; i++) {
+ // Upon api downgrade, skip the promoted instance extensions in the
+ // first pass to avoid duplicate extensions.
+ const std::optional<uint32_t> version =
+ GetInstanceExtensionPromotedVersion(ext_names[i]);
+ if (version && *version > icd_api_version_ &&
+ *version <= loader_api_version_)
+ continue;
+
+ FilterExtension(ext_names[i]);
+ }
+
+ // Enable the required extensions to support core functionalities.
+ const auto promoted_extensions = GetPromotedInstanceExtensions(
+ icd_api_version_, loader_api_version_);
+ for (const auto& promoted_extension : promoted_extensions)
+ FilterExtension(promoted_extension);
+ } else {
+ for (uint32_t i = 0; i < ext_count; i++)
+ FilterExtension(ext_names[i]);
+ }
// Enable device extensions that contain physical-device commands, so that
// vkGetInstanceProcAddr will return those physical-device commands.
@@ -459,6 +480,23 @@
hook_extensions_.set(ProcHook::KHR_swapchain);
}
+ const uint32_t api_version =
+ is_instance_ ? loader_api_version_
+ : std::min(icd_api_version_, loader_api_version_);
+ switch (api_version) {
+ case VK_API_VERSION_1_1:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
+ [[clang::fallthrough]];
+ case VK_API_VERSION_1_0:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0);
+ break;
+ default:
+ ALOGE("Unknown API version[%u]", api_version);
+ break;
+ }
+
ext_names = extension_filter_.names;
ext_count = extension_filter_.name_count;
@@ -516,10 +554,20 @@
filter.ext_count = count;
// allocate name array
- uint32_t enabled_ext_count = (is_instance_)
- ? instance_info_.enabledExtensionCount
- : dev_info_.enabledExtensionCount;
- count = std::min(filter.ext_count, enabled_ext_count);
+ if (is_instance_) {
+ uint32_t enabled_ext_count = instance_info_.enabledExtensionCount;
+
+ // It requires enabling additional promoted extensions to downgrade api,
+ // so we reserve enough space here.
+ if (icd_api_version_ < loader_api_version_) {
+ enabled_ext_count += CountPromotedInstanceExtensions(
+ icd_api_version_, loader_api_version_);
+ }
+
+ count = std::min(filter.ext_count, enabled_ext_count);
+ } else {
+ count = std::min(filter.ext_count, dev_info_.enabledExtensionCount);
+ }
filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
@@ -547,6 +595,10 @@
hook_extensions_.set(ext_bit);
break;
case ProcHook::KHR_get_physical_device_properties2:
+ case ProcHook::KHR_device_group_creation:
+ case ProcHook::KHR_external_memory_capabilities:
+ case ProcHook::KHR_external_semaphore_capabilities:
+ case ProcHook::KHR_external_fence_capabilities:
case ProcHook::EXTENSION_UNKNOWN:
// Extensions we don't need to do anything about at this level
break;
@@ -603,6 +655,10 @@
case ProcHook::KHR_android_surface:
case ProcHook::KHR_get_physical_device_properties2:
+ case ProcHook::KHR_device_group_creation:
+ case ProcHook::KHR_external_memory_capabilities:
+ case ProcHook::KHR_external_semaphore_capabilities:
+ case ProcHook::KHR_external_fence_capabilities:
case ProcHook::KHR_get_surface_capabilities2:
case ProcHook::KHR_surface:
case ProcHook::EXT_debug_report:
@@ -648,40 +704,6 @@
}
}
-void CreateInfoWrapper::DowngradeApiVersion() {
- // If pApplicationInfo is NULL, apiVersion is assumed to be 1.0:
- if (instance_info_.pApplicationInfo) {
- application_info_ = *instance_info_.pApplicationInfo;
- instance_info_.pApplicationInfo = &application_info_;
- application_info_.apiVersion = VK_API_VERSION_1_0;
- }
-}
-
-void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) {
- ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper.");
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
- api_version ^= VK_VERSION_PATCH(api_version);
-#pragma clang diagnostic pop
-
- // cap the API version to the loader supported highest version
- if (api_version > VK_API_VERSION_1_1)
- api_version = VK_API_VERSION_1_1;
-
- switch (api_version) {
- case VK_API_VERSION_1_1:
- hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
- hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
- [[clang::fallthrough]];
- case VK_API_VERSION_1_0:
- break;
- default:
- ALOGD("Unknown upgrade API version[%u]", api_version);
- break;
- }
-}
-
VKAPI_ATTR void* DefaultAllocate(void*,
size_t size,
size_t alignment,
@@ -913,21 +935,14 @@
return result;
}
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
- VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) {
- const InstanceData& data = GetData(physicalDevice);
-
- // GPDP2 must be present and enabled on the instance.
- if (!data.driver.GetPhysicalDeviceProperties2KHR &&
- !data.driver.GetPhysicalDeviceProperties2)
- return false;
-
+ VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
// Request the android-specific presentation properties via GPDP2
- VkPhysicalDeviceProperties2KHR properties = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+ VkPhysicalDeviceProperties2 properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
presentation_properties,
- {}
+ {},
};
#pragma clang diagnostic push
@@ -938,14 +953,7 @@
presentation_properties->pNext = nullptr;
presentation_properties->sharedImage = VK_FALSE;
- if (data.driver.GetPhysicalDeviceProperties2KHR) {
- data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
- &properties);
- } else {
- data.driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
- }
-
- return true;
+ GetPhysicalDeviceProperties2(physicalDevice, &properties);
}
VkResult EnumerateDeviceExtensionProperties(
@@ -967,8 +975,8 @@
}
VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
- if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
- presentation_properties.sharedImage) {
+ QueryPresentationProperties(physicalDevice, &presentation_properties);
+ if (presentation_properties.sharedImage) {
loader_extensions.push_back({
VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
@@ -1039,49 +1047,32 @@
const VkAllocationCallbacks& data_allocator =
(pAllocator) ? *pAllocator : GetDefaultAllocator();
- CreateInfoWrapper wrapper(*pCreateInfo, data_allocator);
- VkResult result = wrapper.Validate();
- if (result != VK_SUCCESS)
- return result;
-
- ATRACE_BEGIN("AllocateInstanceData");
- InstanceData* data = AllocateInstanceData(data_allocator);
- ATRACE_END();
- if (!data)
- return VK_ERROR_OUT_OF_HOST_MEMORY;
-
- data->hook_extensions |= wrapper.GetHookExtensions();
-
- ATRACE_BEGIN("autoDowngradeApiVersion");
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
- uint32_t api_version = ((pCreateInfo->pApplicationInfo)
- ? pCreateInfo->pApplicationInfo->apiVersion
- : VK_API_VERSION_1_0);
- uint32_t api_major_version = VK_VERSION_MAJOR(api_version);
- uint32_t api_minor_version = VK_VERSION_MINOR(api_version);
- uint32_t icd_api_version;
+ VkResult result = VK_SUCCESS;
+ uint32_t icd_api_version = VK_API_VERSION_1_0;
PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version =
reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
Hal::Device().GetInstanceProcAddr(nullptr,
"vkEnumerateInstanceVersion"));
- if (!pfn_enumerate_instance_version) {
- icd_api_version = VK_API_VERSION_1_0;
- } else {
+ if (pfn_enumerate_instance_version) {
ATRACE_BEGIN("pfn_enumerate_instance_version");
result = (*pfn_enumerate_instance_version)(&icd_api_version);
ATRACE_END();
- }
- uint32_t icd_api_major_version = VK_VERSION_MAJOR(icd_api_version);
- uint32_t icd_api_minor_version = VK_VERSION_MINOR(icd_api_version);
+ if (result != VK_SUCCESS)
+ return result;
- if ((icd_api_major_version == 1) && (icd_api_minor_version == 0) &&
- ((api_major_version > 1) || (api_minor_version > 0))) {
- api_version = VK_API_VERSION_1_0;
- wrapper.DowngradeApiVersion();
+ icd_api_version ^= VK_VERSION_PATCH(icd_api_version);
}
-#pragma clang diagnostic pop
- ATRACE_END();
+
+ CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator);
+ result = wrapper.Validate();
+ if (result != VK_SUCCESS)
+ return result;
+
+ InstanceData* data = AllocateInstanceData(data_allocator);
+ if (!data)
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+ data->hook_extensions |= wrapper.GetHookExtensions();
// call into the driver
VkInstance instance;
@@ -1145,7 +1136,16 @@
const VkAllocationCallbacks& data_allocator =
(pAllocator) ? *pAllocator : instance_data.allocator;
- CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator);
+ VkPhysicalDeviceProperties properties;
+ ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
+ instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
+ &properties);
+ ATRACE_END();
+
+ CreateInfoWrapper wrapper(
+ physicalDevice, *pCreateInfo,
+ properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion),
+ data_allocator);
VkResult result = wrapper.Validate();
if (result != VK_SUCCESS)
return result;
@@ -1157,13 +1157,6 @@
if (!data)
return VK_ERROR_OUT_OF_HOST_MEMORY;
- VkPhysicalDeviceProperties properties;
- ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
- instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
- &properties);
- ATRACE_END();
-
- wrapper.UpgradeDeviceCoreApiVersion(properties.apiVersion);
data->hook_extensions |= wrapper.GetHookExtensions();
// call into the driver
@@ -1260,7 +1253,8 @@
VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
- if (!data.driver.EnumeratePhysicalDeviceGroups) {
+ if (!data.driver.EnumeratePhysicalDeviceGroups &&
+ !data.driver.EnumeratePhysicalDeviceGroupsKHR) {
uint32_t device_count = 0;
result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
if (result < 0)
@@ -1292,9 +1286,15 @@
pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
}
} else {
- result = data.driver.EnumeratePhysicalDeviceGroups(
- instance, pPhysicalDeviceGroupCount,
- pPhysicalDeviceGroupProperties);
+ if (data.driver.EnumeratePhysicalDeviceGroups) {
+ result = data.driver.EnumeratePhysicalDeviceGroups(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ } else {
+ result = data.driver.EnumeratePhysicalDeviceGroupsKHR(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ }
if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
*pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
@@ -1335,10 +1335,10 @@
if (*pQueue != VK_NULL_HANDLE) SetData(*pQueue, data);
}
-VKAPI_ATTR VkResult
-AllocateCommandBuffers(VkDevice device,
- const VkCommandBufferAllocateInfo* pAllocateInfo,
- VkCommandBuffer* pCommandBuffers) {
+VkResult AllocateCommandBuffers(
+ VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers) {
ATRACE_CALL();
const auto& data = GetData(device);
@@ -1353,10 +1353,10 @@
return result;
}
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
- uint32_t submitCount,
- const VkSubmitInfo* pSubmits,
- VkFence fence) {
+VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence) {
ATRACE_CALL();
const auto& data = GetData(queue);
@@ -1364,5 +1364,198 @@
return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
}
+void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2* pFeatures) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceFeatures2) {
+ driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+ return;
+ }
+
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+}
+
+void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2* pProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceProperties2) {
+ driver.GetPhysicalDeviceProperties2(physicalDevice, pProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceProperties2KHR(physicalDevice, pProperties);
+}
+
+void GetPhysicalDeviceFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2* pFormatProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceFormatProperties2) {
+ driver.GetPhysicalDeviceFormatProperties2(physicalDevice, format,
+ pFormatProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format,
+ pFormatProperties);
+}
+
+VkResult GetPhysicalDeviceImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+ VkImageFormatProperties2* pImageFormatProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceImageFormatProperties2) {
+ return driver.GetPhysicalDeviceImageFormatProperties2(
+ physicalDevice, pImageFormatInfo, pImageFormatProperties);
+ }
+
+ return driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ physicalDevice, pImageFormatInfo, pImageFormatProperties);
+}
+
+void GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2* pQueueFamilyProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceQueueFamilyProperties2) {
+ driver.GetPhysicalDeviceQueueFamilyProperties2(
+ physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceQueueFamilyProperties2KHR(
+ physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+void GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceMemoryProperties2) {
+ driver.GetPhysicalDeviceMemoryProperties2(physicalDevice,
+ pMemoryProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceMemoryProperties2KHR(physicalDevice,
+ pMemoryProperties);
+}
+
+void GetPhysicalDeviceSparseImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2* pProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceSparseImageFormatProperties2) {
+ driver.GetPhysicalDeviceSparseImageFormatProperties2(
+ physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+ return;
+ }
+
+ driver.GetPhysicalDeviceSparseImageFormatProperties2KHR(
+ physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+}
+
+void GetPhysicalDeviceExternalBufferProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+ VkExternalBufferProperties* pExternalBufferProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalBufferProperties) {
+ driver.GetPhysicalDeviceExternalBufferProperties(
+ physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalBufferPropertiesKHR) {
+ driver.GetPhysicalDeviceExternalBufferPropertiesKHR(
+ physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+ return;
+ }
+
+ memset(&pExternalBufferProperties->externalMemoryProperties, 0,
+ sizeof(VkExternalMemoryProperties));
+}
+
+void GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalSemaphoreProperties) {
+ driver.GetPhysicalDeviceExternalSemaphoreProperties(
+ physicalDevice, pExternalSemaphoreInfo,
+ pExternalSemaphoreProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR) {
+ driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR(
+ physicalDevice, pExternalSemaphoreInfo,
+ pExternalSemaphoreProperties);
+ return;
+ }
+
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+ pExternalSemaphoreProperties->compatibleHandleTypes = 0;
+ pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+}
+
+void GetPhysicalDeviceExternalFenceProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+ VkExternalFenceProperties* pExternalFenceProperties) {
+ ATRACE_CALL();
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceExternalFenceProperties) {
+ driver.GetPhysicalDeviceExternalFenceProperties(
+ physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+ return;
+ }
+
+ if (driver.GetPhysicalDeviceExternalFencePropertiesKHR) {
+ driver.GetPhysicalDeviceExternalFencePropertiesKHR(
+ physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+ return;
+ }
+
+ pExternalFenceProperties->exportFromImportedHandleTypes = 0;
+ pExternalFenceProperties->compatibleHandleTypes = 0;
+ pExternalFenceProperties->externalFenceFeatures = 0;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 23c717c..14c516b 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -103,30 +103,95 @@
bool OpenHAL();
const VkAllocationCallbacks& GetDefaultAllocator();
-bool QueryPresentationProperties(
+void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
- VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties);
+ VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
-// clang-format off
-VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
-VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
-
-VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
-VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
-VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
-
-VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
-
-VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
-VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
-// clang-format on
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
+ const char* pName);
+VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
+ const char* pName);
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult
+EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,
+ const char* pLayerName,
+ uint32_t* pPropertyCount,
+ VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device,
+ const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult
+EnumeratePhysicalDevices(VkInstance instance,
+ uint32_t* pPhysicalDeviceCount,
+ VkPhysicalDevice* pPhysicalDevices);
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(
+ VkInstance instance,
+ uint32_t* pPhysicalDeviceGroupCount,
+ VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
+VKAPI_ATTR void GetDeviceQueue(VkDevice device,
+ uint32_t queueFamilyIndex,
+ uint32_t queueIndex,
+ VkQueue* pQueue);
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device,
+ const VkDeviceQueueInfo2* pQueueInfo,
+ VkQueue* pQueue);
+VKAPI_ATTR VkResult
+AllocateCommandBuffers(VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+ VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t* pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo,
+ uint32_t* pPropertyCount,
+ VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+ VkExternalBufferProperties* pExternalBufferProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
+ VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(
+ VkPhysicalDevice physicalDevice,
+ const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo,
+ VkExternalFenceProperties* pExternalFenceProperties);
template <typename DispatchableType>
void StaticAssertDispatchable(DispatchableType) {
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 52205e9..5f37a50 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -363,6 +363,55 @@
reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
},
{
+ "vkGetPhysicalDeviceExternalBufferProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalBufferProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceExternalFenceProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalFenceProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceExternalSemaphoreProperties",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalSemaphoreProperties),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceFeatures2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFeatures2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFormatProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceImageFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceImageFormatProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceMemoryProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceMemoryProperties2),
+ nullptr,
+ },
+ {
"vkGetPhysicalDevicePresentRectanglesKHR",
ProcHook::INSTANCE,
ProcHook::KHR_swapchain,
@@ -370,6 +419,27 @@
nullptr,
},
{
+ "vkGetPhysicalDeviceProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceQueueFamilyProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceQueueFamilyProperties2),
+ nullptr,
+ },
+ {
+ "vkGetPhysicalDeviceSparseImageFormatProperties2",
+ ProcHook::INSTANCE,
+ ProcHook::EXTENSION_CORE_1_1,
+ reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSparseImageFormatProperties2),
+ nullptr,
+ },
+ {
"vkGetPhysicalDeviceSurfaceCapabilities2KHR",
ProcHook::INSTANCE,
ProcHook::KHR_get_surface_capabilities2,
@@ -480,10 +550,9 @@
} // namespace
const ProcHook* GetProcHook(const char* name) {
- const auto& begin = g_proc_hooks;
- const auto& end =
- g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
- const auto hook = std::lower_bound(
+ auto begin = std::cbegin(g_proc_hooks);
+ auto end = std::cend(g_proc_hooks);
+ auto hook = std::lower_bound(
begin, end, name,
[](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -505,6 +574,10 @@
if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
+ if (strcmp(name, "VK_KHR_device_group_creation") == 0) return ProcHook::KHR_device_group_creation;
+ if (strcmp(name, "VK_KHR_external_memory_capabilities") == 0) return ProcHook::KHR_external_memory_capabilities;
+ if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities;
+ if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
@@ -543,9 +616,28 @@
INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+ INIT_PROC(false, instance, GetPhysicalDeviceFeatures2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFeatures2KHR);
INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceImageFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceImageFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceQueueFamilyProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceQueueFamilyProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceMemoryProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2);
+ INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceSparseImageFormatProperties2KHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties);
+ INIT_PROC_EXT(KHR_external_memory_capabilities, true, instance, GetPhysicalDeviceExternalBufferPropertiesKHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
+ INIT_PROC_EXT(KHR_external_semaphore_capabilities, true, instance, GetPhysicalDeviceExternalSemaphorePropertiesKHR);
+ INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
+ INIT_PROC_EXT(KHR_external_fence_capabilities, true, instance, GetPhysicalDeviceExternalFencePropertiesKHR);
INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
+ INIT_PROC_EXT(KHR_device_group_creation, true, instance, EnumeratePhysicalDeviceGroupsKHR);
// clang-format on
return success;
@@ -577,5 +669,53 @@
return success;
}
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+ // clang-format off
+ std::make_pair("VK_KHR_device_group_creation", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_fence_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_memory_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_external_semaphore_capabilities", VK_API_VERSION_1_1),
+ std::make_pair("VK_KHR_get_physical_device_properties2", VK_API_VERSION_1_1),
+ // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ auto iter =
+ std::lower_bound(begin, end, name,
+ [](const std::pair<const char*, uint32_t>& e,
+ const char* n) { return strcmp(e.first, n) < 0; });
+ return (iter < end && strcmp(iter->first, name) == 0)
+ ? std::optional<uint32_t>(iter->second)
+ : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ uint32_t count = 0;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ count++;
+
+ return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ std::vector<const char*> extensions;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ extensions.emplace_back(iter->first);
+
+ return extensions;
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 43c4d14..1aba674 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -23,6 +23,8 @@
#include <vulkan/vulkan.h>
#include <bitset>
+#include <optional>
+#include <vector>
namespace vulkan {
namespace driver {
@@ -48,6 +50,10 @@
ANDROID_external_memory_android_hardware_buffer,
KHR_bind_memory2,
KHR_get_physical_device_properties2,
+ KHR_device_group_creation,
+ KHR_external_memory_capabilities,
+ KHR_external_semaphore_capabilities,
+ KHR_external_fence_capabilities,
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
@@ -74,9 +80,28 @@
PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+ PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2;
+ PFN_vkGetPhysicalDeviceFeatures2KHR GetPhysicalDeviceFeatures2KHR;
PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
+ PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2;
+ PFN_vkGetPhysicalDeviceFormatProperties2KHR GetPhysicalDeviceFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2;
+ PFN_vkGetPhysicalDeviceImageFormatProperties2KHR GetPhysicalDeviceImageFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2;
+ PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR GetPhysicalDeviceQueueFamilyProperties2KHR;
+ PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2;
+ PFN_vkGetPhysicalDeviceMemoryProperties2KHR GetPhysicalDeviceMemoryProperties2KHR;
+ PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2;
+ PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR GetPhysicalDeviceSparseImageFormatProperties2KHR;
+ PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties;
+ PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR GetPhysicalDeviceExternalBufferPropertiesKHR;
+ PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties;
+ PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR GetPhysicalDeviceExternalSemaphorePropertiesKHR;
+ PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
+ PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR GetPhysicalDeviceExternalFencePropertiesKHR;
PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
+ PFN_vkEnumeratePhysicalDeviceGroupsKHR EnumeratePhysicalDeviceGroupsKHR;
// clang-format on
};
@@ -109,6 +134,12 @@
PFN_vkGetDeviceProcAddr get_proc,
const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d3ed88d..0f791c9 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -881,11 +881,10 @@
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
VkPhysicalDevicePresentationPropertiesANDROID present_properties;
- if (QueryPresentationProperties(pdev, &present_properties)) {
- if (present_properties.sharedImage) {
- present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
- present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
- }
+ QueryPresentationProperties(pdev, &present_properties);
+ if (present_properties.sharedImage) {
+ present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
+ present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
}
uint32_t num_modes = uint32_t(present_modes.size());
@@ -970,7 +969,6 @@
strerror(-err), err);
}
- // TODO(b/143294545): Return something better than "whole window"
pRects[0].offset.x = 0;
pRects[0].offset.y = 0;
pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width),
diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py
index 7c39075..be24172 100644
--- a/vulkan/scripts/api_generator.py
+++ b/vulkan/scripts/api_generator.py
@@ -152,7 +152,9 @@
if (instance == VK_NULL_HANDLE) {\n""")
for cmd in gencom.command_list:
- if gencom.is_globally_dispatched(cmd):
+ # vkGetInstanceProcAddr(nullptr, "vkGetInstanceProcAddr") is effectively
+ # globally dispatched
+ if gencom.is_globally_dispatched(cmd) or cmd == 'vkGetInstanceProcAddr':
f.write(gencom.indent(2) +
'if (strcmp(pName, \"' + cmd +
'\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index a64a702..6a73023 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -40,6 +40,10 @@
'VK_ANDROID_external_memory_android_hardware_buffer',
'VK_KHR_bind_memory2',
'VK_KHR_get_physical_device_properties2',
+ 'VK_KHR_device_group_creation',
+ 'VK_KHR_external_memory_capabilities',
+ 'VK_KHR_external_semaphore_capabilities',
+ 'VK_KHR_external_fence_capabilities',
]
# Functions needed at vulkan::driver level.
@@ -71,12 +75,41 @@
'vkDestroyImage',
'vkGetPhysicalDeviceProperties',
- 'vkGetPhysicalDeviceProperties2',
- 'vkGetPhysicalDeviceProperties2KHR',
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2',
'vkBindImageMemory2KHR',
+
+ # For promoted VK_KHR_device_group_creation
+ 'vkEnumeratePhysicalDeviceGroupsKHR',
+
+ # For promoted VK_KHR_get_physical_device_properties2
+ 'vkGetPhysicalDeviceFeatures2',
+ 'vkGetPhysicalDeviceFeatures2KHR',
+ 'vkGetPhysicalDeviceProperties2',
+ 'vkGetPhysicalDeviceProperties2KHR',
+ 'vkGetPhysicalDeviceFormatProperties2',
+ 'vkGetPhysicalDeviceFormatProperties2KHR',
+ 'vkGetPhysicalDeviceImageFormatProperties2',
+ 'vkGetPhysicalDeviceImageFormatProperties2KHR',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
+ 'vkGetPhysicalDeviceMemoryProperties2',
+ 'vkGetPhysicalDeviceMemoryProperties2KHR',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR',
+
+ # For promoted VK_KHR_external_memory_capabilities
+ 'vkGetPhysicalDeviceExternalBufferProperties',
+ 'vkGetPhysicalDeviceExternalBufferPropertiesKHR',
+
+ # For promoted VK_KHR_external_semaphore_capabilities
+ 'vkGetPhysicalDeviceExternalSemaphoreProperties',
+ 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR',
+
+ # For promoted VK_KHR_external_fence_capabilities
+ 'vkGetPhysicalDeviceExternalFenceProperties',
+ 'vkGetPhysicalDeviceExternalFencePropertiesKHR',
]
# Functions intercepted at vulkan::driver level.
@@ -106,6 +139,24 @@
# VK_KHR_swapchain v69 requirement
'vkBindImageMemory2',
'vkBindImageMemory2KHR',
+
+ # For promoted VK_KHR_get_physical_device_properties2
+ 'vkGetPhysicalDeviceFeatures2',
+ 'vkGetPhysicalDeviceProperties2',
+ 'vkGetPhysicalDeviceFormatProperties2',
+ 'vkGetPhysicalDeviceImageFormatProperties2',
+ 'vkGetPhysicalDeviceQueueFamilyProperties2',
+ 'vkGetPhysicalDeviceMemoryProperties2',
+ 'vkGetPhysicalDeviceSparseImageFormatProperties2',
+
+ # For promoted VK_KHR_external_memory_capabilities
+ 'vkGetPhysicalDeviceExternalBufferProperties',
+
+ # For promoted VK_KHR_external_semaphore_capabilities
+ 'vkGetPhysicalDeviceExternalSemaphoreProperties',
+
+ # For promoted VK_KHR_external_fence_capabilities
+ 'vkGetPhysicalDeviceExternalFenceProperties',
]
@@ -162,6 +213,8 @@
#include <vulkan/vulkan.h>
#include <bitset>
+#include <optional>
+#include <vector>
namespace vulkan {
namespace driver {
@@ -229,6 +282,12 @@
PFN_vkGetDeviceProcAddr get_proc,
const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name);
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version);
+
} // namespace driver
} // namespace vulkan
@@ -464,10 +523,9 @@
} // namespace
const ProcHook* GetProcHook(const char* name) {
- const auto& begin = g_proc_hooks;
- const auto& end =
- g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
- const auto hook = std::lower_bound(
+ auto begin = std::cbegin(g_proc_hooks);
+ auto end = std::cend(g_proc_hooks);
+ auto hook = std::lower_bound(
begin, end, name,
[](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
@@ -539,6 +597,54 @@
return success;
}
+const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = {
+ // clang-format off\n""")
+
+ for key, value in sorted(gencom.promoted_inst_ext_dict.items()):
+ f.write(gencom.indent(1) + 'std::make_pair("' + key + '", ' + value + '),\n')
+
+ f.write("""\
+ // clang-format on
+};
+
+std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ auto iter =
+ std::lower_bound(begin, end, name,
+ [](const std::pair<const char*, uint32_t>& e,
+ const char* n) { return strcmp(e.first, n) < 0; });
+ return (iter < end && strcmp(iter->first, name) == 0)
+ ? std::optional<uint32_t>(iter->second)
+ : std::nullopt;
+}
+
+uint32_t CountPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ uint32_t count = 0;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ count++;
+
+ return count;
+}
+
+std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version,
+ uint32_t end_version) {
+ auto begin = std::cbegin(g_promoted_instance_extensions);
+ auto end = std::cend(g_promoted_instance_extensions);
+ std::vector<const char*> extensions;
+
+ for (auto iter = begin; iter != end; iter++)
+ if (iter->second > begin_version && iter->second <= end_version)
+ extensions.emplace_back(iter->first);
+
+ return extensions;
+}
+
} // namespace driver
} // namespace vulkan\n""")
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index cf370fa..ef021f2 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -22,7 +22,7 @@
import xml.etree.ElementTree as element_tree
# Extensions unsupported on Android.
-_BLACKLISTED_EXTENSIONS = [
+_BLOCKED_EXTENSIONS = [
'VK_EXT_acquire_xlib_display',
'VK_EXT_direct_mode_display',
'VK_EXT_display_control',
@@ -97,6 +97,9 @@
# Dict for mapping a function to the core Vulkan API version.
version_dict = {}
+# Dict for mapping a promoted instance extension to the core Vulkan API version.
+promoted_inst_ext_dict = {}
+
def indent(num):
"""Returns the requested indents.
@@ -183,6 +186,15 @@
return version[11:]
+def version_2_api_version(version):
+ """Returns the api version from a version string.
+
+ Args:
+ version: Vulkan version string.
+ """
+ return 'VK_API' + version[2:]
+
+
def is_function_supported(cmd):
"""Returns true if a function is core or from a supportable extension.
@@ -192,7 +204,7 @@
if cmd not in extension_dict:
return True
else:
- if extension_dict[cmd] not in _BLACKLISTED_EXTENSIONS:
+ if extension_dict[cmd] not in _BLOCKED_EXTENSIONS:
return True
return False
@@ -327,6 +339,7 @@
return_type_dict
version_code_list
version_dict
+ promoted_inst_ext_dict
"""
registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..',
'external', 'vulkan-headers', 'registry', 'vk.xml')
@@ -379,6 +392,10 @@
apiversion = ''
if extension.tag == 'extension':
extname = extension.get('name')
+ if (extension.get('type') == 'instance' and
+ extension.get('promotedto') is not None):
+ promoted_inst_ext_dict[extname] = \
+ version_2_api_version(extension.get('promotedto'))
for req in extension:
if req.get('feature') is not None:
apiversion = req.get('feature')
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index a283b83..52e7bee 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -158,10 +158,7 @@
VkJsonInstance* instance,
std::string* errors);
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
- VkPhysicalDevice device,
- uint32_t instanceExtensionCount,
- const char* const* instanceExtensions);
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device);
std::string VkJsonDeviceToJson(const VkJsonDevice& device);
bool VkJsonDeviceFromJson(const std::string& json,
VkJsonDevice* device,
@@ -177,7 +174,7 @@
typedef VkJsonDevice VkJsonAllProperties;
inline VkJsonAllProperties VkJsonGetAllProperties(
VkPhysicalDevice physicalDevice) {
- return VkJsonGetDevice(VK_NULL_HANDLE, physicalDevice, 0, nullptr);
+ return VkJsonGetDevice(physicalDevice);
}
inline std::string VkJsonAllPropertiesToJson(
const VkJsonAllProperties& properties) {
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 84cfe5e..ace713b 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -28,8 +28,6 @@
#include <utility>
namespace {
-const char* kSupportedInstanceExtensions[] = {
- "VK_KHR_get_physical_device_properties2"};
bool EnumerateExtensions(const char* layer_name,
std::vector<VkExtensionProperties>* extensions) {
@@ -47,15 +45,6 @@
}
bool HasExtension(const char* extension_name,
- uint32_t count,
- const char* const* extensions) {
- return std::find_if(extensions, extensions + count,
- [extension_name](const char* extension) {
- return strcmp(extension, extension_name) == 0;
- }) != extensions + count;
-}
-
-bool HasExtension(const char* extension_name,
const std::vector<VkExtensionProperties>& extensions) {
return std::find_if(extensions.cbegin(), extensions.cend(),
[extension_name](const VkExtensionProperties& extension) {
@@ -65,27 +54,9 @@
}
} // anonymous namespace
-VkJsonDevice VkJsonGetDevice(VkInstance instance,
- VkPhysicalDevice physical_device,
- uint32_t instance_extension_count,
- const char* const* instance_extensions) {
+VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) {
VkJsonDevice device;
- PFN_vkGetPhysicalDeviceProperties2KHR vkpGetPhysicalDeviceProperties2KHR =
- nullptr;
- PFN_vkGetPhysicalDeviceFeatures2KHR vkpGetPhysicalDeviceFeatures2KHR =
- nullptr;
- if (instance != VK_NULL_HANDLE &&
- HasExtension("VK_KHR_get_physical_device_properties2",
- instance_extension_count, instance_extensions)) {
- vkpGetPhysicalDeviceProperties2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR"));
- vkpGetPhysicalDeviceFeatures2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR"));
- }
-
uint32_t extension_count = 0;
vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
&extension_count, nullptr);
@@ -103,55 +74,48 @@
device.layers.data());
}
- if (HasExtension("VK_KHR_get_physical_device_properties2",
- instance_extension_count, instance_extensions)) {
- VkPhysicalDeviceProperties2KHR properties = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
- nullptr,
- {} // properties
- };
- if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
- device.ext_driver_properties.reported = true;
- device.ext_driver_properties.driver_properties_khr.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
- device.ext_driver_properties.driver_properties_khr.pNext =
- properties.pNext;
- properties.pNext =
- &device.ext_driver_properties.driver_properties_khr;
- }
- vkpGetPhysicalDeviceProperties2KHR(physical_device, &properties);
- device.properties = properties.properties;
-
- VkPhysicalDeviceFeatures2KHR features = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
- nullptr,
- {} // features
- };
- if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
- device.ext_variable_pointer_features.reported = true;
- device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
- device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
- features.pNext;
- features.pNext =
- &device.ext_variable_pointer_features.variable_pointer_features_khr;
- }
- if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
- device.ext_shader_float16_int8_features.reported = true;
- device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
- .sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
- device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
- .pNext = features.pNext;
- features.pNext = &device.ext_shader_float16_int8_features
- .shader_float16_int8_features_khr;
- }
- vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features);
- device.features = features.features;
- } else {
- vkGetPhysicalDeviceProperties(physical_device, &device.properties);
- vkGetPhysicalDeviceFeatures(physical_device, &device.features);
+ VkPhysicalDeviceProperties2 properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ nullptr,
+ {},
+ };
+ if (HasExtension("VK_KHR_driver_properties", device.extensions)) {
+ device.ext_driver_properties.reported = true;
+ device.ext_driver_properties.driver_properties_khr.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
+ device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext;
+ properties.pNext = &device.ext_driver_properties.driver_properties_khr;
}
+ vkGetPhysicalDeviceProperties2(physical_device, &properties);
+ device.properties = properties.properties;
+
+ VkPhysicalDeviceFeatures2 features = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ nullptr,
+ {},
+ };
+ if (HasExtension("VK_KHR_variable_pointers", device.extensions)) {
+ device.ext_variable_pointer_features.reported = true;
+ device.ext_variable_pointer_features.variable_pointer_features_khr.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
+ device.ext_variable_pointer_features.variable_pointer_features_khr.pNext =
+ features.pNext;
+ features.pNext =
+ &device.ext_variable_pointer_features.variable_pointer_features_khr;
+ }
+ if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
+ device.ext_shader_float16_int8_features.reported = true;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .pNext = features.pNext;
+ features.pNext = &device.ext_shader_float16_int8_features
+ .shader_float16_int8_features_khr;
+ }
+ vkGetPhysicalDeviceFeatures2(physical_device, &features);
+ device.features = features.features;
+
vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory);
uint32_t queue_family_count = 0;
@@ -189,135 +153,117 @@
}
}
- PFN_vkGetPhysicalDeviceProperties2 vkpGetPhysicalDeviceProperties2 =
- reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2"));
- if (vkpGetPhysicalDeviceProperties2) {
- VkPhysicalDeviceProperties2 properties2 = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}};
+ VkPhysicalDeviceProperties2 properties2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ nullptr,
+ {},
+ };
- device.subgroup_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
- device.subgroup_properties.pNext = properties2.pNext;
- properties2.pNext = &device.subgroup_properties;
+ device.subgroup_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
+ device.subgroup_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.subgroup_properties;
- device.point_clipping_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
- device.point_clipping_properties.pNext = properties2.pNext;
- properties2.pNext = &device.point_clipping_properties;
+ device.point_clipping_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
+ device.point_clipping_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.point_clipping_properties;
- device.multiview_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
- device.multiview_properties.pNext = properties2.pNext;
- properties2.pNext = &device.multiview_properties;
+ device.multiview_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
+ device.multiview_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.multiview_properties;
- device.id_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
- device.id_properties.pNext = properties2.pNext;
- properties2.pNext = &device.id_properties;
+ device.id_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
+ device.id_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.id_properties;
- device.maintenance3_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
- device.maintenance3_properties.pNext = properties2.pNext;
- properties2.pNext = &device.maintenance3_properties;
+ device.maintenance3_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
+ device.maintenance3_properties.pNext = properties2.pNext;
+ properties2.pNext = &device.maintenance3_properties;
- (*vkpGetPhysicalDeviceProperties2)(physical_device, &properties2);
- }
+ vkGetPhysicalDeviceProperties2(physical_device, &properties2);
- PFN_vkGetPhysicalDeviceFeatures2 vkpGetPhysicalDeviceFeatures2 =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>(
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2"));
- if (vkpGetPhysicalDeviceFeatures2) {
- VkPhysicalDeviceFeatures2 features2 = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}};
+ VkPhysicalDeviceFeatures2 features2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ nullptr,
+ {},
+ };
- device.bit16_storage_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
- device.bit16_storage_features.pNext = features2.pNext;
- features2.pNext = &device.bit16_storage_features;
+ device.bit16_storage_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+ device.bit16_storage_features.pNext = features2.pNext;
+ features2.pNext = &device.bit16_storage_features;
- device.multiview_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
- device.multiview_features.pNext = features2.pNext;
- features2.pNext = &device.multiview_features;
+ device.multiview_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+ device.multiview_features.pNext = features2.pNext;
+ features2.pNext = &device.multiview_features;
- device.variable_pointer_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
- device.variable_pointer_features.pNext = features2.pNext;
- features2.pNext = &device.variable_pointer_features;
+ device.variable_pointer_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
+ device.variable_pointer_features.pNext = features2.pNext;
+ features2.pNext = &device.variable_pointer_features;
- device.protected_memory_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
- device.protected_memory_features.pNext = features2.pNext;
- features2.pNext = &device.protected_memory_features;
+ device.protected_memory_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+ device.protected_memory_features.pNext = features2.pNext;
+ features2.pNext = &device.protected_memory_features;
- device.sampler_ycbcr_conversion_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
- device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
- features2.pNext = &device.sampler_ycbcr_conversion_features;
+ device.sampler_ycbcr_conversion_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
+ features2.pNext = &device.sampler_ycbcr_conversion_features;
- device.shader_draw_parameter_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
- device.shader_draw_parameter_features.pNext = features2.pNext;
- features2.pNext = &device.shader_draw_parameter_features;
+ device.shader_draw_parameter_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
+ device.shader_draw_parameter_features.pNext = features2.pNext;
+ features2.pNext = &device.shader_draw_parameter_features;
- (*vkpGetPhysicalDeviceFeatures2)(physical_device, &features2);
- }
+ vkGetPhysicalDeviceFeatures2(physical_device, &features2);
- PFN_vkGetPhysicalDeviceExternalFenceProperties
- vkpGetPhysicalDeviceExternalFenceProperties =
- reinterpret_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>(
- vkGetInstanceProcAddr(
- instance, "vkGetPhysicalDeviceExternalFenceProperties"));
- if (vkpGetPhysicalDeviceExternalFenceProperties) {
- VkPhysicalDeviceExternalFenceInfo external_fence_info = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
- VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
- VkExternalFenceProperties external_fence_properties = {};
+ VkPhysicalDeviceExternalFenceInfo external_fence_info = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
+ VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT};
+ VkExternalFenceProperties external_fence_properties = {};
- for (VkExternalFenceHandleTypeFlagBits handle_type =
- VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
- handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
- handle_type = static_cast<VkExternalFenceHandleTypeFlagBits>(
- handle_type << 1)) {
- external_fence_info.handleType = handle_type;
- (*vkpGetPhysicalDeviceExternalFenceProperties)(
- physical_device, &external_fence_info, &external_fence_properties);
- if (external_fence_properties.exportFromImportedHandleTypes ||
- external_fence_properties.compatibleHandleTypes ||
- external_fence_properties.externalFenceFeatures) {
- device.external_fence_properties.insert(
- std::make_pair(handle_type, external_fence_properties));
- }
+ for (VkExternalFenceHandleTypeFlagBits handle_type =
+ VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+ handle_type =
+ static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) {
+ external_fence_info.handleType = handle_type;
+ vkGetPhysicalDeviceExternalFenceProperties(
+ physical_device, &external_fence_info, &external_fence_properties);
+ if (external_fence_properties.exportFromImportedHandleTypes ||
+ external_fence_properties.compatibleHandleTypes ||
+ external_fence_properties.externalFenceFeatures) {
+ device.external_fence_properties.insert(
+ std::make_pair(handle_type, external_fence_properties));
}
}
- PFN_vkGetPhysicalDeviceExternalSemaphoreProperties
- vkpGetPhysicalDeviceExternalSemaphoreProperties = reinterpret_cast<
- PFN_vkGetPhysicalDeviceExternalSemaphoreProperties>(
- vkGetInstanceProcAddr(
- instance, "vkGetPhysicalDeviceExternalSemaphoreProperties"));
- if (vkpGetPhysicalDeviceExternalSemaphoreProperties) {
- VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
- VkExternalSemaphoreProperties external_semaphore_properties = {};
+ VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr,
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
+ VkExternalSemaphoreProperties external_semaphore_properties = {};
- for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
- handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
- handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
- handle_type << 1)) {
- external_semaphore_info.handleType = handle_type;
- (*vkpGetPhysicalDeviceExternalSemaphoreProperties)(
- physical_device, &external_semaphore_info,
- &external_semaphore_properties);
- if (external_semaphore_properties.exportFromImportedHandleTypes ||
- external_semaphore_properties.compatibleHandleTypes ||
- external_semaphore_properties.externalSemaphoreFeatures) {
- device.external_semaphore_properties.insert(
- std::make_pair(handle_type, external_semaphore_properties));
- }
+ for (VkExternalSemaphoreHandleTypeFlagBits handle_type =
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+ handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>(
+ handle_type << 1)) {
+ external_semaphore_info.handleType = handle_type;
+ vkGetPhysicalDeviceExternalSemaphoreProperties(
+ physical_device, &external_semaphore_info,
+ &external_semaphore_properties);
+ if (external_semaphore_properties.exportFromImportedHandleTypes ||
+ external_semaphore_properties.compatibleHandleTypes ||
+ external_semaphore_properties.externalSemaphoreFeatures) {
+ device.external_semaphore_properties.insert(
+ std::make_pair(handle_type, external_semaphore_properties));
}
}
}
@@ -351,19 +297,15 @@
if (!EnumerateExtensions(nullptr, &instance.extensions))
return VkJsonInstance();
- std::vector<const char*> instance_extensions;
- for (const auto extension : kSupportedInstanceExtensions) {
- if (HasExtension(extension, instance.extensions))
- instance_extensions.push_back(extension);
- }
-
- const VkApplicationInfo app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO,
- nullptr,
- "vkjson_info",
- 1,
- "",
- 0,
- VK_API_VERSION_1_1};
+ const VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ nullptr,
+ "vkjson_info",
+ 1,
+ "",
+ 0,
+ VK_API_VERSION_1_1,
+ };
VkInstanceCreateInfo instance_info = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
nullptr,
@@ -371,8 +313,9 @@
&app_info,
0,
nullptr,
- static_cast<uint32_t>(instance_extensions.size()),
- instance_extensions.data()};
+ 0,
+ nullptr,
+ };
VkInstance vkinstance;
result = vkCreateInstance(&instance_info, nullptr, &vkinstance);
if (result != VK_SUCCESS)
@@ -397,52 +340,38 @@
instance.devices.reserve(sz);
for (uint32_t i = 0; i < sz; ++i) {
device_map.insert(std::make_pair(devices[i], i));
- instance.devices.emplace_back(VkJsonGetDevice(vkinstance, devices[i],
- instance_extensions.size(),
- instance_extensions.data()));
+ instance.devices.emplace_back(VkJsonGetDevice(devices[i]));
}
- PFN_vkEnumerateInstanceVersion vkpEnumerateInstanceVersion =
- reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
- vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"));
- if (!vkpEnumerateInstanceVersion) {
- instance.api_version = VK_API_VERSION_1_0;
- } else {
- result = (*vkpEnumerateInstanceVersion)(&instance.api_version);
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
+ result = vkEnumerateInstanceVersion(&instance.api_version);
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
}
- PFN_vkEnumeratePhysicalDeviceGroups vkpEnumeratePhysicalDeviceGroups =
- reinterpret_cast<PFN_vkEnumeratePhysicalDeviceGroups>(
- vkGetInstanceProcAddr(vkinstance, "vkEnumeratePhysicalDeviceGroups"));
- if (vkpEnumeratePhysicalDeviceGroups) {
- count = 0;
- result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count, nullptr);
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
- }
+ count = 0;
+ result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr);
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
+ }
- VkJsonDeviceGroup device_group;
- std::vector<VkPhysicalDeviceGroupProperties> group_properties;
- group_properties.resize(count);
- result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count,
- group_properties.data());
- if (result != VK_SUCCESS) {
- vkDestroyInstance(vkinstance, nullptr);
- return VkJsonInstance();
+ VkJsonDeviceGroup device_group;
+ std::vector<VkPhysicalDeviceGroupProperties> group_properties;
+ group_properties.resize(count);
+ result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
+ group_properties.data());
+ if (result != VK_SUCCESS) {
+ vkDestroyInstance(vkinstance, nullptr);
+ return VkJsonInstance();
+ }
+ for (auto properties : group_properties) {
+ device_group.properties = properties;
+ for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
+ device_group.device_inds.push_back(
+ device_map[properties.physicalDevices[i]]);
}
- for (auto properties : group_properties) {
- device_group.properties = properties;
- for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) {
- device_group.device_inds.push_back(
- device_map[properties.physicalDevices[i]]);
- }
- instance.device_groups.push_back(device_group);
- }
+ instance.device_groups.push_back(device_group);
}
vkDestroyInstance(vkinstance, nullptr);