Merge "Fix installd_dexopt_test." am: c684ad61dc am: cc20457c7b
am: 71dad97f0d
Change-Id: Ibed722c481448f9a9bd83e5d3fe2982eb439ffdb
diff --git a/libs/gui/CleanSpec.mk b/CleanSpec.mk
similarity index 81%
rename from libs/gui/CleanSpec.mk
rename to CleanSpec.mk
index 5a5144c..6a9007c 100644
--- a/libs/gui/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 The Android Open Source Project
+# Copyright (C) 2018 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.
@@ -44,9 +44,11 @@
#$(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/*)
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
+$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libdvr.so" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvr_intermediates)
$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/thermalserviced)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/thermalservice.rc)
+$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "gpuservice*" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/gpuservice_intermediates)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 73360a3..1a932c3 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,8 +7,10 @@
libs/binder/ndk/
libs/graphicsenv/
libs/gui/
+ libs/renderengine/
libs/ui/
libs/vr/
+ services/bufferhub/
services/surfaceflinger/
services/vr/
diff --git a/cmds/atrace/TEST_MAPPING b/cmds/atrace/TEST_MAPPING
new file mode 100644
index 0000000..f43db22
--- /dev/null
+++ b/cmds/atrace/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAtraceHostTestCases"
+ }
+ ]
+}
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 39b3aac..5836f11 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -99,30 +99,31 @@
/* Tracing categories */
static const TracingCategory k_categories[] = {
- { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } },
- { "input", "Input", ATRACE_TAG_INPUT, { } },
- { "view", "View System", ATRACE_TAG_VIEW, { } },
- { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } },
- { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } },
- { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } },
- { "sm", "Sync Manager", ATRACE_TAG_SYNC_MANAGER, { } },
- { "audio", "Audio", ATRACE_TAG_AUDIO, { } },
- { "video", "Video", ATRACE_TAG_VIDEO, { } },
- { "camera", "Camera", ATRACE_TAG_CAMERA, { } },
- { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } },
- { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } },
- { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } },
- { "rs", "RenderScript", ATRACE_TAG_RS, { } },
- { "bionic", "Bionic C Library", ATRACE_TAG_BIONIC, { } },
- { "power", "Power Management", ATRACE_TAG_POWER, { } },
- { "pm", "Package Manager", ATRACE_TAG_PACKAGE_MANAGER, { } },
- { "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } },
- { "database", "Database", ATRACE_TAG_DATABASE, { } },
- { "network", "Network", ATRACE_TAG_NETWORK, { } },
- { "adb", "ADB", ATRACE_TAG_ADB, { } },
- { "vibrator", "Vibrator", ATRACE_TAG_VIBRATOR, { } },
- { "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } },
- { "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } },
+ { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } },
+ { "input", "Input", ATRACE_TAG_INPUT, { } },
+ { "view", "View System", ATRACE_TAG_VIEW, { } },
+ { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } },
+ { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } },
+ { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } },
+ { "sm", "Sync Manager", ATRACE_TAG_SYNC_MANAGER, { } },
+ { "audio", "Audio", ATRACE_TAG_AUDIO, { } },
+ { "video", "Video", ATRACE_TAG_VIDEO, { } },
+ { "camera", "Camera", ATRACE_TAG_CAMERA, { } },
+ { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } },
+ { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } },
+ { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } },
+ { "rs", "RenderScript", ATRACE_TAG_RS, { } },
+ { "bionic", "Bionic C Library", ATRACE_TAG_BIONIC, { } },
+ { "power", "Power Management", ATRACE_TAG_POWER, { } },
+ { "pm", "Package Manager", ATRACE_TAG_PACKAGE_MANAGER, { } },
+ { "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } },
+ { "database", "Database", ATRACE_TAG_DATABASE, { } },
+ { "network", "Network", ATRACE_TAG_NETWORK, { } },
+ { "adb", "ADB", ATRACE_TAG_ADB, { } },
+ { "vibrator", "Vibrator", ATRACE_TAG_VIBRATOR, { } },
+ { "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } },
+ { "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } },
+ { "rro", "Runtime Resource Overlay", ATRACE_TAG_RRO, { } },
{ k_coreServiceCategory, "Core services", 0, { } },
{ k_pdxServiceCategory, "PDX services", 0, { } },
{ "sched", "CPU Scheduling", 0, {
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index acc62c0..6c86c21 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -15,8 +15,8 @@
chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable
chmod 0666 /sys/kernel/tracing/events/regulator/enable
chmod 0666 /sys/kernel/debug/tracing/events/regulator/enable
- chmod 0666 /sys/kernel/tracing/events/pagecache/enable
- chmod 0666 /sys/kernel/debug/tracing/events/pagecache/enable
+ chmod 0666 /sys/kernel/tracing/events/filemap/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable
# irq
chmod 0666 /sys/kernel/tracing/events/irq/enable
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index 75855cf..ded0ed3 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -55,7 +55,7 @@
errno = ETIMEDOUT;
}
printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
- break;
+ return EXIT_FAILURE;
}
// Writes line by line.
@@ -71,8 +71,5 @@
// Process final line, in case it didn't finish with newline
write_line(line, show_progress);
- if (close(s) == -1) {
- fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
- }
return EXIT_SUCCESS;
}
diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h
index 304e4b3..7af289b 100644
--- a/cmds/bugreportz/bugreportz.h
+++ b/cmds/bugreportz/bugreportz.h
@@ -16,6 +16,7 @@
#define BUGREPORTZ_H
// Calls dumpstate using the given socket and output its result to stdout.
+// Ownership of the socket is not transferred.
int bugreportz(int s, bool show_progress);
#endif // BUGREPORTZ_H
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index a3ae1ff..74a95b0 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -82,7 +82,7 @@
if (s == -1) {
printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
- return EXIT_SUCCESS;
+ return EXIT_FAILURE;
}
// Set a timeout so that if nothing is read in 10 minutes, we'll stop
@@ -92,8 +92,16 @@
tv.tv_sec = 10 * 60;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
- fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+ fprintf(stderr,
+ "WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n",
+ strerror(errno));
}
- bugreportz(s, show_progress);
+ int ret = bugreportz(s, show_progress);
+
+ if (close(s) == -1) {
+ fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
+ ret = EXIT_FAILURE;
+ }
+ return ret;
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index daee7e5..738c20f 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1480,6 +1480,12 @@
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
+
+ printf("========================================================\n");
+ printf("== Obtaining statsd metadata\n");
+ printf("========================================================\n");
+ // This differs from the usual dumpsys stats, which is the stats report data.
+ RunDumpsys("STATSDSTATS", {"stats", "--metadata"});
return Dumpstate::RunStatus::OK;
}
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 8fbea8a..4811927 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -65,7 +65,7 @@
" -l: only list services, do not dump them\n"
" -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
" -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
- " --proto: filter services that support dumping data in proto format. Dumps"
+ " --proto: filter services that support dumping data in proto format. Dumps\n"
" will be in proto format.\n"
" --priority LEVEL: filter services based on specified priority\n"
" LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 8f60881..3ada153 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -371,8 +371,8 @@
IServiceManager::DUMP_FLAG_PRIORITY_NORMAL);
ExpectCheckService("Locksmith");
ExpectCheckService("Valet");
- ExpectDumpWithArgs("Locksmith", {"-a", "--dump-priority", "NORMAL"}, "dump1");
- ExpectDumpWithArgs("Valet", {"-a", "--dump-priority", "NORMAL"}, "dump2");
+ ExpectDumpWithArgs("Locksmith", {"--dump-priority", "NORMAL", "-a"}, "dump1");
+ ExpectDumpWithArgs("Valet", {"--dump-priority", "NORMAL", "-a"}, "dump2");
CallMain({"--priority", "NORMAL"});
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 62d2fa1..d398559 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -222,9 +222,9 @@
}
bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
- sp<IBinder> dpy = mSurfaceComposerClient->getBuiltInDisplay(0);
+ const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken();
if (dpy == nullptr) {
- fprintf(stderr, "SurfaceComposer::getBuiltInDisplay failed.\n");
+ fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n");
return false;
}
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index c5a8f49..b60bbc0 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,7 @@
"dexopt.cpp",
"globals.cpp",
"utils.cpp",
+ "utils_default.cpp",
"view_compiler.cpp",
":installd_aidl",
],
@@ -206,6 +207,7 @@
"otapreopt.cpp",
"otapreopt_utils.cpp",
"utils.cpp",
+ "utils_default.cpp",
"view_compiler.cpp",
],
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index a51946d..2efcf11 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -102,15 +102,6 @@
static constexpr size_t kSha256Size = 32;
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
-// NOTE: keep in sync with Installer
-static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
-static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-static constexpr int FLAG_USE_QUOTA = 1 << 12;
-static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
-static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
-static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15;
-static constexpr int FLAG_FORCE = 1 << 16;
-
namespace {
constexpr const char* kDump = "android.permission.DUMP";
@@ -578,6 +569,35 @@
remove_path_xattr(path, kXattrInodeCodeCache);
}
}
+
+ auto extPath = findDataMediaPath(uuid, userId);
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ // Clear only cached data from shared storage
+ path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+ // No code cache on shared storage
+ } else {
+ // Clear everything on shared storage
+ path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ }
}
if (flags & FLAG_STORAGE_DE) {
std::string suffix = "";
@@ -646,6 +666,24 @@
if (delete_dir_contents_and_dir(path) != 0) {
res = error("Failed to delete " + path);
}
+
+ auto extPath = findDataMediaPath(uuid, userId);
+ path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
+ path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents_and_dir(path, true) != 0) {
+ res = error("Failed to delete " + path);
+ }
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
@@ -1655,6 +1693,8 @@
}
ATRACE_BEGIN("external");
+ auto sandboxPath = create_data_media_package_path(uuid_, userId, "sandbox", pkgname);
+ calculate_tree_size(sandboxPath, &extStats.dataSize);
auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname);
collectManualStats(extPath, &extStats);
auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname);
@@ -2462,7 +2502,7 @@
if (validate_apk_path(packageDir.c_str())) {
return error("Invalid path " + packageDir);
}
- if (delete_dir_contents_and_dir(packageDir) != 0) {
+ if (rm_package_dir(packageDir) != 0) {
return error("Failed to delete " + packageDir);
}
return ok();
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 51fe66b..63c9765 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -114,4 +114,14 @@
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
+
+ const int FLAG_CLEAR_CACHE_ONLY = 0x10;
+ const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20;
+
+ const int FLAG_FREE_CACHE_V2 = 0x100;
+ const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200;
+ const int FLAG_FREE_CACHE_NOOP = 0x400;
+
+ const int FLAG_USE_QUOTA = 0x1000;
+ const int FLAG_FORCE = 0x2000;
}
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 2d58515..db09070 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -40,8 +40,8 @@
constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
-static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
-static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+#define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2
+#define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA
int get_property(const char *key, char *value, const char *default_value) {
return property_get(key, value, default_value);
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index cd63931..a31d510 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -45,7 +45,7 @@
constexpr const char* kTestUuid = "TEST";
-static constexpr int FLAG_FORCE = 1 << 16;
+#define FLAG_FORCE InstalldNativeService::FLAG_FORCE
int get_property(const char *key, char *value, const char *default_value) {
return property_get(key, value, default_value);
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 91d9e13..955d524 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -125,6 +125,8 @@
int delete_dir_contents_fd(int dfd, const char *name);
+int rm_package_dir(const std::string& package_dir);
+
int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group);
int64_t data_disk_free(const std::string& data_path);
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp
new file mode 100644
index 0000000..a6025e6
--- /dev/null
+++ b/cmds/installd/utils_default.cpp
@@ -0,0 +1,30 @@
+/*
+** Copyright 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 "utils.h"
+
+namespace android {
+namespace installd {
+
+// In this file are default definitions of the functions that may contain
+// platform dependent logic.
+
+int rm_package_dir(const std::string& package_dir) {
+ return delete_dir_contents_and_dir(package_dir);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 34a3fdc..d5dc6b7 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -232,7 +232,6 @@
else if (strcmp(key, "categories") == 0)
{
char* context2 = nullptr;
- int categoryCount = 0;
categories[categoryCount] = strtok_r(value, ",", &context2);
while (categories[categoryCount] != nullptr)
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index fade8cf..cf3b172 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -146,7 +146,19 @@
int binder_become_context_manager(struct binder_state *bs)
{
- return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
+ struct flat_binder_object obj;
+ memset(&obj, 0, sizeof(obj));
+ obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
+
+ int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+
+ // fallback to original method
+ if (result != 0) {
+ android_errorWriteLog(0x534e4554, "121035042");
+
+ result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
+ }
+ return result;
}
int binder_write(struct binder_state *bs, void *data, size_t len)
@@ -240,13 +252,28 @@
#endif
ptr += sizeof(struct binder_ptr_cookie);
break;
+ case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION: {
- struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
- if ((end - ptr) < sizeof(*txn)) {
- ALOGE("parse: txn too small!\n");
- return -1;
+ struct binder_transaction_data_secctx txn;
+ if (cmd == BR_TRANSACTION_SEC_CTX) {
+ if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) {
+ ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n");
+ return -1;
+ }
+ memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx));
+ ptr += sizeof(struct binder_transaction_data_secctx);
+ } else /* BR_TRANSACTION */ {
+ if ((end - ptr) < sizeof(struct binder_transaction_data)) {
+ ALOGE("parse: txn too small (binder_transaction_data)!\n");
+ return -1;
+ }
+ memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data));
+ ptr += sizeof(struct binder_transaction_data);
+
+ txn.secctx = 0;
}
- binder_dump_txn(txn);
+
+ binder_dump_txn(&txn.transaction_data);
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
@@ -254,15 +281,14 @@
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
- bio_init_from_txn(&msg, txn);
- res = func(bs, txn, &msg, &reply);
- if (txn->flags & TF_ONE_WAY) {
- binder_free_buffer(bs, txn->data.ptr.buffer);
+ bio_init_from_txn(&msg, &txn.transaction_data);
+ res = func(bs, &txn, &msg, &reply);
+ if (txn.transaction_data.flags & TF_ONE_WAY) {
+ binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer);
} else {
- binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
+ binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res);
}
}
- ptr += sizeof(*txn);
break;
}
case BR_REPLY: {
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
index c95b33f..a9ccc74 100644
--- a/cmds/servicemanager/binder.h
+++ b/cmds/servicemanager/binder.h
@@ -4,8 +4,8 @@
#ifndef _BINDER_H_
#define _BINDER_H_
-#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#include <sys/ioctl.h>
struct binder_state;
@@ -42,7 +42,7 @@
};
typedef int (*binder_handler)(struct binder_state *bs,
- struct binder_transaction_data *txn,
+ struct binder_transaction_data_secctx *txn,
struct binder_io *msg,
struct binder_io *reply);
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index d776682..ec3fac5 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -61,14 +61,14 @@
static char *service_manager_context;
static struct selabel_handle* sehandle;
-static bool check_mac_perms(pid_t spid, uid_t uid, const char *tctx, const char *perm, const char *name)
+static bool check_mac_perms(pid_t spid, const char* sid, uid_t uid, const char *tctx, const char *perm, const char *name)
{
- char *sctx = NULL;
+ char *lookup_sid = NULL;
const char *class = "service_manager";
bool allowed;
struct audit_data ad;
- if (getpidcon(spid, &sctx) < 0) {
+ if (sid == NULL && getpidcon(spid, &lookup_sid) < 0) {
ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid);
return false;
}
@@ -77,19 +77,23 @@
ad.uid = uid;
ad.name = name;
- int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad);
+ if (sid == NULL) {
+ android_errorWriteLog(0x534e4554, "121035042");
+ }
+
+ int result = selinux_check_access(sid ? sid : lookup_sid, tctx, class, perm, (void *) &ad);
allowed = (result == 0);
- freecon(sctx);
+ freecon(lookup_sid);
return allowed;
}
-static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
+static bool check_mac_perms_from_getcon(pid_t spid, const char* sid, uid_t uid, const char *perm)
{
- return check_mac_perms(spid, uid, service_manager_context, perm, NULL);
+ return check_mac_perms(spid, sid, uid, service_manager_context, perm, NULL);
}
-static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name)
+static bool check_mac_perms_from_lookup(pid_t spid, const char* sid, uid_t uid, const char *perm, const char *name)
{
bool allowed;
char *tctx = NULL;
@@ -104,12 +108,12 @@
return false;
}
- allowed = check_mac_perms(spid, uid, tctx, perm, name);
+ allowed = check_mac_perms(spid, sid, uid, tctx, perm, name);
freecon(tctx);
return allowed;
}
-static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
+static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid)
{
const char *perm = "add";
@@ -117,19 +121,19 @@
return 0; /* Don't allow apps to register services */
}
- return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
+ return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0;
}
-static int svc_can_list(pid_t spid, uid_t uid)
+static int svc_can_list(pid_t spid, const char* sid, uid_t uid)
{
const char *perm = "list";
- return check_mac_perms_from_getcon(spid, uid, perm) ? 1 : 0;
+ return check_mac_perms_from_getcon(spid, sid, uid, perm) ? 1 : 0;
}
-static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
+static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid)
{
const char *perm = "find";
- return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
+ return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0;
}
struct svcinfo
@@ -175,7 +179,7 @@
};
-uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
+uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid)
{
struct svcinfo *si = find_svc(s, len);
@@ -192,7 +196,7 @@
}
}
- if (!svc_can_find(s, len, spid, uid)) {
+ if (!svc_can_find(s, len, spid, sid, uid)) {
return 0;
}
@@ -200,7 +204,7 @@
}
int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
- uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
+ uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) {
struct svcinfo *si;
//ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
@@ -209,7 +213,7 @@
if (!handle || (len == 0) || (len > 127))
return -1;
- if (!svc_can_register(s, len, spid, uid)) {
+ if (!svc_can_register(s, len, spid, sid, uid)) {
ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
str8(s, len), handle, uid);
return -1;
@@ -248,7 +252,7 @@
}
int svcmgr_handler(struct binder_state *bs,
- struct binder_transaction_data *txn,
+ struct binder_transaction_data_secctx *txn_secctx,
struct binder_io *msg,
struct binder_io *reply)
{
@@ -260,6 +264,8 @@
int allow_isolated;
uint32_t dumpsys_priority;
+ struct binder_transaction_data *txn = &txn_secctx->transaction_data;
+
//ALOGI("target=%p code=%d pid=%d uid=%d\n",
// (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);
@@ -274,6 +280,7 @@
// Note that we ignore the strict_policy and don't propagate it
// further (since we do no outbound RPCs anyway).
strict_policy = bio_get_uint32(msg);
+ bio_get_uint32(msg); // Ignore worksource header.
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
@@ -304,7 +311,8 @@
if (s == NULL) {
return -1;
}
- handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
+ handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid,
+ (const char*) txn_secctx->secctx);
if (!handle)
break;
bio_put_ref(reply, handle);
@@ -319,7 +327,7 @@
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
dumpsys_priority = bio_get_uint32(msg);
if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
- txn->sender_pid))
+ txn->sender_pid, (const char*) txn_secctx->secctx))
return -1;
break;
@@ -327,7 +335,7 @@
uint32_t n = bio_get_uint32(msg);
uint32_t req_dumpsys_priority = bio_get_uint32(msg);
- if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
+ if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) {
ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
txn->sender_euid);
return -1;
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 4d93cb4..152ac28 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -13,5 +13,6 @@
onrestart restart cameraserver
onrestart restart keystore
onrestart restart gatekeeperd
+ onrestart restart thermalservice
writepid /dev/cpuset/system-background/tasks
shutdown critical
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 0bc08a9..c70bc3e 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -30,14 +30,13 @@
message SurfaceChange {
required int32 id = 1;
-
+ reserved 7;
oneof SurfaceChange {
PositionChange position = 2;
SizeChange size = 3;
AlphaChange alpha = 4;
LayerChange layer = 5;
CropChange crop = 6;
- FinalCropChange final_crop = 7;
MatrixChange matrix = 8;
OverrideScalingModeChange override_scaling_mode = 9;
TransparentRegionHintChange transparent_region_hint = 10;
@@ -46,6 +45,7 @@
OpaqueFlagChange opaque_flag = 13;
SecureFlagChange secure_flag = 14;
DeferredTransactionChange deferred_transaction = 15;
+ CornerRadiusChange corner_radius = 16;
}
}
@@ -63,6 +63,10 @@
required float alpha = 1;
}
+message CornerRadiusChange {
+ required float corner_radius = 1;
+}
+
message LayerChange {
required uint32 layer = 1;
}
@@ -71,10 +75,6 @@
required Rectangle rectangle = 1;
}
-message FinalCropChange {
- required Rectangle rectangle = 1;
-}
-
message MatrixChange {
required float dsdx = 1;
required float dtdx = 2;
@@ -165,7 +165,7 @@
message DisplayCreation {
required int32 id = 1;
required string name = 2;
- required int32 type = 3;
+ optional uint64 display_id = 3;
required bool is_secure = 4;
}
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 4140f40..34886a9 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -286,10 +286,6 @@
std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
.detach();
} break;
- case increment.kSurfaceDeletion: {
- std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
- .detach();
- } break;
case increment.kBufferUpdate: {
std::lock_guard<std::mutex> lock1(mLayerLock);
std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
@@ -385,12 +381,12 @@
case SurfaceChange::SurfaceChangeCase::kCrop:
setCrop(transaction, change.id(), change.crop());
break;
+ case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+ setCornerRadius(transaction, change.id(), change.corner_radius());
+ break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
setMatrix(transaction, change.id(), change.matrix());
break;
- case SurfaceChange::SurfaceChangeCase::kFinalCrop:
- setFinalCrop(transaction, change.id(), change.final_crop());
- break;
case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
setOverrideScalingMode(transaction, change.id(),
change.override_scaling_mode());
@@ -489,17 +485,14 @@
Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
cc.rectangle().bottom());
- t.setCrop(mLayers[id], r);
+ t.setCrop_legacy(mLayers[id], r);
}
-void Replayer::setFinalCrop(SurfaceComposerClient::Transaction& t,
- layer_id id, const FinalCropChange& fcc) {
- ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
- fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
- fcc.rectangle().bottom());
- Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
- fcc.rectangle().bottom());
- t.setFinalCrop(mLayers[id], r);
+void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
+ layer_id id, const CornerRadiusChange& cc) {
+ ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius());
+
+ t.setCornerRadius(mLayers[id], cc.corner_radius());
}
void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
@@ -570,7 +563,7 @@
auto handle = mLayers[dtc.layer_id()]->getHandle();
- t.deferTransactionUntil(mLayers[id], handle, dtc.frame_number());
+ t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number());
}
void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
@@ -631,47 +624,10 @@
return NO_ERROR;
}
-status_t Replayer::deleteSurfaceControl(
- const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
- ALOGV("Deleting %d Surface Control", delete_.id());
- event->readyToExecute();
-
- std::lock_guard<std::mutex> lock1(mPendingLayersLock);
-
- mLayersPendingRemoval.push_back(delete_.id());
-
- const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
- if (iterator != mBufferQueueSchedulers.end()) {
- (*iterator).second->stopScheduling();
- }
-
- std::lock_guard<std::mutex> lock2(mLayerLock);
- if (mLayers[delete_.id()] != nullptr) {
- mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
- }
-
- return NO_ERROR;
-}
-
-void Replayer::doDeleteSurfaceControls() {
- std::lock_guard<std::mutex> lock1(mPendingLayersLock);
- std::lock_guard<std::mutex> lock2(mLayerLock);
- if (!mLayersPendingRemoval.empty()) {
- for (int id : mLayersPendingRemoval) {
- mLayers.erase(id);
- mColors.erase(id);
- mBufferQueueSchedulers.erase(id);
- }
- mLayersPendingRemoval.clear();
- }
-}
-
status_t Replayer::injectVSyncEvent(
const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
ALOGV("Injecting VSync Event");
- doDeleteSurfaceControls();
-
event->readyToExecute();
SurfaceComposerClient::injectVSync(vSyncEvent.when());
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 295403e..ad807ee 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -70,8 +70,6 @@
status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
status_t createSurfaceControl(const SurfaceCreation& create,
const std::shared_ptr<Event>& event);
- status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
- const std::shared_ptr<Event>& event);
status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
@@ -92,8 +90,8 @@
layer_id id, const LayerChange& lc);
void setCrop(SurfaceComposerClient::Transaction& t,
layer_id id, const CropChange& cc);
- void setFinalCrop(SurfaceComposerClient::Transaction& t,
- layer_id id, const FinalCropChange& fcc);
+ void setCornerRadius(SurfaceComposerClient::Transaction& t,
+ layer_id id, const CornerRadiusChange& cc);
void setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc);
void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
@@ -120,7 +118,6 @@
void setDisplayProjection(SurfaceComposerClient::Transaction& t,
display_id id, const ProjectionChange& pc);
- void doDeleteSurfaceControls();
void waitUntilTimestamp(int64_t timestamp);
void waitUntilDeferredTransactionLayerExists(
const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
index a892e46..d63d97f 100644
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -172,7 +172,7 @@
def display_create(increment):
increment.display_creation.id = int(input("Enter id: "))
increment.display_creation.name = str(raw_input("Enter name: "))
- increment.display_creation.type = int(input("Enter type: "))
+ increment.display_creation.display_id = int(input("Enter display ID: "))
increment.display_creation.is_secure = bool(input("Enter if secure: "))
def display_delete(increment):
diff --git a/data/etc/android.hardware.biometrics.face.xml b/data/etc/android.hardware.biometrics.face.xml
new file mode 100644
index 0000000..7fa0bf9
--- /dev/null
+++ b/data/etc/android.hardware.biometrics.face.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 set of features for a biometric face authentication sensor. -->
+<permissions>
+ <feature name="android.hardware.biometrics.face" />
+</permissions>
diff --git a/data/etc/android.software.ipsec_tunnels.xml b/data/etc/android.software.ipsec_tunnels.xml
new file mode 100644
index 0000000..f7ffc02
--- /dev/null
+++ b/data/etc/android.software.ipsec_tunnels.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 feature indicating that the device has support for multinetworking-capable IPsec
+ tunnels
+-->
+
+<permissions>
+ <feature name="android.software.ipsec_tunnels" />
+</permissions>
diff --git a/data/etc/android.software.secure_lock_screen.xml b/data/etc/android.software.secure_lock_screen.xml
new file mode 100644
index 0000000..3464487
--- /dev/null
+++ b/data/etc/android.software.secure_lock_screen.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+ <feature name="android.software.secure_lock_screen" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 561f5ba..5c1cab6 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -36,12 +36,15 @@
<feature name="android.hardware.type.automotive" />
<!-- basic system services -->
- <feature name="android.software.app_widgets" />
<feature name="android.software.connectionservice" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.print" />
+ <feature name="android.software.companion_device_setup" />
+ <feature name="android.software.autofill" />
+ <feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index 8b5a461..915e579 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -43,6 +43,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 060a334..619d017 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -51,6 +51,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 6db2627..52524ca 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -51,6 +51,7 @@
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
+ <feature name="android.software.secure_lock_screen" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index e2ab71a..0f364c1 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -35,6 +35,7 @@
<!-- basic system services -->
<feature name="android.software.home_screen" />
+ <feature name="android.software.secure_lock_screen" />
<!-- input management and third-party input method editors -->
<feature name="android.software.input_methods" />
diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h
index 4de314d..c87ee56 100644
--- a/headers/media_plugin/media/cas/CasAPI.h
+++ b/headers/media_plugin/media/cas/CasAPI.h
@@ -48,6 +48,14 @@
uint8_t *data,
size_t size);
+typedef void (*CasPluginCallbackExt)(
+ void *appData,
+ int32_t event,
+ int32_t arg,
+ uint8_t *data,
+ size_t size,
+ const CasSessionId *sessionId);
+
struct CasFactory {
CasFactory() {}
virtual ~CasFactory() {}
@@ -67,6 +75,13 @@
CasPluginCallback callback,
CasPlugin **plugin) = 0;
+ // Construct a new extend instance of a CasPlugin given a CA_system_id
+ virtual status_t createPlugin(
+ int32_t CA_system_id,
+ void *appData,
+ CasPluginCallbackExt callback,
+ CasPlugin **plugin) = 0;
+
private:
CasFactory(const CasFactory &);
CasFactory &operator=(const CasFactory &); /* NOLINT */
@@ -110,7 +125,15 @@
int32_t arg,
const CasData &eventData) = 0;
- // Native implementation of the MediaCas Java API provision method.
+ // Deliver an session event to the CasPlugin. The format of the event is
+ // specific to the CA scheme and is opaque to the framework.
+ virtual status_t sendSessionEvent(
+ const CasSessionId &sessionId,
+ int32_t event,
+ int32_t arg,
+ const CasData &eventData) = 0;
+
+ // Native implementation of the MediaCas Java API provision method.
virtual status_t provision(
const String8 &provisionString) = 0;
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index c44a1f6..59bd292 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -84,6 +84,7 @@
kDrmPluginEventSessionReclaimed,
kDrmPluginEventExpirationUpdate,
kDrmPluginEventKeysChange,
+ kDrmPluginEventSessionLostState,
};
// Drm keys can be for offline content or for online streaming.
@@ -139,6 +140,8 @@
kHdcpV2_1,
// HDCP version 2.2 Type 1.
kHdcpV2_2,
+ // HDCP version 2.3 Type 1.
+ kHdcpV2_3,
// No digital output, implicitly secure
kHdcpNoOutput = 0x7fff
};
@@ -167,6 +170,25 @@
kSecurityLevelHwSecureAll
};
+ // An offline license may be usable or inactive. The keys in a
+ // usable offline license are available for decryption. When
+ // the offline license state is inactive, the keys have been
+ // marked for release using getKeyRequest with
+ // kKeyType_Release but the key response has not been
+ // received. The keys in an inactive offline license are not
+ // usable for decryption.
+
+ enum OfflineLicenseState {
+ // The offline license state is unknown due to an error
+ kOfflineLicenseStateUnknown,
+ // Offline license state is usable, the keys may be used for decryption.
+ kOfflineLicenseStateUsable,
+ // Offline license state is released, the keys have been marked for
+ // release using getKeyRequest() with kKeyType_Release but the
+ // key response has not been received.
+ kOfflineLicenseStateReleased
+ };
+
DrmPlugin() {}
virtual ~DrmPlugin() {}
diff --git a/headers/media_plugin/media/hardware/HardwareAPI.h b/headers/media_plugin/media/hardware/HardwareAPI.h
index 6c1ba3d..ae0220a 100644
--- a/headers/media_plugin/media/hardware/HardwareAPI.h
+++ b/headers/media_plugin/media/hardware/HardwareAPI.h
@@ -425,7 +425,7 @@
// HDR color description parameters.
// This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the
-// 'OMX.google.android.index.describeHDRColorInfo' extension is given and an HDR stream
+// 'OMX.google.android.index.describeHDRStaticInfo' extension is given and an HDR stream
// is detected. Component SHALL behave as described below if it supports this extension.
//
// Currently, only Static Metadata Descriptor Type 1 support is required.
@@ -496,6 +496,64 @@
HDRStaticInfo sInfo; // IN/OUT
};
+// HDR10+ metadata configuration.
+//
+// nParamSize: size of the storage starting at nValue (must be at least 1 and at most
+// MAX_HDR10PLUSINFO_SIZE). This field must not be modified by the component.
+// nParamSizeUsed: size of the actual HDR10+ metadata starting at nValue. For OMX_SetConfig,
+// it must not be modified by the component. For OMX_GetConfig, the component
+// should put the actual size of the retrieved config in this field (and in
+// case where nParamSize is smaller than nParamSizeUsed, the component should
+// still update nParamSizeUsed without actually copying the metadata to nValue).
+// nValue: storage of the HDR10+ metadata conforming to the user_data_registered_itu_t_t35()
+// syntax of SEI message for ST 2094-40.
+//
+// This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the
+// 'OMX.google.android.index.describeHDR10PlusInfo' extension is given. In general, this config
+// is associated with a particular frame. A typical sequence of usage is as follows:
+//
+// a) OMX_SetConfig associates the config with the next input buffer sent in OMX_EmptyThisBuffer
+// (input A);
+// b) The component sends OMX_EventConfigUpdate to notify the client that there is a config
+// update on the output port that is associated with the next output buffer that's about to
+// be sent via FillBufferDone callback (output A);
+// c) The client, upon receiving the OMX_EventConfigUpdate, calls OMX_GetConfig to retrieve
+// the config and associates it with output A.
+//
+// All config updates will be retrieved in the order reported, and the client is required to
+// call OMX_GetConfig for each OMX_EventConfigUpdate for this config. Note that the order of
+// OMX_EventConfigUpdate relative to FillBufferDone callback determines which output frame
+// the config should be associated with, the actual OMX_GetConfig for the config could happen
+// before or after the component calls the FillBufferDone callback.
+//
+// Depending on the video codec type (in particular, whether the codec uses in-band or out-of-
+// band HDR10+ metadata), the component shall behave as detailed below:
+//
+// VIDEO DECODERS:
+// 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence
+// a) ~ c) outlined above;
+// 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config should be
+// ignored (as the metadata is embedded in the input buffer), while the notification and
+// retrieval of the config on the output as outlined in b) & c) must be supported.
+//
+// VIDEO ENCODERS:
+// 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence
+// a) ~ c) outlined above;
+// 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config outlined in
+// a) must be supported. The notification as outlined in b) must not be sent, and the
+// retrieval of the config via OMX_GetConfig should be ignored (as the metadata is embedded
+// in the output buffer).
+
+#define MAX_HDR10PLUSINFO_SIZE 1024
+struct DescribeHDR10PlusInfoParams {
+ OMX_U32 nSize; // IN
+ OMX_VERSIONTYPE nVersion; // IN
+ OMX_U32 nPortIndex; // IN
+ OMX_U32 nParamSize; // IN
+ OMX_U32 nParamSizeUsed; // IN/OUT
+ OMX_U8 nValue[1]; // IN/OUT
+};
+
} // namespace android
extern android::OMXPluginBase *createOMXPlugin();
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index dc25ded..ce30b41 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -188,7 +188,9 @@
inline static const char *asString(OMX_AUDIO_CODINGEXTTYPE i, const char *def = "??") {
switch (i) {
case OMX_AUDIO_CodingAndroidAC3: return "AndroidAC3";
+ case OMX_AUDIO_CodingAndroidEAC3: return "AndroidEAC3";
case OMX_AUDIO_CodingAndroidOPUS: return "AndroidOPUS";
+ case OMX_AUDIO_CodingAndroidAC4: return "AndroidAC4";
default: return asString((OMX_AUDIO_CODINGTYPE)i, def);
}
}
@@ -533,9 +535,11 @@
// case OMX_IndexConfigCommit: return "ConfigCommit";
case OMX_IndexConfigAndroidVendorExtension: return "ConfigAndroidVendorExtension";
case OMX_IndexParamAudioAndroidAc3: return "ParamAudioAndroidAc3";
+ case OMX_IndexConfigAudioPresentation: return "ConfigAudioPresentation";
case OMX_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus";
case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation";
case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3";
+ case OMX_IndexParamAudioAndroidAc4: return "ParamAudioAndroidAc4";
case OMX_IndexParamAudioProfileQuerySupported: return "ParamAudioProfileQuerySupported";
// case OMX_IndexParamNalStreamFormatSupported: return "ParamNalStreamFormatSupported";
// case OMX_IndexParamNalStreamFormat: return "ParamNalStreamFormat";
@@ -907,6 +911,9 @@
case OMX_VIDEO_AVCLevel5: return "Level5";
case OMX_VIDEO_AVCLevel51: return "Level51";
case OMX_VIDEO_AVCLevel52: return "Level52";
+ case OMX_VIDEO_AVCLevel6: return "Level6";
+ case OMX_VIDEO_AVCLevel61: return "Level61";
+ case OMX_VIDEO_AVCLevel62: return "Level62";
default: return def;
}
}
diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h
index 8409553..477faed 100644
--- a/headers/media_plugin/media/openmax/OMX_AudioExt.h
+++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h
@@ -48,6 +48,7 @@
OMX_AUDIO_CodingAndroidAC3, /**< AC3 encoded data */
OMX_AUDIO_CodingAndroidOPUS, /**< OPUS encoded data */
OMX_AUDIO_CodingAndroidEAC3, /**< EAC3 encoded data */
+ OMX_AUDIO_CodingAndroidAC4, /**< AC4 encoded data */
} OMX_AUDIO_CODINGEXTTYPE;
typedef struct OMX_AUDIO_PARAM_ANDROID_AC3TYPE {
@@ -68,6 +69,15 @@
variable or unknown sampling rate. */
} OMX_AUDIO_PARAM_ANDROID_EAC3TYPE;
+typedef struct OMX_AUDIO_PARAM_ANDROID_AC4TYPE {
+ OMX_U32 nSize; /**< size of the structure in bytes */
+ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
+ OMX_U32 nPortIndex; /**< port that this structure applies to */
+ OMX_U32 nChannels; /**< Number of channels */
+ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for
+ variable or unknown sampling rate. */
+} OMX_AUDIO_PARAM_ANDROID_AC4TYPE;
+
typedef struct OMX_AUDIO_PARAM_ANDROID_OPUSTYPE {
OMX_U32 nSize; /**< size of the structure in bytes */
OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
@@ -117,6 +127,13 @@
OMX_U32 nProfileIndex; /**< Used to query for individual profile support information */
} OMX_AUDIO_PARAM_ANDROID_PROFILETYPE;
+typedef struct OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION {
+ OMX_U32 nSize; /**< size of the structure in bytes */
+ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
+ OMX_S32 nPresentationId; /**< presentation id */
+ OMX_S32 nProgramId; /**< program id */
+} OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION;
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/headers/media_plugin/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h
index bb974b3..9ff934e 100644
--- a/headers/media_plugin/media/openmax/OMX_Core.h
+++ b/headers/media_plugin/media/openmax/OMX_Core.h
@@ -542,6 +542,20 @@
* fool-proof way to do that for video encoders.
*/
OMX_EventDataSpaceChanged,
+
+ /**
+ * Event when a component has an updated configuration on output for the client to retrieve.
+ * |arg1| contains the port index (currently only output port is valid). |arg2| contains the
+ * index of the updated config.
+ *
+ * For config updates that's associated with one frame, the update should be applied to the
+ * next output frame that comes in EmptyBufferDone callback.
+ *
+ * Upon receiving this event, the client must call the corresponding OMX_GetConfig to retrieve
+ * the config update.
+ */
+ OMX_EventConfigUpdate,
+
OMX_EventMax = 0x7FFFFFFF
} OMX_EVENTTYPE;
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 716d959..479e9b8 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -64,6 +64,8 @@
OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
OMX_IndexParamAudioAndroidAacDrcPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */
+ OMX_IndexParamAudioAndroidAc4, /**< reference: OMX_AUDIO_PARAM_ANDROID_AC4TYPE */
+ OMX_IndexConfigAudioPresentation, /**< reference: OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION */
OMX_IndexExtAudioEndUnused,
/* Image parameters and configurations */
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h
index 9fd2fd2..b6edaa9 100644
--- a/headers/media_plugin/media/openmax/OMX_Video.h
+++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -832,6 +832,9 @@
OMX_VIDEO_AVCLevel5 = 0x4000, /**< Level 5 */
OMX_VIDEO_AVCLevel51 = 0x8000, /**< Level 5.1 */
OMX_VIDEO_AVCLevel52 = 0x10000, /**< Level 5.2 */
+ OMX_VIDEO_AVCLevel6 = 0x20000, /**< Level 6 */
+ OMX_VIDEO_AVCLevel61 = 0x40000, /**< Level 6.1 */
+ OMX_VIDEO_AVCLevel62 = 0x80000, /**< Level 6.2 */
OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index bbf157b..435fcc8 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -164,6 +164,8 @@
// HDR profiles also support passing HDR metadata
OMX_VIDEO_VP9Profile2HDR = 0x1000,
OMX_VIDEO_VP9Profile3HDR = 0x2000,
+ OMX_VIDEO_VP9Profile2HDR10Plus = 0x4000,
+ OMX_VIDEO_VP9Profile3HDR10Plus = 0x8000,
OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
} OMX_VIDEO_VP9PROFILETYPE;
@@ -216,6 +218,7 @@
OMX_VIDEO_HEVCProfileMainStill = 0x4,
// Main10 profile with HDR SEI support.
OMX_VIDEO_HEVCProfileMain10HDR10 = 0x1000,
+ OMX_VIDEO_HEVCProfileMain10HDR10Plus = 0x2000,
OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF
} OMX_VIDEO_HEVCPROFILETYPE;
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index d75de1e..44883cc 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -26,6 +26,7 @@
#ifndef ANDROID_CHOREOGRAPHER_H
#define ANDROID_CHOREOGRAPHER_H
+#include <stdint.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -43,6 +44,16 @@
*/
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
+/**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the time that the frame is being rendered as nanoseconds in the
+ * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
+ * application that registered a callback. All callbacks that run as part of
+ * rendering a frame will observe the same frame time, so it should be used
+ * whenever events need to be synchronized (e.g. animations).
+ */
+typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
+
#if __ANDROID_API__ >= 24
/**
@@ -52,23 +63,39 @@
AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
/**
- * Post a callback to be run on the next frame. The data pointer provided will
- * be passed to the callback function when it's called.
+ * Deprecated: Use AChoreographer_postFrameCallback64 instead.
*/
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24);
+ AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
/**
- * Post a callback to be run on the frame following the specified delay. The
- * data pointer provided will be passed to the callback function when it's
- * called.
+ * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
*/
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
- long delayMillis) __INTRODUCED_IN(24);
+ long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
#endif /* __ANDROID_API__ >= 24 */
+#if __ANDROID_API__ >= 29
+
+/**
+ * Power a callback to be run on the next frame. The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postFrameCallback64(AChoreographer* chroreographer,
+ AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
+
+/**
+ * Post a callback to be run on the frame following the specified delay. The
+ * data pointer provided will be passed to the callback function when it's
+ * called.
+ */
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/input.h b/include/android/input.h
index 6810901..cfade6c 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -83,7 +83,7 @@
};
/**
- * Meta key / modifer state.
+ * Meta key / modifier state.
*/
enum {
/** No meta keys are pressed. */
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 59d67f3..214559d 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -769,7 +769,14 @@
/** all apps */
AKEYCODE_ALL_APPS = 284,
/** refresh key */
- AKEYCODE_REFRESH = 285
+ AKEYCODE_REFRESH = 285,
+ /** Thumbs up key. Apps can use this to let user upvote content. */
+ AKEYCODE_THUMBS_UP = 286,
+ /** Thumbs down key. Apps can use this to let user downvote content. */
+ AKEYCODE_THUMBS_DOWN = 287,
+ /** Used to switch current account that is consuming content.
+ * May be consumed by system to switch current viewer profile. */
+ AKEYCODE_PROFILE_SWITCH = 288
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 005564d..e9d5c16 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -64,6 +64,7 @@
#define ASENSOR_RESOLUTION_INVALID (nanf(""))
#define ASENSOR_FIFO_COUNT_INVALID (-1)
#define ASENSOR_DELAY_INVALID INT32_MIN
+#define ASENSOR_INVALID (-1)
/* (Keep in sync with hardware/sensors-base.h and Sensor.java.) */
@@ -208,6 +209,35 @@
*/
ASENSOR_TYPE_HEART_BEAT = 31,
/**
+ * This sensor type is for delivering additional sensor information aside
+ * from sensor event data.
+ *
+ * Additional information may include:
+ * - {@link ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE}
+ * - {@link ASENSOR_ADDITIONAL_INFO_SAMPLING}
+ * - {@link ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT}
+ * - {@link ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY}
+ * - {@link ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION}
+ *
+ * This type will never bind to a sensor. In other words, no sensor in the
+ * sensor list can have the type {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
+ *
+ * If a device supports the sensor additional information feature, it will
+ * report additional information events via {@link ASensorEvent} and will
+ * have {@link ASensorEvent#type} set to
+ * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set
+ * to the handle of the reporting sensor.
+ *
+ * Additional information reports consist of multiple frames ordered by
+ * {@link ASensorEvent#timestamp}. The first frame in the report will have
+ * a {@link AAdditionalInfoEvent#type} of
+ * {@link ASENSOR_ADDITIONAL_INFO_BEGIN}, and the last frame in the report
+ * will have a {@link AAdditionalInfoEvent#type} of
+ * {@link ASENSOR_ADDITIONAL_INFO_END}.
+ *
+ */
+ ASENSOR_TYPE_ADDITIONAL_INFO = 33,
+ /**
* {@link ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT}
*/
ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT = 34,
@@ -273,6 +303,51 @@
ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2
};
+/**
+ * Sensor Additional Info Types.
+ *
+ * Used to populate {@link AAdditionalInfoEvent#type}.
+ */
+enum {
+ /** Marks the beginning of additional information frames */
+ ASENSOR_ADDITIONAL_INFO_BEGIN = 0,
+
+ /** Marks the end of additional information frames */
+ ASENSOR_ADDITIONAL_INFO_END = 1,
+
+ /**
+ * Estimation of the delay that is not tracked by sensor timestamps. This
+ * includes delay introduced by sensor front-end filtering, data transport,
+ * etc.
+ * float[2]: delay in seconds, standard deviation of estimated value
+ */
+ ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY = 0x10000,
+
+ /** float: Celsius temperature */
+ ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE,
+
+ /**
+ * First three rows of a homogeneous matrix, which represents calibration to
+ * a three-element vector raw sensor reading.
+ * float[12]: 3x4 matrix in row major order
+ */
+ ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION,
+
+ /**
+ * Location and orientation of sensor element in the device frame: origin is
+ * the geometric center of the mobile device screen surface; the axis
+ * definition corresponds to Android sensor definitions.
+ * float[12]: 3x4 matrix in row major order
+ */
+ ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT,
+
+ /**
+ * float[2]: raw sample period in seconds,
+ * standard deviation of sampling period
+ */
+ ASENSOR_ADDITIONAL_INFO_SAMPLING,
+};
+
/*
* A few useful constants
*/
@@ -341,7 +416,7 @@
int32_t handle;
} ADynamicSensorEvent;
-typedef struct {
+typedef struct AAdditionalInfoEvent {
int32_t type;
int32_t serial;
union {
@@ -420,6 +495,7 @@
* - ASensorEventQueue_hasEvents()
* - ASensorEventQueue_getEvents()
* - ASensorEventQueue_setEventRate()
+ * - ASensorEventQueue_requestAdditionalInfoEvents()
*/
typedef struct ASensorEventQueue ASensorEventQueue;
@@ -444,6 +520,7 @@
* - ASensor_getStringType()
* - ASensor_getReportingMode()
* - ASensor_isWakeUpSensor()
+ * - ASensor_getHandle()
*/
typedef struct ASensor ASensor;
/**
@@ -703,6 +780,29 @@
*/
ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
+ * the given {@link ASensorEventQueue}.
+ *
+ * Sensor data events are always delivered to the {@ASensorEventQueue}.
+ *
+ * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through
+ * {@link ASensorEventQueue_getEvents}. The client is responsible for checking
+ * {@link ASensorEvent#type} to determine the event type prior to handling of
+ * the event.
+ *
+ * The client must be tolerant of any value for
+ * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future
+ * and may delivered to the client.
+ *
+ * \param queue {@link ASensorEventQueue} to configure
+ * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events,
+ * false to stop receiving events
+ * \return 0 on success or a negative error code on failure
+ */
+int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable);
+#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */
/*****************************************************************************/
@@ -785,6 +885,24 @@
int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26);
#endif /* __ANDROID_API__ >= 26 */
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Returns the sensor's handle.
+ *
+ * The handle identifies the sensor within the system and is included in the
+ * {@link ASensorEvent#sensor} field of sensor events, including those sent with type
+ * {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
+ *
+ * A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the
+ * sensor that generated the event.
+ *
+ * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as
+ * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists
+ * between the values.
+ */
+int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__);
+#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */
+
#ifdef __cplusplus
};
#endif
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
new file mode 100644
index 0000000..ef2ad99
--- /dev/null
+++ b/include/android/surface_control.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2018 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.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file surface_control.h
+ */
+
+#ifndef ANDROID_SURFACE_CONTROL_H
+#define ANDROID_SURFACE_CONTROL_H
+
+#include <sys/cdefs.h>
+
+#include <android/data_space.h>
+#include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+struct ASurfaceControl;
+
+/**
+ * The SurfaceControl API can be used to provide a hierarchy of surfaces for
+ * composition to the system compositor. ASurfaceControl represents a content node in
+ * this hierarchy.
+ */
+typedef struct ASurfaceControl ASurfaceControl;
+
+/*
+ * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
+ * |debug_name| is a debug name associated with this surface. It can be used to
+ * identify this surface in the SurfaceFlinger's layer tree. It must not be
+ * null.
+ *
+ * The caller takes ownership of the ASurfaceControl returned and must release it
+ * using ASurfaceControl_release below.
+ */
+ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
+ __INTRODUCED_IN(29);
+
+ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name)
+ __INTRODUCED_IN(29);
+
+/**
+ * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
+ * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
+ * as their parent remains on display.
+ */
+void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
+
+struct ASurfaceTransaction;
+
+/**
+ * ASurfaceTransaction is a collection of updates to the surface tree that must
+ * be applied atomically.
+ */
+typedef struct ASurfaceTransaction ASurfaceTransaction;
+
+/**
+ * The caller takes ownership of the transaction and must release it using
+ * ASurfaceControl_delete below.
+ */
+ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
+
+/**
+ * Destroys the |transaction| object.
+ */
+void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+
+/**
+ * Applies the updates accumulated in |transaction|.
+ *
+ * Note that the transaction is guaranteed to be applied atomically. The
+ * transactions which are applied on the same thread are also guaranteed to be
+ * applied in order.
+ */
+void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+
+/**
+ * An opaque handle returned during a callback that can be used to query general stats and stats for
+ * surfaces which were either removed or for which buffers were updated after this transaction was
+ * applied.
+ */
+typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
+
+/**
+ * Since the transactions are applied asynchronously, the
+ * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
+ * including the updates in a transaction was presented.
+ *
+ * |context| is the optional context provided by the client that is passed into
+ * the callback.
+ *
+ * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the callback.
+ *
+ * THREADING
+ * The transaction completed callback can be invoked on any thread.
+ */
+typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the frame was latched by the framework. Once a frame is
+ * latched by the framework, it is presented at the following hardware vsync.
+ */
+int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns a sync fence that signals when the transaction has been presented.
+ * The recipient of the callback takes ownership of the fence and is responsible for closing
+ * it.
+ */
+int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
+ __INTRODUCED_IN(29);
+
+/**
+ * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
+ * When the client is done using the array, it must release it by calling
+ * ASurfaceTransactionStats_releaseASurfaceControls.
+ *
+ * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ */
+void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
+ ASurfaceControl*** outASurfaceControls,
+ size_t* outASurfaceControlsSize)
+ __INTRODUCED_IN(29);
+/**
+ * Releases the array of ASurfaceControls that were returned by
+ * ASurfaceTransactionStats_getASurfaceControls.
+ */
+void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered
+ * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until
+ * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
+ */
+int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats,
+ ASurfaceControl* surface_control)
+ __INTRODUCED_IN(29);
+
+/**
+ * The returns the fence used to signal the release of the PREVIOUS buffer set on
+ * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the
+ * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS
+ * buffer is already released. The recipient of the callback takes ownership of the
+ * previousReleaseFenceFd and is responsible for closing it.
+ *
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
+ * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * addition of a buffer to a particular surface as a unique ref. When a transaction updates or
+ * removes a buffer from a surface, or removes the surface itself from the tree, this ref is
+ * guaranteed to be released in the OnComplete callback for this transaction. The
+ * ASurfaceControlStats provided in the callback for this surface may contain an optional fence
+ * which must be signaled before the ref is assumed to be released.
+ *
+ * The client must ensure that all pending refs on a buffer are released before attempting to reuse
+ * this buffer, otherwise synchronization errors may occur.
+ */
+int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
+ ASurfaceTransactionStats* surface_transaction_stats,
+ ASurfaceControl* surface_control)
+ __INTRODUCED_IN(29);
+
+/**
+ * Sets the callback that will be invoked when the updates from this transaction
+ * are presented. For details on the callback semantics and data, see the
+ * comments on the ASurfaceTransaction_OnComplete declaration above.
+ */
+void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context,
+ ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
+
+/**
+ * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
+ * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ *
+ * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ */
+void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, ASurfaceControl* new_parent)
+ __INTRODUCED_IN(29);
+
+/* Parameter for ASurfaceTransaction_setVisibility */
+enum {
+ ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
+ ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
+};
+/**
+ * Updates the visibility of |surface_control|. If show is set to
+ * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
+ * be hidden.
+ */
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, int8_t visibility)
+ __INTRODUCED_IN(29);
+
+/**
+ * Updates the z order index for |surface_control|. Note that the z order for a surface
+ * is relative to other surfaces which are siblings of this surface. The behavior of sibilings with
+ * the same z order is undefined.
+ *
+ * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0.
+ */
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, int32_t z_order)
+ __INTRODUCED_IN(29);
+
+/**
+ * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
+ * acquire_fence_fd should be a file descriptor that is signaled when all pending work
+ * for the buffer is complete and the buffer can be safely read.
+ *
+ * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
+ * for closing it.
+ */
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, AHardwareBuffer* buffer,
+ int acquire_fence_fd = -1) __INTRODUCED_IN(29);
+
+/**
+ * Updates the color for |surface_control|. This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|,
+ * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha|
+ * will be the dataspace and alpha set for the background color layer.
+ */
+void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float r, float g, float b,
+ float alpha, ADataSpace dataspace)
+ __INTRODUCED_IN(29);
+
+/**
+ * |source| the sub-rect within the buffer's content to be rendered inside the surface's area
+ * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
+ * and height must be > 0.
+ *
+ * |destination| specifies the rect in the parent's space where this surface will be drawn. The post
+ * source rect bounds are scaled to fit the destination rect. The surface's destination rect is
+ * clipped by the bounds of its parent. The destination rect's width and height must be > 0.
+ *
+ * |transform| the transform applied after the source rect is applied to the buffer. This parameter
+ * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
+ * enum.
+ */
+void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, const ARect& source,
+ const ARect& destination, int32_t transform)
+ __INTRODUCED_IN(29);
+
+
+/* Parameter for ASurfaceTransaction_setBufferTransparency */
+enum {
+ ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
+ ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
+ ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
+};
+/**
+ * Updates whether the content for the buffer associated with this surface is
+ * completely opaque. If true, every pixel of content inside the buffer must be
+ * opaque or visual errors can occur.
+ */
+void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ int8_t transparency)
+ __INTRODUCED_IN(29);
+
+/**
+ * Updates the region for the content on this surface updated in this
+ * transaction. If unspecified, the complete surface is assumed to be damaged.
+ */
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, const ARect rects[],
+ uint32_t count) __INTRODUCED_IN(29);
+
+/**
+ * Specifies a desiredPresentTime for the transaction. The framework will try to present
+ * the transaction at or after the time specified.
+ *
+ * Transactions will not be presented until all of their acquire fences have signaled even if the
+ * app requests an earlier present time.
+ *
+ * If an earlier transaction has a desired present time of x, and a later transaction has a desired
+ * present time that is before x, the later transaction will not preempt the earlier transaction.
+ */
+void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction,
+ int64_t desiredPresentTime) __INTRODUCED_IN(29);
+
+/**
+ * Sets the alpha for the buffer. It uses a premultiplied blending.
+ *
+ * The |alpha| must be between 0.0 and 1.0.
+ */
+void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float alpha)
+ __INTRODUCED_IN(29);
+
+/**
+ * Sets the data space of the surface_control's buffers.
+ *
+ * If no data space is set, the surface control defaults to ADATASPACE_SRGB.
+ */
+void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, ADataSpace data_space)
+ __INTRODUCED_IN(29);
+
+/*
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ *
+ * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ struct AHdrMetadata_smpte2086* metadata)
+ __INTRODUCED_IN(29);
+
+/*
+ * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
+ *
+ * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ struct AHdrMetadata_cta861_3* metadata)
+ __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
new file mode 100644
index 0000000..38f036e
--- /dev/null
+++ b/include/android/system_fonts.h
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/**
+ * @file system_fonts.h
+ * @brief Provides the system font configurations.
+ *
+ * These APIs provides the list of system installed font files with additional metadata about the
+ * font.
+ *
+ * The ASystemFontIterator_open method will give you an iterator which can iterate all system
+ * installed font files as shown in the following example.
+ *
+ * \code{.cpp}
+ * ASystemFontIterator* iterator = ASystemFontIterator_open();
+ * ASystemFont* font = NULL;
+ *
+ * while ((font = ASystemFontIterator_next(iterator)) != nullptr) {
+ * // Look if the font is your desired one.
+ * if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font)
+ * && ASystemFont_getLocale(font) == NULL) {
+ * break;
+ * }
+ * ASystemFont_close(font);
+ * }
+ * ASystemFontIterator_close(iterator);
+ *
+ * int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY);
+ * int collectionIndex = ASystemFont_getCollectionINdex(font);
+ * std::vector<std::pair<uint32_t, float>> variationSettings;
+ * for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) {
+ * variationSettings.push_back(std::make_pair(
+ * ASystemFont_getAxisTag(font, i),
+ * ASystemFont_getAxisValue(font, i)));
+ * }
+ * ASystemFont_close(font);
+ *
+ * // Use this font for your text rendering engine.
+ *
+ * \endcode
+ *
+ * Available since API level 29.
+ */
+
+#ifndef ANDROID_SYSTEM_FONTS_H
+#define ANDROID_SYSTEM_FONTS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit).
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+enum {
+ /** The minimum value fot the font weight value. */
+ ASYSTEM_FONT_WEIGHT_MIN = 0,
+
+ /** A font weight value for the thin weight. */
+ ASYSTEM_FONT_WEIGHT_THIN = 100,
+
+ /** A font weight value for the extra-light weight. */
+ ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT = 200,
+
+ /** A font weight value for the light weight. */
+ ASYSTEM_FONT_WEIGHT_LIGHT = 300,
+
+ /** A font weight value for the normal weight. */
+ ASYSTEM_FONT_WEIGHT_NORMAL = 400,
+
+ /** A font weight value for the medium weight. */
+ ASYSTEM_FONT_WEIGHT_MEDIUM = 500,
+
+ /** A font weight value for the semi-bold weight. */
+ ASYSTEM_FONT_WEIGHT_SEMI_BOLD = 600,
+
+ /** A font weight value for the bold weight. */
+ ASYSTEM_FONT_WEIGHT_BOLD = 700,
+
+ /** A font weight value for the extra-bold weight. */
+ ASYSTEM_FONT_WEIGHT_EXTRA_BOLD = 800,
+
+ /** A font weight value for the black weight. */
+ ASYSTEM_FONT_WEIGHT_BLACK = 900,
+
+ /** The maximum value for the font weight value. */
+ ASYSTEM_FONT_WEIGHT_MAX = 1000
+};
+
+/**
+ * ASystemFontIterator provides access to the system font configuration.
+ *
+ * ASystemFontIterator is an iterator for all available system font settings.
+ * This iterator is not a thread-safe object. Do not pass this iterator to other threads.
+ */
+struct ASystemFontIterator;
+
+/**
+ * ASystemFont provides information of the single system font configuration.
+ */
+struct ASystemFont;
+
+/**
+ * Create a system font iterator.
+ *
+ * Use ASystemFont_close() to close the iterator.
+ *
+ * \return a pointer for a newly allocated iterator, nullptr on failure.
+ */
+ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29);
+
+/**
+ * Close an opened system font iterator, freeing any related resources.
+ *
+ * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
+ */
+void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29);
+
+/**
+ * Move to the next system font.
+ *
+ * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \return a font. If no more font is available, returns nullptr. You need to release the returned
+ * font by ASystemFont_close when it is no longer needed.
+ */
+ASystemFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29);
+
+/**
+ * Close an ASystemFont returned by ASystemFontIterator_next.
+ *
+ * \param font a font returned by ASystemFontIterator_next or ASystemFont_matchFamilyStyleCharacter.
+ * Do nothing if NULL is passed.
+ */
+void ASystemFont_close(ASystemFont* _Nullable font) __INTRODUCED_IN(29);
+
+
+/**
+ * Select the best font from given parameters.
+ *
+ * Only generic font families are supported.
+ * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families)
+ *
+ * Even if no font can render the given text, this function will return a non-null result for
+ * drawing Tofu character.
+ *
+ * Examples:
+ * \code{.cpp}
+ * // Simple font query for the ASCII character.
+ * std::vector<uint16_t> text = { 'A' };
+ * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ * "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a valid font file.
+ *
+ * // Querying font for CJK characters
+ * std::vector<uint16_t> text = { 0x9AA8 };
+ * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ * "sans", 400, false, "zh-CN,ja-JP", text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Simplified Chinese font.
+ * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ * "sans", 400, false, "ja-JP,zh-CN", text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Japanese font.
+ *
+ * // Querying font for text/color emoji
+ * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
+ * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ * "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
+ * // runLength will be 8 and the font will points a color emoji font.
+ *
+ * // Mixture of multiple script of characters.
+ * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
+ * std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
+ * ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
+ * "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Hebrew font.
+ * \endcode
+ *
+ * \param familyName a null character terminated font family name
+ * \param weight a font weight value. Only from 0 to 1000 value is valid
+ * \param italic true if italic, otherwise false.
+ * \param languageTags a null character terminated comma separated IETF BCP47 compliant language
+ * tags.
+ * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string.
+ * \param textLength a length of the given text buffer. This must not be zero.
+ * \param runLengthOut if not null, the font run length will be filled.
+ * \return a font to be used for given text and params. You need to release the returned font by
+ * ASystemFont_close when it is no longer needed.
+ */
+ASystemFont* _Nonnull ASystemFont_matchFamilyStyleCharacter(
+ const char* _Nonnull familyName,
+ uint16_t weight,
+ bool italic,
+ const char* _Nonnull languageTags,
+ const uint16_t* _Nonnull text,
+ uint32_t textLength,
+ uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29);
+
+/**
+ * Return an absolute path to the current font file.
+ *
+ * Here is a list of font formats returned by this method:
+ * <ul>
+ * <li>OpenType</li>
+ * <li>OpenType Font Collection</li>
+ * <li>TrueType</li>
+ * <li>TrueType Collection</li>
+ * </ul>
+ * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc.
+ *
+ * The font file returned is guaranteed to be opend with O_RDONLY.
+ * Note that the returned pointer is valid until ASystemFont_close() is called for the given font.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a string of the font file path.
+ */
+const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a weight value associated with the current font.
+ *
+ * The weight values are positive and less than or equal to 1000.
+ * Here are pairs of the common names and their values.
+ * <p>
+ * <table>
+ * <tr>
+ * <th align="center">Value</th>
+ * <th align="center">Name</th>
+ * <th align="center">NDK Definition</th>
+ * </tr>
+ * <tr>
+ * <td align="center">100</td>
+ * <td align="center">Thin</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_THIN}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">200</td>
+ * <td align="center">Extra Light (Ultra Light)</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">300</td>
+ * <td align="center">Light</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">400</td>
+ * <td align="center">Normal (Regular)</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_NORMAL}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">500</td>
+ * <td align="center">Medium</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_MEDIUM}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">600</td>
+ * <td align="center">Semi Bold (Demi Bold)</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_SEMI_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">700</td>
+ * <td align="center">Bold</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">800</td>
+ * <td align="center">Extra Bold (Ultra Bold)</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">900</td>
+ * <td align="center">Black (Heavy)</td>
+ * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td>
+ * </tr>
+ * </table>
+ * </p>
+ * Note that the weight value may fall in between above values, e.g. 250 weight.
+ *
+ * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
+ */
+uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return true if the current font is italic, otherwise returns false.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return true if italic, otherwise false.
+ */
+bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a IETF BCP47 compliant language tag associated with the current font.
+ *
+ * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)")
+ *
+ * Note that the returned pointer is valid until ASystemFont_close() is called.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a IETF BCP47 compliant langauge tag or nullptr if not available.
+ */
+const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a font collection index value associated with the current font.
+ *
+ * In case the target font file is a font collection (e.g. .ttc or .otc), this
+ * returns a non-negative value as an font offset in the collection. This
+ * always returns 0 if the target font file is a regular font.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a font collection index.
+ */
+size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+/**
+ * Return a count of font variation settings associated with the current font
+ *
+ * The font variation settings are provided as multiple tag-values pairs.
+ *
+ * For example, bold italic font may have following font variation settings:
+ * 'wght' 700, 'slnt' -12
+ * In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag
+ * and ASystemFont_getAxisValue will return following values.
+ * \code{.cpp}
+ * ASystemFont* font = ASystemFontIterator_next(ite);
+ *
+ * // Returns the number of axes
+ * ASystemFont_getAxisCount(font); // Returns 2
+ *
+ * // Returns the tag-value pair for the first axis.
+ * ASystemFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874)
+ * ASystemFont_getAxisValue(font, 0); // Returns 700.0
+ *
+ * // Returns the tag-value pair for the second axis.
+ * ASystemFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74)
+ * ASystemFont_getAxisValue(font, 1); // Returns -12.0
+ * \endcode
+ *
+ * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a number of font variation settings.
+ */
+size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
+
+
+/**
+ * Return an OpenType axis tag associated with the current font.
+ *
+ * See ASystemFont_getAxisCount for more details.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
+ * equal to {@link ASystemFont_getAxisCount} is not allowed.
+ * \return an OpenType axis tag value for the given font variation setting.
+ */
+uint32_t ASystemFont_getAxisTag(const ASystemFont* _Nonnull font, uint32_t axisIndex)
+ __INTRODUCED_IN(29);
+
+/**
+ * Return an OpenType axis value associated with the current font.
+ *
+ * See ASystemFont_getAxisCount for more details.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
+ * equal to {@link ASYstemFont_getAxisCount} is not allwed.
+ * \return a float value for the given font variation setting.
+ */
+float ASystemFont_getAxisValue(const ASystemFont* _Nonnull font, uint32_t axisIndex)
+ __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_SYSTEM_FONTS_H
diff --git a/include/android/trace.h b/include/android/trace.h
index aa24995..bb7ff28 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -33,6 +33,7 @@
#define ANDROID_NATIVE_TRACE_H
#include <stdbool.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#ifdef __cplusplus
@@ -73,6 +74,40 @@
#endif /* __ANDROID_API__ >= 23 */
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+/**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link ATrace_endAsyncSection} with the same
+ * methodName and cookie. Unlike {@link ATrace_beginSection} and {@link ATrace_endSection},
+ * asynchronous events do not need to be nested. The name and cookie used to
+ * begin an event must be used to end it.
+ *
+ * \param sectionName The method name to appear in the trace.
+ * \param cookie Unique identifier for distinguishing simultaneous events
+ */
+void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29);
+
+/**
+ * Writes a trace message to indicate that the current method has ended.
+ * Must be called exactly once for each call to {@link ATrace_beginAsyncSection}
+ * using the same name and cookie.
+ *
+ * \param methodName The method name to appear in the trace.
+ * \param cookie Unique identifier for distinguishing simultaneous events
+ */
+void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29);
+
+/**
+ * Writes trace message to indicate the value of a given counter.
+ *
+ * \param counterName The counter name to appear in the trace.
+ * \param counterValue The counter value.
+ */
+void ATrace_setCounter(const char* counterName, int64_t counterValue) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
#ifdef __cplusplus
};
#endif
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 86da4d3..fa456bb 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,11 +17,39 @@
#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
#define _LIBINPUT_DISPLAY_VIEWPORT_H
+#include <android-base/stringprintf.h>
#include <ui/DisplayInfo.h>
#include <input/Input.h>
+#include <inttypes.h>
+#include <optional>
+
+using android::base::StringPrintf;
namespace android {
+/**
+ * Describes the different type of viewports supported by input flinger.
+ * Keep in sync with values in InputManagerService.java.
+ */
+enum class ViewportType : int32_t {
+ VIEWPORT_INTERNAL = 1,
+ VIEWPORT_EXTERNAL = 2,
+ VIEWPORT_VIRTUAL = 3,
+};
+
+static const char* viewportTypeToString(ViewportType type) {
+ switch(type) {
+ case ViewportType::VIEWPORT_INTERNAL:
+ return "INTERNAL";
+ case ViewportType::VIEWPORT_EXTERNAL:
+ return "EXTERNAL";
+ case ViewportType::VIEWPORT_VIRTUAL:
+ return "VIRTUAL";
+ default:
+ return "UNKNOWN";
+ }
+}
+
/*
* Describes how coordinates are mapped on a physical display.
* See com.android.server.display.DisplayViewport.
@@ -39,13 +67,18 @@
int32_t physicalBottom;
int32_t deviceWidth;
int32_t deviceHeight;
- String8 uniqueId;
+ 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) {
+ deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
+ type(ViewportType::VIEWPORT_INTERNAL) {
}
bool operator==(const DisplayViewport& other) const {
@@ -61,7 +94,9 @@
&& physicalBottom == other.physicalBottom
&& deviceWidth == other.deviceWidth
&& deviceHeight == other.deviceHeight
- && uniqueId == other.uniqueId;
+ && uniqueId == other.uniqueId
+ && physicalPort == other.physicalPort
+ && type == other.type;
}
bool operator!=(const DisplayViewport& other) const {
@@ -86,17 +121,25 @@
deviceWidth = width;
deviceHeight = height;
uniqueId.clear();
+ physicalPort = std::nullopt;
+ type = ViewportType::VIEWPORT_INTERNAL;
}
-};
-/**
- * Describes the different type of viewports supported by input flinger.
- * Keep in sync with values in InputManagerService.java.
- */
-enum class ViewportType : int32_t {
- VIEWPORT_INTERNAL = 1,
- VIEWPORT_EXTERNAL = 2,
- VIEWPORT_VIRTUAL = 3,
+ 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);
+ }
};
} // namespace android
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 11bb721..4365a3c 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -22,6 +22,9 @@
#include <binder/IInterface.h>
+#include <input/InputWindow.h>
+#include <input/ISetInputWindowsListener.h>
+
namespace android {
/*
@@ -31,6 +34,12 @@
class IInputFlinger : public IInterface {
public:
DECLARE_META_INTERFACE(InputFlinger)
+
+ virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0;
+ virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
+ virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
+ virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
};
@@ -40,7 +49,10 @@
class BnInputFlinger : public BnInterface<IInputFlinger> {
public:
enum {
- DO_SOMETHING_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ REGISTER_INPUT_CHANNEL_TRANSACTION,
+ UNREGISTER_INPUT_CHANNEL_TRANSACTION,
+ TRANSFER_TOUCH_FOCUS
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h
new file mode 100644
index 0000000..15d31b2
--- /dev/null
+++ b/include/input/ISetInputWindowsListener.h
@@ -0,0 +1,40 @@
+/*
+ * 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 756b337..805957a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -17,6 +17,8 @@
#ifndef _LIBINPUT_INPUT_H
#define _LIBINPUT_INPUT_H
+#pragma GCC system_header
+
/**
* Native input event structures.
*/
@@ -25,7 +27,6 @@
#include <utils/BitSet.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
-#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
#include <stdint.h>
@@ -58,6 +59,12 @@
*/
AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
+ /**
+ * This flag indicates that the event has been generated by a gesture generator. It
+ * provides a hint to the GestureDetector to not apply any touch slop.
+ */
+ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+
/* Motion event is inconsistent with previously sent motion events. */
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
};
@@ -213,6 +220,32 @@
POLICY_FLAG_PASS_TO_USER = 0x40000000,
};
+/**
+ * Classifications of the current gesture, if available.
+ *
+ * The following values must be kept in sync with MotionEvent.java
+ */
+enum class MotionClassification : uint8_t {
+ /**
+ * No classification is available.
+ */
+ NONE = 0,
+ /**
+ * Too early to classify the current gesture. Need more events. Look for changes in the
+ * upcoming motion events.
+ */
+ AMBIGUOUS_GESTURE = 1,
+ /**
+ * The current gesture likely represents a user intentionally exerting force on the touchscreen.
+ */
+ DEEP_PRESS = 2,
+};
+
+/**
+ * String representation of MotionClassification
+ */
+const char* motionClassificationToString(MotionClassification classification);
+
/*
* Pointer coordinate data.
*/
@@ -237,7 +270,12 @@
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
- void scale(float scale);
+ void scale(float globalScale);
+
+ // Scale the pointer coordinates according to a global scale and a
+ // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
+ // axes, however the window scaling will not.
+ void scale(float globalScale, float windowXScale, float windowYScale);
void applyOffset(float xOffset, float yOffset);
inline float getX() const {
@@ -302,12 +340,18 @@
inline void setSource(int32_t source) { mSource = source; }
+ inline int32_t getDisplayId() const { return mDisplayId; }
+
+ inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
+
+
protected:
- void initialize(int32_t deviceId, int32_t source);
+ void initialize(int32_t deviceId, int32_t source, int32_t displayId);
void initialize(const InputEvent& from);
int32_t mDeviceId;
int32_t mSource;
+ int32_t mDisplayId;
};
/*
@@ -339,10 +383,11 @@
static const char* getLabel(int32_t keyCode);
static int32_t getKeyCodeFromLabel(const char* label);
-
+
void initialize(
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t flags,
int32_t keyCode,
@@ -400,6 +445,8 @@
inline void setButtonState(int32_t buttonState) { mButtonState = buttonState; }
+ inline MotionClassification getClassification() const { return mClassification; }
+
inline int32_t getActionButton() const { return mActionButton; }
inline void setActionButton(int32_t button) { mActionButton = button; }
@@ -556,12 +603,14 @@
void initialize(
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t actionButton,
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
+ MotionClassification classification,
float xOffset,
float yOffset,
float xPrecision,
@@ -580,7 +629,7 @@
void offsetLocation(float xOffset, float yOffset);
- void scale(float scaleFactor);
+ void scale(float globalScaleFactor);
// Apply 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
@@ -615,6 +664,7 @@
int32_t mEdgeFlags;
int32_t mMetaState;
int32_t mButtonState;
+ MotionClassification mClassification;
float mXOffset;
float mYOffset;
float mXPrecision;
diff --git a/services/inputflinger/InputApplication.h b/include/input/InputApplication.h
similarity index 87%
rename from services/inputflinger/InputApplication.h
rename to include/input/InputApplication.h
index 724fc2c..71a8f20 100644
--- a/services/inputflinger/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -17,6 +17,11 @@
#ifndef _UI_INPUT_APPLICATION_H
#define _UI_INPUT_APPLICATION_H
+#include <string>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+
#include <input/Input.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -27,8 +32,12 @@
* Describes the properties of an application that can receive input.
*/
struct InputApplicationInfo {
+ sp<IBinder> token;
std::string name;
nsecs_t dispatchingTimeout;
+
+ status_t write(Parcel& output) const;
+ static InputApplicationInfo read(const Parcel& from);
};
@@ -52,6 +61,10 @@
return mInfo ? mInfo->dispatchingTimeout : defaultValue;
}
+ inline sp<IBinder> getApplicationToken() const {
+ return mInfo ? mInfo->token : nullptr;
+ }
+
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 1ea69d3..b6efc82 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -19,6 +19,7 @@
#include <input/Input.h>
#include <input/KeyCharacterMap.h>
+#include <vector>
namespace android {
@@ -31,9 +32,9 @@
}
// Information provided by the kernel.
- String8 name;
- String8 location;
- String8 uniqueId;
+ std::string name;
+ std::string location;
+ std::string uniqueId;
uint16_t bus;
uint16_t vendor;
uint16_t product;
@@ -45,12 +46,21 @@
// It is hashed from whatever kernel provided information is available.
// Ideally, the way this value is computed should not change between Android releases
// because that would invalidate persistent settings that rely on it.
- String8 descriptor;
+ std::string descriptor;
// A value added to uniquely identify a device in the absence of a unique id. This
// is intended to be a minimum way to distinguish from other active devices and may
// reuse values that are not associated with an input anymore.
uint16_t nonce;
+
+ /**
+ * Return InputDeviceIdentifier.name that has been adjusted as follows:
+ * - all characters besides alphanumerics, dash,
+ * and underscore have been replaced with underscores.
+ * This helps in situations where a file that matches the device name is needed,
+ * while conforming to the filename limitations.
+ */
+ std::string getCanonicalName() const;
};
/*
@@ -73,16 +83,16 @@
};
void initialize(int32_t id, int32_t generation, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal,
+ const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
bool hasMic);
inline int32_t getId() const { return mId; }
inline int32_t getControllerNumber() const { return mControllerNumber; }
inline int32_t getGeneration() const { return mGeneration; }
inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; }
- inline const String8& getAlias() const { return mAlias; }
- inline const String8& getDisplayName() const {
- return mAlias.isEmpty() ? mIdentifier.name : mAlias;
+ inline const std::string& getAlias() const { return mAlias; }
+ inline const std::string& getDisplayName() const {
+ return mAlias.empty() ? mIdentifier.name : mAlias;
}
inline bool isExternal() const { return mIsExternal; }
inline bool hasMic() const { return mHasMic; }
@@ -112,7 +122,7 @@
inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; }
inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; }
- inline const Vector<MotionRange>& getMotionRanges() const {
+ inline const std::vector<MotionRange>& getMotionRanges() const {
return mMotionRanges;
}
@@ -121,7 +131,7 @@
int32_t mGeneration;
int32_t mControllerNumber;
InputDeviceIdentifier mIdentifier;
- String8 mAlias;
+ std::string mAlias;
bool mIsExternal;
bool mHasMic;
uint32_t mSources;
@@ -130,7 +140,7 @@
bool mHasVibrator;
bool mHasButtonUnderPad;
- Vector<MotionRange> mMotionRanges;
+ std::vector<MotionRange> mMotionRanges;
};
/* Types of input device configuration files. */
@@ -149,7 +159,7 @@
*
* Returns an empty string if not found.
*/
-extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type);
@@ -162,8 +172,15 @@
*
* Returns an empty string if not found.
*/
-extern String8 getInputDeviceConfigurationFilePathByName(
- const String8& name, InputDeviceConfigurationFileType type);
+extern std::string getInputDeviceConfigurationFilePathByName(
+ const std::string& name, InputDeviceConfigurationFileType type);
+
+enum ReservedInputDeviceId : int32_t {
+ // Device id of a special "virtual" keyboard that is always present.
+ VIRTUAL_KEYBOARD_ID = -1,
+ // Device id of the "built-in" keyboard if there is one.
+ BUILT_IN_KEYBOARD_ID = 0,
+};
} // namespace android
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 4b33a96..59d16d1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -325,8 +325,11 @@
DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
DEFINE_KEYCODE(ALL_APPS),
DEFINE_KEYCODE(REFRESH),
+ DEFINE_KEYCODE(THUMBS_UP),
+ DEFINE_KEYCODE(THUMBS_DOWN),
+ DEFINE_KEYCODE(PROFILE_SWITCH),
- { NULL, 0 }
+ { nullptr, 0 }
};
static const InputEventLabel AXES[] = {
@@ -375,7 +378,7 @@
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
- { NULL, 0 }
+ { nullptr, 0 }
};
static const InputEventLabel LEDS[] = {
@@ -396,7 +399,7 @@
DEFINE_LED(CONTROLLER_4),
// NOTE: If you add new LEDs here, you must also add them to Input.h
- { NULL, 0 }
+ { nullptr, 0 }
};
static const InputEventLabel FLAGS[] = {
@@ -404,7 +407,7 @@
DEFINE_FLAG(FUNCTION),
DEFINE_FLAG(GESTURE),
- { NULL, 0 }
+ { nullptr, 0 }
};
static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
@@ -424,7 +427,7 @@
}
list++;
}
- return NULL;
+ return nullptr;
}
static inline int32_t getKeyCodeByLabel(const char* label) {
@@ -435,7 +438,7 @@
if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) {
return KEYCODES[keyCode].literal;
}
- return NULL;
+ return nullptr;
}
static inline uint32_t getKeyFlagByLabel(const char* label) {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ecdc075..63606e5 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -17,6 +17,8 @@
#ifndef _LIBINPUT_INPUT_TRANSPORT_H
#define _LIBINPUT_INPUT_TRANSPORT_H
+#pragma GCC system_header
+
/**
* Native input transport.
*
@@ -27,6 +29,9 @@
* The InputConsumer is used by the application to receive events from the input dispatcher.
*/
+#include <string>
+
+#include <binder/IBinder.h>
#include <input/Input.h>
#include <utils/Errors.h>
#include <utils/Timers.h>
@@ -35,6 +40,7 @@
#include <utils/BitSet.h>
namespace android {
+class Parcel;
/*
* Intermediate representation used to send input events and related signals.
@@ -99,8 +105,9 @@
int32_t flags;
int32_t metaState;
int32_t buttonState;
+ MotionClassification classification; // base type: uint8_t
+ uint8_t empty2[3];
int32_t edgeFlags;
- uint32_t empty2;
nsecs_t downTime __attribute__((aligned(8)));
float xOffset;
float yOffset;
@@ -154,6 +161,7 @@
virtual ~InputChannel();
public:
+ InputChannel() = default;
InputChannel(const std::string& name, int fd);
/* Creates a pair of input channels.
@@ -194,9 +202,19 @@
/* Returns a new object that has a duplicate of this channel's fd. */
sp<InputChannel> dup() const;
+ status_t write(Parcel& out) const;
+ status_t read(const Parcel& from);
+
+ sp<IBinder> getToken() const;
+ void setToken(const sp<IBinder>& token);
+
private:
+ void setFd(int fd);
+
std::string mName;
- int mFd;
+ int mFd = -1;
+
+ sp<IBinder> mToken = nullptr;
};
/*
@@ -225,6 +243,7 @@
uint32_t seq,
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t flags,
int32_t keyCode,
@@ -253,6 +272,7 @@
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
+ MotionClassification classification,
float xOffset,
float yOffset,
float xPrecision,
@@ -318,7 +338,7 @@
* Other errors probably indicate that the channel is broken.
*/
status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId);
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
/* Sends a finished signal to the publisher to inform it that the message
* with the specified sequence number has finished being process and whether
@@ -473,10 +493,9 @@
Vector<SeqChain> mSeqChains;
status_t consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId);
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
status_t consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent,
- int32_t* displayId);
+ Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
void updateTouchState(InputMessage& msg);
void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
diff --git a/services/inputflinger/InputWindow.h b/include/input/InputWindow.h
similarity index 77%
rename from services/inputflinger/InputWindow.h
rename to include/input/InputWindow.h
index 5a48375..916af69 100644
--- a/services/inputflinger/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -27,12 +27,15 @@
#include "InputApplication.h"
namespace android {
-
+class Parcel;
/*
* Describes the properties of a window that can receive input.
*/
struct InputWindowInfo {
+ InputWindowInfo() = default;
+ InputWindowInfo(const Parcel& from);
+
// Window flags from WindowManager.LayoutParams
enum {
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
@@ -114,16 +117,41 @@
INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
};
- sp<InputChannel> inputChannel;
+ /* These values are filled in by the WM and passed through SurfaceFlinger
+ * unless specified otherwise.
+ */
+ sp<IBinder> token;
std::string name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
nsecs_t dispatchingTimeout;
+
+ /* These values are filled in by SurfaceFlinger. */
int32_t frameLeft;
int32_t frameTop;
int32_t frameRight;
int32_t frameBottom;
- float scaleFactor;
+
+ /*
+ * SurfaceFlinger consumes this value to shrink the computed frame. This is
+ * different from shrinking the touchable region in that it DOES shift the coordinate
+ * space where-as the touchable region does not and is more like "cropping". This
+ * is used for window shadows.
+ */
+ int32_t surfaceInset = 0;
+
+ // A global scaling factor for all windows. Unlike windowScaleX/Y this results
+ // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
+ float globalScaleFactor;
+
+ // Scaling factors applied to individual windows.
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+
+ /*
+ * This is filled in by the WM relative to the frame and then translated
+ * to absolute coordinates by SurfaceFlinger once the frame is computed.
+ */
Region touchableRegion;
bool visible;
bool canReceiveKeys;
@@ -135,6 +163,10 @@
int32_t ownerUid;
int32_t inputFeatures;
int32_t displayId;
+ int32_t portalToDisplayId = ADISPLAY_ID_NONE;
+ InputApplicationInfo applicationInfo;
+ bool replaceTouchableRegionWithCrop;
+ wp<IBinder> touchableRegionCropHandle;
void addTouchableRegion(const Rect& region);
@@ -151,6 +183,9 @@
bool supportsSplitTouch() const;
bool overlaps(const InputWindowInfo* other) const;
+
+ status_t write(Parcel& output) const;
+ static InputWindowInfo read(const Parcel& from);
};
@@ -162,22 +197,23 @@
*/
class InputWindowHandle : public RefBase {
public:
- const sp<InputApplicationHandle> inputApplicationHandle;
inline const InputWindowInfo* getInfo() const {
- return mInfo;
+ return &mInfo;
}
- inline sp<InputChannel> getInputChannel() const {
- return mInfo ? mInfo->inputChannel : NULL;
+ sp<IBinder> getToken() const;
+
+ sp<IBinder> getApplicationToken() {
+ return mInfo.applicationInfo.token;
}
inline std::string getName() const {
- return mInfo ? mInfo->name : "<invalid>";
+ return mInfo.token ? mInfo.name : "<invalid>";
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+ return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
/**
@@ -192,16 +228,21 @@
virtual bool updateInfo() = 0;
/**
- * Releases the storage used by the associated information when it is
+ * Updates from another input window handle.
+ */
+ void updateFrom(const sp<InputWindowHandle> handle);
+
+ /**
+ * Releases the channel used by the associated information when it is
* no longer needed.
*/
- void releaseInfo();
+ void releaseChannel();
protected:
- explicit InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+ explicit InputWindowHandle();
virtual ~InputWindowHandle();
- InputWindowInfo* mInfo;
+ InputWindowInfo mInfo;
};
} // namespace android
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 5e542b8..a1a32a6 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -27,7 +27,6 @@
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Tokenizer.h>
-#include <utils/String8.h>
#include <utils/Unicode.h>
#include <utils/RefBase.h>
@@ -75,10 +74,10 @@
};
/* Loads a key character map from a file. */
- static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
+ static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap);
/* Loads a key character map from its string contents. */
- static status_t loadContents(const String8& filename,
+ static status_t loadContents(const std::string& filename,
const char* contents, Format format, sp<KeyCharacterMap>* outMap);
/* Combines a base key character map and an overlay. */
@@ -221,7 +220,7 @@
status_t parseKey();
status_t parseKeyProperty();
status_t finishKey(Key* key);
- status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseModifier(const std::string& token, int32_t* outMetaState);
status_t parseCharacterLiteral(char16_t* outCharacter);
};
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 1e8de71..26f3501 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -62,11 +62,11 @@
*/
class KeyLayoutMap : public RefBase {
public:
- static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap);
+ static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap);
status_t mapKey(int32_t scanCode, int32_t usageCode,
int32_t* outKeyCode, uint32_t* outFlags) const;
- status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
+ status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const;
status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const;
status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index d4903e9..92da10c 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -21,20 +21,10 @@
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
#include <utils/Errors.h>
-#include <utils/String8.h>
#include <utils/PropertyMap.h>
namespace android {
-enum {
- /* Device id of the built in keyboard. */
- DEVICE_ID_BUILT_IN_KEYBOARD = 0,
-
- /* Device id of a generic virtual keyboard with a full layout that can be used
- * to synthesize key events. */
- DEVICE_ID_VIRTUAL_KEYBOARD = -1,
-};
-
class KeyLayoutMap;
class KeyCharacterMap;
@@ -43,10 +33,10 @@
*/
class KeyMap {
public:
- String8 keyLayoutFile;
+ std::string keyLayoutFile;
sp<KeyLayoutMap> keyLayoutMap;
- String8 keyCharacterMapFile;
+ std::string keyCharacterMapFile;
sp<KeyCharacterMap> keyCharacterMap;
KeyMap();
@@ -56,11 +46,11 @@
const PropertyMap* deviceConfiguration);
inline bool haveKeyLayout() const {
- return !keyLayoutFile.isEmpty();
+ return !keyLayoutFile.empty();
}
inline bool haveKeyCharacterMap() const {
- return !keyCharacterMapFile.isEmpty();
+ return !keyCharacterMapFile.empty();
}
inline bool isComplete() const {
@@ -68,12 +58,12 @@
}
private:
- bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
- status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name);
+ bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
+ status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name);
status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name);
- String8 getPath(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name, InputDeviceConfigurationFileType type);
+ const std::string& name);
+ std::string getPath(const InputDeviceIdentifier& deviceIdentifier,
+ const std::string& name, InputDeviceConfigurationFileType type);
};
/**
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
new file mode 100644
index 0000000..b49c623
--- /dev/null
+++ b/include/input/TouchVideoFrame.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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_TOUCHVIDEOFRAME_H
+#define _LIBINPUT_TOUCHVIDEOFRAME_H
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <ui/DisplayInfo.h>
+#include <vector>
+
+namespace android {
+
+/**
+ * Represents data from a single scan of the touchscreen device.
+ * Similar in concept to a video frame, but the touch strength is used as
+ * the values instead.
+ */
+class TouchVideoFrame {
+public:
+ TouchVideoFrame(uint32_t height, uint32_t width, std::vector<int16_t> data,
+ const struct timeval& timestamp);
+
+ bool operator==(const TouchVideoFrame& rhs) const;
+
+ /**
+ * Height of the frame
+ */
+ uint32_t getHeight() const;
+ /**
+ * Width of the frame
+ */
+ uint32_t getWidth() const;
+ /**
+ * The touch strength data.
+ * The array is a 2-D row-major matrix, with dimensions (height, width).
+ * Total size of the array should equal getHeight() * getWidth().
+ * Data is allowed to be negative.
+ */
+ const std::vector<int16_t>& getData() const;
+ /**
+ * Time at which the heatmap was taken.
+ */
+ const struct timeval& getTimestamp() const;
+
+ /**
+ * Rotate the video frame.
+ * The rotation value is an enum from ui/DisplayInfo.h
+ */
+ void rotate(int32_t orientation);
+
+private:
+ uint32_t mHeight;
+ uint32_t mWidth;
+ std::vector<int16_t> mData;
+ struct timeval mTimestamp;
+
+ /**
+ * Common method for 90 degree and 270 degree rotation
+ */
+ void rotateQuarterTurn(bool clockwise);
+ void rotate180();
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_TOUCHVIDEOFRAME_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index ffa1614..727865a 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -63,7 +63,7 @@
// Creates a velocity tracker using the specified strategy.
// If strategy is NULL, uses the default strategy for the platform.
- VelocityTracker(const char* strategy = NULL);
+ VelocityTracker(const char* strategy = nullptr);
~VelocityTracker();
diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h
index e245ead..6e8e2c9 100644
--- a/include/input/VirtualKeyMap.h
+++ b/include/input/VirtualKeyMap.h
@@ -23,8 +23,8 @@
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Tokenizer.h>
-#include <utils/String8.h>
#include <utils/Unicode.h>
+#include <vector>
namespace android {
@@ -50,9 +50,9 @@
public:
~VirtualKeyMap();
- static status_t load(const String8& filename, VirtualKeyMap** outMap);
+ static std::unique_ptr<VirtualKeyMap> load(const std::string& filename);
- inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const {
+ inline const std::vector<VirtualKeyDefinition>& getVirtualKeys() const {
return mVirtualKeys;
}
@@ -71,7 +71,7 @@
bool parseNextIntField(int32_t* outValue);
};
- Vector<VirtualKeyDefinition> mVirtualKeys;
+ std::vector<VirtualKeyDefinition> mVirtualKeys;
VirtualKeyMap();
};
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
index 3c81f0f..853f0c9 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -45,7 +45,7 @@
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 = IBinder::FIRST_CALL_TRANSACTION + 14,
+ 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,
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index a20154f..ad8287c 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -25,4 +25,9 @@
host_supported: true,
vendor_available: true,
export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
}
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 28d0e4f..49a9414 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -89,6 +89,15 @@
return false;
}
+int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& callingPackage)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->getUidProcessState(uid, callingPackage);
+ }
+ return PROCESS_STATE_UNKNOWN;
+}
+
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 da10687..aedf6b0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -107,6 +107,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wzero-as-null-pointer-constant",
],
product_variables: {
binder32bit: {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index f6cc3af..cb0e08d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -86,6 +86,10 @@
class BBinder::Extras
{
public:
+ // unlocked objects
+ bool mRequestingSid = false;
+
+ // for below objects
Mutex mLock;
BpBinder::ObjectManager mObjects;
};
@@ -163,19 +167,8 @@
const void* objectID, void* object, void* cleanupCookie,
object_cleanup_func func)
{
- Extras* e = mExtras.load(std::memory_order_acquire);
-
- if (!e) {
- e = new Extras;
- Extras* expected = nullptr;
- if (!mExtras.compare_exchange_strong(expected, e,
- std::memory_order_release,
- std::memory_order_acquire)) {
- delete e;
- e = expected; // Filled in by CAS
- }
- if (e == nullptr) return; // out of memory
- }
+ Extras* e = getOrCreateExtras();
+ if (!e) return; // out of memory
AutoMutex _l(e->mLock);
e->mObjects.attach(objectID, object, cleanupCookie, func);
@@ -204,6 +197,30 @@
return this;
}
+bool BBinder::isRequestingSid()
+{
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ return e && e->mRequestingSid;
+}
+
+void BBinder::setRequestingSid(bool requestingSid)
+{
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (!e) {
+ // default is false. Most things don't need sids, so avoiding allocations when possible.
+ if (!requestingSid) {
+ return;
+ }
+
+ e = getOrCreateExtras();
+ if (!e) return; // out of memory
+ }
+
+ e->mRequestingSid = requestingSid;
+}
+
BBinder::~BBinder()
{
Extras* e = mExtras.load(std::memory_order_relaxed);
@@ -267,6 +284,25 @@
}
}
+BBinder::Extras* BBinder::getOrCreateExtras()
+{
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (!e) {
+ e = new Extras;
+ Extras* expected = nullptr;
+ if (!mExtras.compare_exchange_strong(expected, e,
+ std::memory_order_release,
+ std::memory_order_acquire)) {
+ delete e;
+ e = expected; // Filled in by CAS
+ }
+ if (e == nullptr) return nullptr; // out of memory
+ }
+
+ return e;
+}
+
// ---------------------------------------------------------------------------
enum {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 428db4d..377f604 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,8 +17,8 @@
#include <unistd.h>
#include <fcntl.h>
+#include <binder/ActivityManager.h>
#include <binder/IActivityManager.h>
-
#include <binder/Parcel.h>
namespace android {
@@ -90,6 +90,20 @@
if (reply.readExceptionCode() != 0) return false;
return reply.readInt32() == 1;
}
+
+ virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(uid);
+ data.writeString16(callingPackage);
+ remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) {
+ return ActivityManager::PROCESS_STATE_UNKNOWN;
+ }
+ return reply.readInt32();
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 307bc28..caf2318 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -86,7 +86,7 @@
virtual void* getBase() const;
virtual size_t getSize() const;
virtual uint32_t getFlags() const;
- virtual uint32_t getOffset() const;
+ off_t getOffset() const override;
private:
friend class IMemory;
@@ -113,7 +113,7 @@
mutable void* mBase;
mutable size_t mSize;
mutable uint32_t mFlags;
- mutable uint32_t mOffset;
+ mutable off_t mOffset;
mutable bool mRealHeap;
mutable Mutex mLock;
};
@@ -189,13 +189,16 @@
data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
sp<IBinder> heap = reply.readStrongBinder();
- ssize_t o = reply.readInt32();
- size_t s = reply.readInt32();
if (heap != nullptr) {
mHeap = interface_cast<IMemoryHeap>(heap);
if (mHeap != nullptr) {
+ const int64_t offset64 = reply.readInt64();
+ const uint64_t size64 = reply.readUint64();
+ const ssize_t o = (ssize_t)offset64;
+ const size_t s = (size_t)size64;
size_t heapSize = mHeap->getSize();
- if (s <= heapSize
+ if (s == size64 && o == offset64 // ILP32 bounds check
+ && s <= heapSize
&& o >= 0
&& (static_cast<size_t>(o) <= heapSize - s)) {
mOffset = o;
@@ -236,8 +239,8 @@
ssize_t offset;
size_t size;
reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) );
- reply->writeInt32(offset);
- reply->writeInt32(size);
+ reply->writeInt64(offset);
+ reply->writeUint64(size);
return NO_ERROR;
} break;
default:
@@ -316,18 +319,23 @@
data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
status_t err = remote()->transact(HEAP_ID, data, &reply);
int parcel_fd = reply.readFileDescriptor();
- ssize_t size = reply.readInt32();
- uint32_t flags = reply.readInt32();
- uint32_t offset = reply.readInt32();
-
- ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)",
- IInterface::asBinder(this).get(),
- parcel_fd, size, err, strerror(-err));
+ const uint64_t size64 = reply.readUint64();
+ const int64_t offset64 = reply.readInt64();
+ const uint32_t flags = reply.readUint32();
+ const size_t size = (size_t)size64;
+ const off_t offset = (off_t)offset64;
+ if (err != NO_ERROR || // failed transaction
+ size != size64 || offset != offset64) { // ILP32 size check
+ ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)",
+ IInterface::asBinder(this).get(),
+ parcel_fd, size, err, strerror(-err));
+ return;
+ }
Mutex::Autolock _l(mLock);
if (mHeapId.load(memory_order_relaxed) == -1) {
int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
- ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
+ ALOGE_IF(fd == -1, "cannot dup fd=%d, size=%zu, err=%d (%s)",
parcel_fd, size, err, strerror(errno));
int access = PROT_READ;
@@ -337,7 +345,7 @@
mRealHeap = true;
mBase = mmap(nullptr, size, access, MAP_SHARED, fd, offset);
if (mBase == MAP_FAILED) {
- ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)",
+ ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zu, fd=%d (%s)",
IInterface::asBinder(this).get(), size, fd, strerror(errno));
close(fd);
} else {
@@ -371,7 +379,7 @@
return mFlags;
}
-uint32_t BpMemoryHeap::getOffset() const {
+off_t BpMemoryHeap::getOffset() const {
assertMapped();
return mOffset;
}
@@ -394,9 +402,9 @@
case HEAP_ID: {
CHECK_INTERFACE(IMemoryHeap, data, reply);
reply->writeFileDescriptor(getHeapID());
- reply->writeInt32(getSize());
- reply->writeInt32(getFlags());
- reply->writeInt32(getOffset());
+ reply->writeUint64(getSize());
+ reply->writeInt64(getOffset());
+ reply->writeUint32(getFlags());
return NO_ERROR;
} break;
default:
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 22f6f54..9a561cb 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -88,7 +88,8 @@
"BR_FINISHED",
"BR_DEAD_BINDER",
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
- "BR_FAILED_REPLY"
+ "BR_FAILED_REPLY",
+ "BR_TRANSACTION_SEC_CTX",
};
static const char *kCommandStrings[] = {
@@ -111,6 +112,8 @@
"BC_DEAD_BINDER_DONE"
};
+static const int64_t kWorkSourcePropagatedBitIndex = 32;
+
static const char* getReturnString(uint32_t cmd)
{
size_t idx = cmd & 0xff;
@@ -363,6 +366,11 @@
return mCallingPid;
}
+const char* IPCThreadState::getCallingSid() const
+{
+ return mCallingSid;
+}
+
uid_t IPCThreadState::getCallingUid() const
{
return mCallingUid;
@@ -370,6 +378,7 @@
int64_t IPCThreadState::clearCallingIdentity()
{
+ // ignore mCallingSid for legacy reasons
int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
clearCaller();
return token;
@@ -385,6 +394,48 @@
return mStrictModePolicy;
}
+int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid)
+{
+ int64_t token = setCallingWorkSourceUidWithoutPropagation(uid);
+ mPropagateWorkSource = true;
+ return token;
+}
+
+int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid)
+{
+ const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex;
+ int64_t token = propagatedBit | mWorkSource;
+ mWorkSource = uid;
+ return token;
+}
+
+void IPCThreadState::clearPropagateWorkSource()
+{
+ mPropagateWorkSource = false;
+}
+
+bool IPCThreadState::shouldPropagateWorkSource() const
+{
+ return mPropagateWorkSource;
+}
+
+uid_t IPCThreadState::getCallingWorkSourceUid() const
+{
+ return mWorkSource;
+}
+
+int64_t IPCThreadState::clearCallingWorkSource()
+{
+ return setCallingWorkSourceUid(kUnsetWorkSource);
+}
+
+void IPCThreadState::restoreCallingWorkSource(int64_t token)
+{
+ uid_t uid = (int)token;
+ setCallingWorkSourceUidWithoutPropagation(uid);
+ mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1;
+}
+
void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
{
mLastTransactionBinderFlags = flags;
@@ -398,12 +449,14 @@
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
+ mCallingSid = nullptr; // not enough data to restore
mCallingPid = (int)token;
}
void IPCThreadState::clearCaller()
{
mCallingPid = getpid();
+ mCallingSid = nullptr; // expensive to lookup
mCallingUid = getuid();
}
@@ -748,6 +801,8 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
+ mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0),
mCallRestriction(mProcess->mCallRestriction)
@@ -1089,10 +1144,19 @@
}
break;
+ case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION:
{
- binder_transaction_data tr;
- result = mIn.read(&tr, sizeof(tr));
+ binder_transaction_data_secctx tr_secctx;
+ binder_transaction_data& tr = tr_secctx.transaction_data;
+
+ if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
+ result = mIn.read(&tr_secctx, sizeof(tr_secctx));
+ } else {
+ result = mIn.read(&tr, sizeof(tr));
+ tr_secctx.secctx = 0;
+ }
+
ALOG_ASSERT(result == NO_ERROR,
"Not enough command data for brTRANSACTION");
if (result != NO_ERROR) break;
@@ -1108,15 +1172,25 @@
tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
const pid_t origPid = mCallingPid;
+ const char* origSid = mCallingSid;
const uid_t origUid = mCallingUid;
const int32_t origStrictModePolicy = mStrictModePolicy;
const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
+ const int32_t origWorkSource = mWorkSource;
+ const bool origPropagateWorkSet = mPropagateWorkSource;
+ // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface
+ // is only guaranteed to be called for AIDL-generated stubs so we reset the work source
+ // here to never propagate it.
+ clearCallingWorkSource();
+ clearPropagateWorkSource();
mCallingPid = tr.sender_pid;
+ mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
mCallingUid = tr.sender_euid;
mLastTransactionBinderFlags = tr.flags;
- //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
+ // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
+ // (mCallingSid ? mCallingSid : "<N/A>"), mCallingUid);
Parcel reply;
status_t error;
@@ -1148,8 +1222,8 @@
}
mIPCThreadStateBase->popCurrentState();
- //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
- // mCallingPid, origPid, origUid);
+ //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n",
+ // mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid);
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
@@ -1160,9 +1234,12 @@
}
mCallingPid = origPid;
+ mCallingSid = origSid;
mCallingUid = origUid;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
+ mWorkSource = origWorkSource;
+ mPropagateWorkSource = origPropagateWorkSet;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 697e948..82f9047 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -55,6 +55,16 @@
data.writeInt32(disabled ? 1 : 0);
remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
+ data.writeInt32((int32_t) uid);
+ data.writeInt32(procState);
+ data.writeInt64(procStateSeq);
+ remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
// ----------------------------------------------------------------------
@@ -89,6 +99,14 @@
onUidIdle(uid, disabled);
return NO_ERROR;
} break;
+ case ON_UID_STATE_CHANGED_TRANSACTION: {
+ CHECK_INTERFACE(IUidObserver, data, reply);
+ uid_t uid = data.readInt32();
+ int32_t procState = data.readInt32();
+ int64_t procStateSeq = data.readInt64();
+ onUidStateChanged(uid, procState, procStateSeq);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 9850ad9..4c300b4 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -76,7 +76,7 @@
}
}
-MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
+MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset)
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(nullptr), mNeedUnmap(false), mOffset(0)
{
@@ -85,7 +85,7 @@
mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
}
-status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
+status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device)
{
if (mFD != -1) {
return INVALID_OPERATION;
@@ -98,13 +98,20 @@
return NO_ERROR;
}
-status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
+status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset)
{
if (size == 0) {
// try to figure out the size automatically
struct stat sb;
- if (fstat(fd, &sb) == 0)
- size = sb.st_size;
+ if (fstat(fd, &sb) == 0) {
+ size = (size_t)sb.st_size;
+ // sb.st_size is off_t which on ILP32 may be 64 bits while size_t is 32 bits.
+ if ((off_t)size != sb.st_size) {
+ ALOGE("%s: size of file %lld cannot fit in memory",
+ __func__, (long long)sb.st_size);
+ return INVALID_OPERATION;
+ }
+ }
// if it didn't work, let mmap() fail.
}
@@ -112,12 +119,12 @@
void* base = (uint8_t*)mmap(nullptr, size,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
if (base == MAP_FAILED) {
- ALOGE("mmap(fd=%d, size=%u) failed (%s)",
- fd, uint32_t(size), strerror(errno));
+ ALOGE("mmap(fd=%d, size=%zu) failed (%s)",
+ fd, size, strerror(errno));
close(fd);
return -errno;
}
- //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
+ //ALOGD("mmap(fd=%d, base=%p, size=%zu)", fd, base, size);
mBase = base;
mNeedUnmap = true;
} else {
@@ -140,7 +147,7 @@
int fd = android_atomic_or(-1, &mFD);
if (fd >= 0) {
if (mNeedUnmap) {
- //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize);
+ //ALOGD("munmap(fd=%d, base=%p, size=%zu)", fd, mBase, mSize);
munmap(mBase, mSize);
}
mBase = nullptr;
@@ -169,7 +176,7 @@
return mDevice;
}
-uint32_t MemoryHeapBase::getOffset() const {
+off_t MemoryHeapBase::getOffset() const {
return mOffset;
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 0423264..a595c01 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -74,7 +74,7 @@
}
// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
-#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
+#define STRICT_MODE_PENALTY_GATHER (1 << 31)
// XXX This can be made public if we want to provide
// support for typed data.
@@ -223,7 +223,7 @@
}
if (binder != nullptr) {
- IBinder *local = binder->localBinder();
+ BBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == nullptr) {
@@ -235,6 +235,9 @@
obj.handle = handle;
obj.cookie = 0;
} else {
+ if (local->isRequestingSid()) {
+ obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
+ }
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
@@ -599,15 +602,53 @@
return mHasFds;
}
+void Parcel::updateWorkSourceRequestHeaderPosition() const {
+ // Only update the request headers once. We only want to point
+ // to the first headers read/written.
+ if (!mRequestHeaderPresent) {
+ mWorkSourceRequestHeaderPosition = dataPosition();
+ mRequestHeaderPresent = true;
+ }
+}
+
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
- writeInt32(IPCThreadState::self()->getStrictModePolicy() |
- STRICT_MODE_PENALTY_GATHER);
+ const IPCThreadState* threadState = IPCThreadState::self();
+ writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+ updateWorkSourceRequestHeaderPosition();
+ writeInt32(threadState->shouldPropagateWorkSource() ?
+ threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
+bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
+{
+ if (!mRequestHeaderPresent) {
+ return false;
+ }
+
+ const size_t initialPosition = dataPosition();
+ setDataPosition(mWorkSourceRequestHeaderPosition);
+ status_t err = writeInt32(uid);
+ setDataPosition(initialPosition);
+ return err == NO_ERROR;
+}
+
+uid_t Parcel::readCallingWorkSourceUid()
+{
+ if (!mRequestHeaderPresent) {
+ return IPCThreadState::kUnsetWorkSource;
+ }
+
+ const size_t initialPosition = dataPosition();
+ setDataPosition(mWorkSourceRequestHeaderPosition);
+ uid_t uid = readInt32();
+ setDataPosition(initialPosition);
+ return uid;
+}
+
bool Parcel::checkInterface(IBinder* binder) const
{
return enforceInterface(binder->getInterfaceDescriptor());
@@ -616,6 +657,7 @@
bool Parcel::enforceInterface(const String16& interface,
IPCThreadState* threadState) const
{
+ // StrictModePolicy.
int32_t strictPolicy = readInt32();
if (threadState == nullptr) {
threadState = IPCThreadState::self();
@@ -630,6 +672,11 @@
} else {
threadState->setStrictModePolicy(strictPolicy);
}
+ // WorkSource.
+ updateWorkSourceRequestHeaderPosition();
+ int32_t workSource = readInt32();
+ threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+ // Interface descriptor.
const String16 str(readString16());
if (str == interface) {
return true;
@@ -874,6 +921,16 @@
return writeNullableTypedVector(val, &Parcel::writeInt64);
}
+status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val)
+{
+ return writeTypedVector(val, &Parcel::writeUint64);
+}
+
+status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeUint64);
+}
+
status_t Parcel::writeFloatVector(const std::vector<float>& val)
{
return writeTypedVector(val, &Parcel::writeFloat);
@@ -1735,6 +1792,14 @@
return readTypedVector(val, &Parcel::readInt64);
}
+status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUint64);
+}
+
+status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const {
+ return readTypedVector(val, &Parcel::readUint64);
+}
+
status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const {
return readNullableTypedVector(val, &Parcel::readFloat);
}
@@ -2332,6 +2397,15 @@
int fd = readFileDescriptor();
if (fd == int(BAD_TYPE)) return BAD_VALUE;
+ if (!ashmem_valid(fd)) {
+ ALOGE("invalid fd");
+ return BAD_VALUE;
+ }
+ int size = ashmem_get_size_region(fd);
+ if (size < 0 || size_t(size) < len) {
+ ALOGE("request size %zu does not match fd size %d", len, size);
+ return BAD_VALUE;
+ }
void* ptr = ::mmap(nullptr, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ,
MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) return NO_MEMORY;
@@ -2855,6 +2929,8 @@
mAllowFds = true;
mOwner = nullptr;
mOpenAshmemSize = 0;
+ mWorkSourceRequestHeaderPosition = 0;
+ mRequestHeaderPresent = false;
// racing multiple init leads only to multiple identical write
if (gMaxFds == 0) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 3798b61..79db0cb 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -181,8 +181,20 @@
mBinderContextCheckFunc = checkFunc;
mBinderContextUserData = userData;
- int dummy = 0;
- status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+ flat_binder_object obj {
+ .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
+ };
+
+ status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+
+ // fallback to original method
+ if (result != 0) {
+ android_errorWriteLog(0x534e4554, "121035042");
+
+ int dummy = 0;
+ result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+ }
+
if (result == 0) {
mManagesContexts = true;
} else if (result == -1) {
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 5b66b92..ccde12a 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -54,4 +54,9 @@
long getVersionCodeForPackage(in String packageName);
+ /**
+ * Return if each app, identified by its package name allows its audio to be recorded.
+ * Unknown packages are mapped to false.
+ */
+ boolean[] isAudioPlaybackCaptureAllowed(in @utf8InCpp String[] packageNames);
}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index b8db091..7b086d0 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -31,6 +31,8 @@
public:
enum {
+ // Flag for registerUidObserver: report uid state changed
+ UID_OBSERVER_PROCSTATE = 1<<0,
// Flag for registerUidObserver: report uid gone
UID_OBSERVER_GONE = 1<<1,
// Flag for registerUidObserver: report uid has become idle
@@ -40,8 +42,28 @@
};
enum {
- // Not a real process state
- PROCESS_STATE_UNKNOWN = -1
+ PROCESS_STATE_UNKNOWN = -1,
+ PROCESS_STATE_PERSISTENT = 0,
+ PROCESS_STATE_PERSISTENT_UI = 1,
+ PROCESS_STATE_TOP = 2,
+ PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
+ PROCESS_STATE_FOREGROUND_SERVICE = 4,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+ PROCESS_STATE_BACKUP = 9,
+ PROCESS_STATE_SERVICE = 10,
+ PROCESS_STATE_RECEIVER = 11,
+ PROCESS_STATE_TOP_SLEEPING = 12,
+ PROCESS_STATE_HEAVY_WEIGHT = 13,
+ PROCESS_STATE_HOME = 14,
+ PROCESS_STATE_LAST_ACTIVITY = 15,
+ PROCESS_STATE_CACHED_ACTIVITY = 16,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+ PROCESS_STATE_CACHED_RECENT = 18,
+ PROCESS_STATE_CACHED_EMPTY = 19,
+ PROCESS_STATE_NONEXISTENT = 20,
};
ActivityManager();
@@ -53,8 +75,10 @@
const String16& callingPackage);
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
+ int getUidProcessState(const uid_t uid, const String16& callingPackage);
- status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+
+ status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
private:
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index c5b57c7..37237df 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -95,6 +95,20 @@
OP_USE_FINGERPRINT = 55,
OP_BODY_SENSORS = 56,
OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
+ OP_READ_PHONE_NUMBERS = 65,
+ OP_REQUEST_INSTALL_PACKAGES = 66,
+ OP_PICTURE_IN_PICTURE = 67,
+ OP_INSTANT_APP_START_FOREGROUND = 68,
+ OP_ANSWER_PHONE_CALLS = 69,
+ OP_RUN_ANY_IN_BACKGROUND = 70,
+ OP_CHANGE_WIFI_STATE = 71,
+ OP_REQUEST_DELETE_PACKAGES = 72,
+ OP_BIND_ACCESSIBILITY_SERVICE = 73,
+ OP_ACCEPT_HANDOVER = 74,
+ OP_MANAGE_IPSEC_TUNNELS = 75,
+ OP_START_FOREGROUND = 76,
+ OP_BLUETOOTH_SCAN = 77,
+ OP_USE_BIOMETRIC = 78,
};
AppOpsManager();
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index c251468..cf3ef84 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -60,6 +60,10 @@
virtual BBinder* localBinder();
+ bool isRequestingSid();
+ // This must be called before the object is sent to another process. Not thread safe.
+ void setRequestingSid(bool requestSid);
+
protected:
virtual ~BBinder();
@@ -75,6 +79,8 @@
class Extras;
+ Extras* getOrCreateExtras();
+
std::atomic<Extras*> mExtras;
void* mReserved0;
};
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index f34969b..6abc071 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -38,12 +38,14 @@
const String16& callingPackage) = 0;
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;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
- IS_UID_ACTIVE_TRANSACTION
+ IS_UID_ACTIVE_TRANSACTION,
+ GET_UID_PROCESS_STATE_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 0630963..aa44285 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -47,7 +47,7 @@
* (method calls, property get and set) is down through a low-level
* protocol implemented on top of the transact() API.
*/
-class IBinder : public virtual RefBase
+class [[clang::lto_visibility_public]] IBinder : public virtual RefBase
{
public:
enum {
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index db9f53a..071946f 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -43,7 +43,7 @@
virtual void* getBase() const = 0;
virtual size_t getSize() const = 0;
virtual uint32_t getFlags() const = 0;
- virtual uint32_t getOffset() const = 0;
+ virtual off_t getOffset() const = 0;
// these are there just for backward source compatibility
int32_t heapID() const { return getHeapID(); }
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 745f618..614b0b3 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -42,15 +42,34 @@
status_t clearLastError();
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.
+ const char* getCallingSid() const;
uid_t getCallingUid() const;
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
+ // See Binder#setCallingWorkSourceUid in Binder.java.
+ int64_t setCallingWorkSourceUid(uid_t uid);
+ // Internal only. Use setCallingWorkSourceUid(uid) instead.
+ int64_t setCallingWorkSourceUidWithoutPropagation(uid_t uid);
+ // See Binder#getCallingWorkSourceUid in Binder.java.
+ uid_t getCallingWorkSourceUid() const;
+ // See Binder#clearCallingWorkSource in Binder.java.
+ int64_t clearCallingWorkSource();
+ // See Binder#restoreCallingWorkSource in Binder.java.
+ void restoreCallingWorkSource(int64_t token);
+ void clearPropagateWorkSource();
+ bool shouldPropagateWorkSource() const;
+
void setLastTransactionBinderFlags(int32_t flags);
int32_t getLastTransactionBinderFlags() const;
int64_t clearCallingIdentity();
+ // Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
int setupPolling(int* fd);
@@ -118,6 +137,13 @@
// infer information about thread state.
bool isServingCall() const;
+ // The work source represents the UID of the process we should attribute the transaction
+ // to. We use -1 to specify that the work source was not set using #setWorkSource.
+ //
+ // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
+ // side.
+ static const int32_t kUnsetWorkSource = -1;
+
private:
IPCThreadState();
~IPCThreadState();
@@ -154,7 +180,13 @@
Parcel mOut;
status_t mLastError;
pid_t mCallingPid;
+ const char* mCallingSid;
uid_t mCallingUid;
+ // The UID of the process who is responsible for this transaction.
+ // This is used for resource attribution.
+ int32_t mWorkSource;
+ // Whether the work source should be propagated.
+ bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
IPCThreadStateBase *mIPCThreadStateBase;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index e5d8ea6..aeea1a2 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -88,7 +88,7 @@
const sp<IServiceManager> sm = defaultServiceManager();
if (sm != nullptr) {
*outService = interface_cast<INTERFACE>(sm->getService(name));
- if ((*outService) != NULL) return NO_ERROR;
+ if ((*outService) != nullptr) return NO_ERROR;
}
return NAME_NOT_FOUND;
}
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index 9937ad6..a1f530d 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,11 +34,13 @@
virtual void onUidGone(uid_t uid, bool disabled) = 0;
virtual void onUidActive(uid_t uid) = 0;
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
+ virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
ON_UID_ACTIVE_TRANSACTION,
- ON_UID_IDLE_TRANSACTION
+ ON_UID_IDLE_TRANSACTION,
+ ON_UID_STATE_CHANGED_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 4be20a0..100d784 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -42,7 +42,7 @@
* maps the memory referenced by fd. but DOESN'T take ownership
* of the filedescriptor (it makes a copy with dup()
*/
- MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);
+ MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0);
/*
* maps memory from the given device
@@ -64,7 +64,7 @@
virtual size_t getSize() const;
virtual uint32_t getFlags() const;
- virtual uint32_t getOffset() const;
+ off_t getOffset() const override;
const char* getDevice() const;
@@ -82,11 +82,11 @@
protected:
MemoryHeapBase();
// init() takes ownership of fd
- status_t init(int fd, void *base, int size,
+ status_t init(int fd, void *base, size_t size,
int flags = 0, const char* device = nullptr);
private:
- status_t mapfd(int fd, size_t size, uint32_t offset = 0);
+ status_t mapfd(int fd, size_t size, off_t offset = 0);
int mFD;
size_t mSize;
@@ -94,7 +94,7 @@
uint32_t mFlags;
const char* mDevice;
bool mNeedUnmap;
- uint32_t mOffset;
+ off_t mOffset;
};
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index c9c273a..e5219a5 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -20,6 +20,8 @@
#include <string>
#include <vector>
+#include <linux/android/binder.h>
+
#include <android-base/unique_fd.h>
#include <cutils/native_handle.h>
#include <utils/Errors.h>
@@ -27,7 +29,6 @@
#include <utils/String16.h>
#include <utils/Vector.h>
#include <utils/Flattenable.h>
-#include <linux/android/binder.h>
#include <binder/IInterface.h>
#include <binder/Parcelable.h>
@@ -139,6 +140,8 @@
status_t writeInt32Vector(const std::vector<int32_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::unique_ptr<std::vector<uint64_t>>& val);
+ status_t writeUint64Vector(const std::vector<uint64_t>& 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::unique_ptr<std::vector<double>>& val);
@@ -313,6 +316,8 @@
status_t readInt32Vector(std::vector<int32_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::unique_ptr<std::vector<uint64_t>>* val) const;
+ status_t readUint64Vector(std::vector<uint64_t>* 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::unique_ptr<std::vector<double>>* val) const;
@@ -391,6 +396,12 @@
static size_t getGlobalAllocSize();
static size_t getGlobalAllocCount();
+ bool replaceCallingWorkSourceUid(uid_t uid);
+ // Returns the work source provided by the caller. This can only be trusted for trusted calling
+ // uid.
+ uid_t readCallingWorkSourceUid();
+ void readRequestHeaders() const;
+
private:
typedef void (*release_func)(Parcel* parcel,
const uint8_t* data, size_t dataSize,
@@ -425,6 +436,7 @@
void initState();
void scanForFds() const;
status_t validateReadData(size_t len) const;
+ void updateWorkSourceRequestHeaderPosition() const;
template<class T>
status_t readAligned(T *pArg) const;
@@ -473,6 +485,9 @@
mutable size_t mNextObjectHint;
mutable bool mObjectsSorted;
+ mutable bool mRequestHeaderPresent;
+ mutable size_t mWorkSourceRequestHeaderPosition;
+
mutable bool mFdsKnown;
mutable bool mHasFds;
bool mAllowFds;
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index 3bfd462..5fa2ff6 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -152,6 +152,12 @@
return callParcel("writeParcelableVector",
[&]() { return parcel->writeParcelableVector(v); });
}
+ status_t read(const Parcel& parcel, float* f) const {
+ return callParcel("readFloat", [&]() { return parcel.readFloat(f); });
+ }
+ status_t write(Parcel* parcel, float f) const {
+ return callParcel("writeFloat", [&]() { return parcel->writeFloat(f); });
+ }
// Templates to handle integral types. We use a struct template to require that the called
// function exactly matches the signedness and size of the argument (e.g., the argument isn't
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 99a71bd..78f1159 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -71,6 +71,8 @@
BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
+ BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
+ BINDER_LIB_TEST_ECHO_VECTOR,
};
pid_t start_server_process(int arg2, bool usePoll = false)
@@ -179,6 +181,7 @@
public:
virtual void SetUp() {
m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+ IPCThreadState::self()->restoreCallingWorkSource(0);
}
virtual void TearDown() {
}
@@ -938,6 +941,141 @@
EXPECT_EQ(NO_ERROR, ret);
}
+TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
+{
+ status_t ret;
+ Parcel data, reply;
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+ EXPECT_EQ(-1, reply.readInt32());
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSet)
+{
+ status_t ret;
+ Parcel data, reply;
+ IPCThreadState::self()->clearCallingWorkSource();
+ int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100);
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+ EXPECT_EQ(100, reply.readInt32());
+ EXPECT_EQ(-1, previousWorkSource);
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100);
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+ EXPECT_EQ(-1, reply.readInt32());
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceCleared)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ int64_t token = IPCThreadState::self()->clearCallingWorkSource();
+ int32_t previousWorkSource = (int32_t)token;
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+ EXPECT_EQ(-1, reply.readInt32());
+ EXPECT_EQ(100, previousWorkSource);
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceRestored)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ int64_t token = IPCThreadState::self()->clearCallingWorkSource();
+ IPCThreadState::self()->restoreCallingWorkSource(token);
+
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+ EXPECT_EQ(100, reply.readInt32());
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, PropagateFlagSet)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->clearPropagateWorkSource();
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagCleared)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ IPCThreadState::self()->clearPropagateWorkSource();
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagRestored)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ int token = IPCThreadState::self()->setCallingWorkSourceUid(100);
+ IPCThreadState::self()->restoreCallingWorkSource(token);
+
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls)
+{
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+
+ Parcel data, reply;
+ status_t ret;
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+ Parcel data2, reply2;
+ status_t ret2;
+ data2.writeInterfaceToken(binderLibTestServiceName);
+ ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2);
+ EXPECT_EQ(100, reply2.readInt32());
+ EXPECT_EQ(NO_ERROR, ret2);
+}
+
+TEST_F(BinderLibTest, VectorSent) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 };
+ data.writeUint64Vector(testValue);
+
+ status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+ std::vector<uint64_t> readValue;
+ ret = reply.readUint64Vector(&readValue);
+ EXPECT_EQ(readValue, testValue);
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -1236,6 +1374,19 @@
}
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: {
+ data.enforceInterface(binderLibTestServiceName);
+ reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_ECHO_VECTOR: {
+ std::vector<uint64_t> vector;
+ auto err = data.readUint64Vector(&vector);
+ if (err != NO_ERROR)
+ return err;
+ reply->writeUint64Vector(vector);
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 6a16e24..3b1db27 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -229,6 +229,7 @@
IncrementUint32,
IncrementInt64,
IncrementUint64,
+ IncrementFloat,
IncrementTwo,
Last,
};
@@ -259,6 +260,7 @@
virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0;
virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0;
virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0;
+ virtual status_t increment(float a, float* aPlusOne) const = 0;
// This tests that input/output parameter interleaving works correctly
virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b,
@@ -353,6 +355,11 @@
using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne);
}
+ status_t increment(float a, float* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const;
+ return callRemote<Signature>(Tag::IncrementFloat, a, aPlusOne);
+ }
status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
using Signature =
@@ -474,6 +481,11 @@
*aPlusOne = a + 1;
return NO_ERROR;
}
+ status_t increment(float a, float* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1.0f;
+ return NO_ERROR;
+ }
status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
*aPlusOne = a + 1;
@@ -555,6 +567,10 @@
using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
}
+ case ISafeInterfaceTest::Tag::IncrementFloat: {
+ using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
case ISafeInterfaceTest::Tag::IncrementTwo: {
using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t,
int32_t*) const;
@@ -804,6 +820,14 @@
ASSERT_EQ(a + 1, aPlusOne);
}
+TEST_F(SafeInterfaceTest, TestIncrementFloat) {
+ const float a = 1.0f;
+ float aPlusOne = 0.0f;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1.0f, aPlusOne);
+}
+
TEST_F(SafeInterfaceTest, TestIncrementTwo) {
const int32_t a = 1;
int32_t aPlusOne = 0;
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
new file mode 100644
index 0000000..28cb138
--- /dev/null
+++ b/libs/cputimeinstate/Android.bp
@@ -0,0 +1,30 @@
+cc_library {
+ name: "libtimeinstate",
+ srcs: ["cputimeinstate.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
+ "liblog",
+ "libnetdutils"
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+}
+
+cc_test {
+ name: "libtimeinstate_test",
+ srcs: ["testtimeinstate.cpp"],
+ shared_libs: [
+ "libtimeinstate",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+}
+
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
new file mode 100644
index 0000000..5fd4a95
--- /dev/null
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libtimeinstate"
+
+#include "cputimeinstate.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bpf/BpfMap.h>
+#include <libbpf.h>
+#include <log/log.h>
+
+#define BPF_FS_PATH "/sys/fs/bpf/"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace bpf {
+
+struct time_key_t {
+ uint32_t uid;
+ uint32_t freq;
+};
+
+struct val_t {
+ uint64_t ar[100];
+};
+
+static std::mutex gInitializedMutex;
+static bool gInitialized = false;
+static uint32_t gNPolicies = 0;
+static std::vector<std::vector<uint32_t>> gPolicyFreqs;
+static std::vector<std::vector<uint32_t>> gPolicyCpus;
+static std::set<uint32_t> gAllFreqs;
+static unique_fd gMapFd;
+
+static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) {
+ std::string data;
+
+ if (!android::base::ReadFileToString(path, &data)) return false;
+
+ auto strings = android::base::Split(data, " \n");
+ for (const auto &s : strings) {
+ if (s.empty()) continue;
+ uint32_t n;
+ if (!android::base::ParseUint(s, &n)) return false;
+ out->emplace_back(n);
+ }
+ return true;
+}
+
+static int isPolicyFile(const struct dirent *d) {
+ return android::base::StartsWith(d->d_name, "policy");
+}
+
+static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) {
+ uint32_t policyN1, policyN2;
+ if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 ||
+ sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1)
+ return 0;
+ return policyN1 - policyN2;
+}
+
+static bool initGlobals() {
+ std::lock_guard<std::mutex> guard(gInitializedMutex);
+ if (gInitialized) return true;
+
+ struct dirent **dirlist;
+ const char basepath[] = "/sys/devices/system/cpu/cpufreq";
+ int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles);
+ if (ret == -1) return false;
+ gNPolicies = ret;
+
+ std::vector<std::string> policyFileNames;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ policyFileNames.emplace_back(dirlist[i]->d_name);
+ free(dirlist[i]);
+ }
+ free(dirlist);
+
+ for (const auto &policy : policyFileNames) {
+ std::vector<uint32_t> freqs;
+ for (const auto &name : {"available", "boost"}) {
+ std::string path =
+ StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name);
+ if (!readNumbersFromFile(path, &freqs)) return false;
+ }
+ std::sort(freqs.begin(), freqs.end());
+ gPolicyFreqs.emplace_back(freqs);
+
+ for (auto freq : freqs) gAllFreqs.insert(freq);
+
+ std::vector<uint32_t> cpus;
+ std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus");
+ if (!readNumbersFromFile(path, &cpus)) return false;
+ gPolicyCpus.emplace_back(cpus);
+ }
+
+ gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")};
+ if (gMapFd < 0) return false;
+
+ gInitialized = true;
+ return true;
+}
+
+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 = bpf_obj_get(path.c_str());
+ if (prog_fd < 0) return false;
+ return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
+}
+
+// Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes.
+// Returns true on success, false otherwise.
+// Tracking is active only once a live process has successfully called this function; if the calling
+// process dies then it must be called again to resume tracking.
+// This function should *not* be called while tracking is already active; doing so is unnecessary
+// and can lead to accounting errors.
+bool startTrackingUidCpuFreqTimes() {
+ return attachTracepointProgram("sched", "sched_switch") &&
+ attachTracepointProgram("power", "cpu_frequency");
+}
+
+// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes.
+// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors
+// using the format:
+// [[t0_0, t0_1, ...],
+// [t1_0, t1_1, ...], ...]
+// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq.
+bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) {
+ if (!gInitialized && !initGlobals()) return false;
+ time_key_t key = {.uid = uid, .freq = 0};
+
+ freqTimes->clear();
+ freqTimes->resize(gNPolicies);
+ std::vector<uint32_t> idxs(gNPolicies, 0);
+
+ val_t value;
+ for (uint32_t freq : gAllFreqs) {
+ key.freq = freq;
+ int ret = findMapEntry(gMapFd, &key, &value);
+ if (ret) {
+ if (errno == ENOENT)
+ memset(&value.ar, 0, sizeof(value.ar));
+ else
+ return false;
+ }
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue;
+ uint64_t time = 0;
+ for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu];
+ idxs[i] += 1;
+ (*freqTimes)[i].emplace_back(time);
+ }
+ }
+
+ return true;
+}
+
+// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap.
+// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to
+// vectors of vectors using the format:
+// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+// uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
+bool getUidsCpuFreqTimes(
+ std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) {
+ if (!gInitialized && !initGlobals()) return false;
+
+ int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times");
+ if (fd < 0) return false;
+ BpfMap<time_key_t, val_t> m(fd);
+
+ std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ std::unordered_map<uint32_t, uint32_t> freqIdxs;
+ for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j;
+ policyFreqIdxs.emplace_back(freqIdxs);
+ }
+
+ auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val,
+ const BpfMap<time_key_t, val_t> &) {
+ if (freqTimeMap->find(key.uid) == freqTimeMap->end()) {
+ (*freqTimeMap)[key.uid].resize(gNPolicies);
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0);
+ }
+ }
+
+ for (size_t policy = 0; policy < gNPolicies; ++policy) {
+ for (const auto &cpu : gPolicyCpus[policy]) {
+ auto freqIdx = policyFreqIdxs[policy][key.freq];
+ (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu];
+ }
+ }
+ return android::netdutils::status::ok;
+ };
+ return isOk(m.iterateWithValue(fn));
+}
+
+// Clear all time in state data for a given uid. Returns false on error, true otherwise.
+bool clearUidCpuFreqTimes(uint32_t uid) {
+ if (!gInitialized && !initGlobals()) return false;
+ time_key_t key = {.uid = uid, .freq = 0};
+
+ std::vector<uint32_t> idxs(gNPolicies, 0);
+ for (auto freq : gAllFreqs) {
+ key.freq = freq;
+ if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false;
+ }
+ return true;
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/cputimeinstate/cputimeinstate.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/cputimeinstate/cputimeinstate.h
index e6ac6bf..9f6103e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -14,14 +14,18 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <unordered_map>
+#include <vector>
namespace android {
-namespace mock {
+namespace bpf {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+bool startTrackingUidCpuFreqTimes();
+bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes);
+bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap);
+bool clearUidCpuFreqTimes(unsigned int uid);
-} // namespace mock
+} // namespace bpf
} // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
new file mode 100644
index 0000000..9837865
--- /dev/null
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -0,0 +1,58 @@
+
+#include <unordered_map>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <cputimeinstate.h>
+
+namespace android {
+namespace bpf {
+
+using std::vector;
+
+TEST(TimeInStateTest, SingleUid) {
+ vector<vector<uint64_t>> times;
+ ASSERT_TRUE(getUidCpuFreqTimes(0, ×));
+ EXPECT_FALSE(times.empty());
+}
+
+TEST(TimeInStateTest, AllUid) {
+ vector<size_t> sizes;
+ std::unordered_map<uint32_t, vector<vector<uint64_t>>> map;
+ ASSERT_TRUE(getUidsCpuFreqTimes(&map));
+
+ ASSERT_FALSE(map.empty());
+
+ auto firstEntry = map.begin()->second;
+ for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
+
+ for (const auto &vec : map) {
+ ASSERT_EQ(vec.second.size(), sizes.size());
+ for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+ }
+}
+
+TEST(TimeInStateTest, RemoveUid) {
+ vector<vector<uint64_t>> times, times2;
+ ASSERT_TRUE(getUidCpuFreqTimes(0, ×));
+ ASSERT_FALSE(times.empty());
+
+ uint64_t sum = 0;
+ for (size_t i = 0; i < times.size(); ++i) {
+ for (auto x : times[i]) sum += x;
+ }
+ ASSERT_GT(sum, (uint64_t)0);
+
+ ASSERT_TRUE(clearUidCpuFreqTimes(0));
+
+ ASSERT_TRUE(getUidCpuFreqTimes(0, ×2));
+ ASSERT_EQ(times2.size(), times.size());
+ for (size_t i = 0; i < times.size(); ++i) {
+ ASSERT_EQ(times2[i].size(), times[i].size());
+ for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]);
+ }
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 35296a9..04884bb 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -46,6 +46,7 @@
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.drm@1.0::IDrmFactory",
+ "android.hardware.graphics.allocator@2.0::IAllocator",
"android.hardware.graphics.composer@2.1::IComposer",
"android.hardware.health@2.0::IHealth",
"android.hardware.media.omx@1.0::IOmx",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 4da30e9..0571dcc 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -16,13 +16,19 @@
name: "libgraphicsenv",
srcs: [
+ "GpuStatsInfo.cpp",
"GraphicsEnv.cpp",
+ "IGpuService.cpp"
],
cflags: ["-Wall", "-Werror"],
shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
"liblog",
+ "libutils",
],
export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
new file mode 100644
index 0000000..0fa0d9e
--- /dev/null
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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 <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <graphicsenv/GpuStatsInfo.h>
+
+namespace android {
+
+using base::StringAppendF;
+
+status_t GpuStatsGlobalInfo::writeToParcel(Parcel* parcel) const {
+ status_t status;
+ if ((status = parcel->writeUtf8AsUtf16(driverPackageName)) != OK) return status;
+ if ((status = parcel->writeUtf8AsUtf16(driverVersionName)) != OK) return status;
+ if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status;
+ if ((status = parcel->writeInt64(driverBuildTime)) != OK) return status;
+ if ((status = parcel->writeInt32(glLoadingCount)) != OK) return status;
+ if ((status = parcel->writeInt32(glLoadingFailureCount)) != OK) return status;
+ if ((status = parcel->writeInt32(vkLoadingCount)) != OK) return status;
+ if ((status = parcel->writeInt32(vkLoadingFailureCount)) != OK) return status;
+ return OK;
+}
+
+status_t GpuStatsGlobalInfo::readFromParcel(const Parcel* parcel) {
+ status_t status;
+ if ((status = parcel->readUtf8FromUtf16(&driverPackageName)) != OK) return status;
+ if ((status = parcel->readUtf8FromUtf16(&driverVersionName)) != OK) return status;
+ if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status;
+ if ((status = parcel->readInt64(&driverBuildTime)) != OK) return status;
+ if ((status = parcel->readInt32(&glLoadingCount)) != OK) return status;
+ if ((status = parcel->readInt32(&glLoadingFailureCount)) != OK) return status;
+ if ((status = parcel->readInt32(&vkLoadingCount)) != OK) return status;
+ if ((status = parcel->readInt32(&vkLoadingFailureCount)) != OK) return status;
+ return OK;
+}
+
+std::string GpuStatsGlobalInfo::toString() const {
+ std::string result;
+ StringAppendF(&result, "driverPackageName = %s\n", driverPackageName.c_str());
+ StringAppendF(&result, "driverVersionName = %s\n", driverVersionName.c_str());
+ StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode);
+ StringAppendF(&result, "driverBuildTime = %" PRId64 "\n", driverBuildTime);
+ StringAppendF(&result, "glLoadingCount = %d\n", glLoadingCount);
+ StringAppendF(&result, "glLoadingFailureCount = %d\n", glLoadingFailureCount);
+ StringAppendF(&result, "vkLoadingCount = %d\n", vkLoadingCount);
+ StringAppendF(&result, "vkLoadingFailureCount = %d\n", vkLoadingFailureCount);
+ return result;
+}
+
+status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const {
+ status_t status;
+ if ((status = parcel->writeUtf8AsUtf16(appPackageName)) != OK) return status;
+ if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status;
+ if ((status = parcel->writeInt64Vector(glDriverLoadingTime)) != OK) return status;
+ if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status;
+ return OK;
+}
+
+status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) {
+ status_t status;
+ if ((status = parcel->readUtf8FromUtf16(&appPackageName)) != OK) return status;
+ if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status;
+ if ((status = parcel->readInt64Vector(&glDriverLoadingTime)) != OK) return status;
+ if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status;
+ return OK;
+}
+
+std::string GpuStatsAppInfo::toString() const {
+ std::string result;
+ StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str());
+ StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode);
+ result.append("glDriverLoadingTime:");
+ for (int32_t loadingTime : glDriverLoadingTime) {
+ StringAppendF(&result, " %d", loadingTime);
+ }
+ result.append("\n");
+ result.append("vkDriverLoadingTime:");
+ for (int32_t loadingTime : vkDriverLoadingTime) {
+ StringAppendF(&result, " %d", loadingTime);
+ }
+ result.append("\n");
+ return result;
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index a8ef7a0..13c0d87 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -14,46 +14,456 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
//#define LOG_NDEBUG 1
#define LOG_TAG "GraphicsEnv"
+
#include <graphicsenv/GraphicsEnv.h>
-#include <mutex>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#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 <sys/prctl.h>
+#include <utils/Trace.h>
+
+#include <memory>
+#include <string>
// TODO(b/37049319) Get this from a header once one exists
extern "C" {
- android_namespace_t* android_get_exported_namespace(const char*);
- android_namespace_t* android_create_namespace(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- android_namespace_t* parent);
+android_namespace_t* android_get_exported_namespace(const char*);
+android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+ const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path,
+ android_namespace_t* parent);
+bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to,
+ const char* shared_libs_sonames);
- enum {
- ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
- ANDROID_NAMESPACE_TYPE_SHARED = 2,
- };
+enum {
+ ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+ ANDROID_NAMESPACE_TYPE_SHARED = 2,
+};
}
+// TODO(ianelliott@): Get the following from an ANGLE header:
+#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
+// Version-2 API:
+typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse);
+typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle,
+ int* rulesVersion);
+typedef bool (*fpANGLEGetSystemInfo)(void** handle);
+typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel,
+ void* handle);
+typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion,
+ void* systemInfoHandle, const char* appName);
+typedef bool (*fpANGLEFreeRulesHandle)(void* handle);
+typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle);
+
namespace android {
+enum NativeLibrary {
+ LLNDK = 0,
+ VNDKSP = 1,
+};
+
+static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt",
+ "/etc/vndksp.libraries.txt"};
+
+static std::string vndkVersionStr() {
+#ifdef __BIONIC__
+ std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+#endif
+ return "";
+}
+
+static void insertVndkVersionStr(std::string* fileName) {
+ LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr");
+ size_t insertPos = fileName->find_last_of(".");
+ if (insertPos == std::string::npos) {
+ insertPos = fileName->length();
+ }
+ fileName->insert(insertPos, vndkVersionStr());
+}
+
+static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) {
+ // Read list of public native libraries from the config file.
+ std::string fileContent;
+ if (!base::ReadFileToString(configFile, &fileContent)) {
+ return false;
+ }
+
+ std::vector<std::string> lines = base::Split(fileContent, "\n");
+
+ for (auto& line : lines) {
+ auto trimmedLine = base::Trim(line);
+ if (!trimmedLine.empty()) {
+ soNames->push_back(trimmedLine);
+ }
+ }
+
+ return true;
+}
+
+static const std::string getSystemNativeLibraries(NativeLibrary type) {
+ static const char* androidRootEnv = getenv("ANDROID_ROOT");
+ static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system";
+
+ std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type];
+
+ insertVndkVersionStr(&nativeLibrariesSystemConfig);
+
+ std::vector<std::string> soNames;
+ if (!readConfig(nativeLibrariesSystemConfig, &soNames)) {
+ ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str());
+ return "";
+ }
+
+ return base::Join(soNames, ':');
+}
+
/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
static GraphicsEnv env;
return env;
}
-void GraphicsEnv::setDriverPath(const std::string path) {
- if (!mDriverPath.empty()) {
- ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
- mDriverPath.c_str(), path.c_str());
+int GraphicsEnv::getCanLoadSystemLibraries() {
+ if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ // Return an integer value since this crosses library boundaries
+ return 1;
+ }
+ return 0;
+}
+
+void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
+ const std::string sphalLibraries) {
+ if (!mDriverPath.empty() || !mSphalLibraries.empty()) {
+ ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries "
+ "from '%s' to '%s'",
+ mDriverPath.c_str(), path.c_str(), mSphalLibraries.c_str(), sphalLibraries.c_str());
return;
}
- ALOGV("setting driver path to '%s'", path.c_str());
+ ALOGV("setting driver path to '%s' and sphal libraries to '%s'", path.c_str(),
+ sphalLibraries.c_str());
mDriverPath = path;
+ mSphalLibraries = sphalLibraries;
+}
+
+void GraphicsEnv::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, uint64_t driverVersionCode,
+ int64_t driverBuildTime, const std::string& appPackageName) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ ALOGV("setGpuStats:\n"
+ "\tdriverPackageName[%s]\n"
+ "\tdriverVersionName[%s]\n"
+ "\tdriverVersionCode[%" PRIu64 "]\n"
+ "\tdriverBuildTime[%" PRId64 "]\n"
+ "\tappPackageName[%s]\n",
+ driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime,
+ appPackageName.c_str());
+
+ mGpuStats.driverPackageName = driverPackageName;
+ mGpuStats.driverVersionName = driverVersionName;
+ mGpuStats.driverVersionCode = driverVersionCode;
+ mGpuStats.driverBuildTime = driverBuildTime;
+ mGpuStats.appPackageName = appPackageName;
+}
+
+void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ switch (driver) {
+ case GraphicsEnv::Driver::GL:
+ case GraphicsEnv::Driver::GL_UPDATED:
+ case GraphicsEnv::Driver::ANGLE: {
+ if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE) {
+ mGpuStats.glDriverToLoad = driver;
+ break;
+ }
+
+ if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) {
+ mGpuStats.glDriverFallback = driver;
+ }
+ break;
+ }
+ case Driver::VULKAN:
+ case Driver::VULKAN_UPDATED: {
+ if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE) {
+ mGpuStats.vkDriverToLoad = driver;
+ break;
+ }
+
+ if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) {
+ mGpuStats.vkDriverFallback = driver;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isLoaded, int64_t driverLoadingTime) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE;
+ bool isIntendedDriverLoaded = false;
+ if (api == GraphicsEnv::Api::API_GL) {
+ driver = mGpuStats.glDriverToLoad;
+ isIntendedDriverLoaded = isLoaded &&
+ ((mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) ||
+ (mGpuStats.glDriverToLoad == mGpuStats.glDriverFallback));
+ } else {
+ driver = mGpuStats.vkDriverToLoad;
+ isIntendedDriverLoaded =
+ isLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE);
+ }
+
+ sendGpuStatsLocked(driver, isIntendedDriverLoaded, driverLoadingTime);
+}
+
+void GraphicsEnv::clearDriverLoadingInfo(GraphicsEnv::Api api) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ if (api == GraphicsEnv::Api::API_GL) {
+ mGpuStats.glDriverToLoad = GraphicsEnv::Driver::NONE;
+ mGpuStats.glDriverFallback = GraphicsEnv::Driver::NONE;
+ }
+}
+
+static sp<IGpuService> getGpuService() {
+ const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
+ if (!binder) {
+ ALOGE("Failed to get gpu service");
+ return nullptr;
+ }
+
+ return interface_cast<IGpuService>(binder);
+}
+
+void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) {
+ ATRACE_CALL();
+
+ // Do not sendGpuStats for those skipping the GraphicsEnvironment setup
+ if (mGpuStats.appPackageName.empty()) return;
+
+ ALOGV("sendGpuStats:\n"
+ "\tdriverPackageName[%s]\n"
+ "\tdriverVersionName[%s]\n"
+ "\tdriverVersionCode[%" PRIu64 "]\n"
+ "\tdriverBuildTime[%" PRId64 "]\n"
+ "\tappPackageName[%s]\n"
+ "\tdriver[%d]\n"
+ "\tisDriverLoaded[%d]\n"
+ "\tdriverLoadingTime[%" PRId64 "]",
+ mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(),
+ mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(),
+ static_cast<int32_t>(driver), isDriverLoaded, driverLoadingTime);
+
+ const sp<IGpuService> gpuService = getGpuService();
+ if (gpuService) {
+ gpuService->setGpuStats(mGpuStats.driverPackageName, mGpuStats.driverVersionName,
+ mGpuStats.driverVersionCode, mGpuStats.driverBuildTime,
+ mGpuStats.appPackageName, driver, isDriverLoaded,
+ driverLoadingTime);
+ }
+}
+
+void* GraphicsEnv::loadLibrary(std::string name) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = getAngleNamespace(),
+ };
+
+ std::string libName = std::string("lib") + name + "_angle.so";
+
+ void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+
+ if (so) {
+ ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so);
+ return so;
+ } else {
+ ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror());
+ }
+
+ return nullptr;
+}
+
+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 ANGLEGetFeatureSupportUtilAPIVersion =
+ (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
+ "ANGLEGetFeatureSupportUtilAPIVersion");
+
+ if (!ANGLEGetFeatureSupportUtilAPIVersion) {
+ ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
+ return false;
+ }
+
+ // Negotiate the interface version by requesting most recent known to the platform
+ unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
+ if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
+ ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
+ "requested version %u",
+ versionToUse);
+ return false;
+ }
+
+ // Add and remove versions below as needed
+ bool useAngle = false;
+ switch (versionToUse) {
+ case 2: {
+ ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
+ void* rulesHandle = nullptr;
+ int rulesVersion = 0;
+ void* systemInfoHandle = nullptr;
+
+ // Get the symbols for the feature-support-utility library:
+#define GET_SYMBOL(symbol) \
+ fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \
+ if (!symbol) { \
+ ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
+ break; \
+ }
+ GET_SYMBOL(ANGLEAndroidParseRulesString);
+ GET_SYMBOL(ANGLEGetSystemInfo);
+ GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
+ GET_SYMBOL(ANGLEShouldBeUsedForApplication);
+ GET_SYMBOL(ANGLEFreeRulesHandle);
+ GET_SYMBOL(ANGLEFreeSystemInfoHandle);
+
+ // Parse the rules, obtain the SystemInfo, and evaluate the
+ // application against the rules:
+ if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
+ ALOGW("ANGLE feature-support library cannot parse rules file");
+ break;
+ }
+ if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
+ ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
+ break;
+ }
+ if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+ ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
+ break;
+ }
+ useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
+ systemInfoHandle, mAngleAppName.c_str());
+ (ANGLEFreeRulesHandle)(rulesHandle);
+ (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
+ } break;
+
+ default:
+ ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
+ }
+
+ ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
+ return useAngle;
+}
+
+bool GraphicsEnv::shouldUseAngle(std::string appName) {
+ if (appName != mAngleAppName) {
+ // Make sure we are checking the app we were init'ed for
+ ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(),
+ appName.c_str());
+ return false;
+ }
+
+ return shouldUseAngle();
+}
+
+bool GraphicsEnv::shouldUseAngle() {
+ // Make sure we are init'ed
+ if (mAngleAppName.empty()) {
+ ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
+ return false;
+ }
+
+ return (mUseAngle == YES) ? true : false;
+}
+
+void GraphicsEnv::updateUseAngle() {
+ mUseAngle = NO;
+
+ const char* ANGLE_PREFER_ANGLE = "angle";
+ const char* ANGLE_PREFER_NATIVE = "native";
+
+ if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
+ ALOGV("User set \"Developer Options\" to force the use of ANGLE");
+ mUseAngle = YES;
+ } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
+ ALOGV("User set \"Developer Options\" to force the use of Native");
+ mUseAngle = NO;
+ } else {
+ // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily
+ // load ANGLE and call the updatable opt-in/out logic:
+ void* featureSo = loadLibrary("feature_support");
+ if (featureSo) {
+ ALOGV("loaded ANGLE's opt-in/out logic from namespace");
+ mUseAngle = checkAngleRules(featureSo) ? YES : NO;
+ dlclose(featureSo);
+ featureSo = nullptr;
+ } else {
+ ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
+ }
+ }
+}
+
+void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
+ const std::string developerOptIn, const int rulesFd,
+ const long rulesOffset, const long rulesLength) {
+ if (mUseAngle != UNKNOWN) {
+ // We've already figured out an answer for this app, so just return.
+ ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
+ (mUseAngle == YES) ? "true" : "false");
+ return;
+ }
+
+ ALOGV("setting ANGLE path to '%s'", path.c_str());
+ mAnglePath = path;
+ ALOGV("setting ANGLE app name to '%s'", appName.c_str());
+ mAngleAppName = appName;
+ ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
+ mAngleDeveloperOptIn = developerOptIn;
+
+ lseek(rulesFd, rulesOffset, SEEK_SET);
+ mRulesBuffer = std::vector<char>(rulesLength + 1);
+ ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
+ if (numBytesRead < 0) {
+ ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
+ numBytesRead = 0;
+ } else if (numBytesRead == 0) {
+ ALOGW("Empty rules file");
+ }
+ if (numBytesRead != rulesLength) {
+ ALOGW("Did not read all of the necessary bytes from the rules file."
+ "expected: %ld, got: %zd",
+ rulesLength, numBytesRead);
+ }
+ mRulesBuffer[numBytesRead] = '\0';
+
+ // Update the current status of whether we should use ANGLE or not
+ updateUseAngle();
}
void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
@@ -62,7 +472,7 @@
mAppNamespace = appNamespace;
} else {
ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
- layerPaths.c_str(), appNamespace);
+ layerPaths.c_str(), appNamespace);
}
}
@@ -70,40 +480,111 @@
return mAppNamespace;
}
-const std::string GraphicsEnv::getLayerPaths(){
+std::string& GraphicsEnv::getAngleAppName() {
+ return mAngleAppName;
+}
+
+const std::string& GraphicsEnv::getLayerPaths() {
return mLayerPaths;
}
-const std::string GraphicsEnv::getDebugLayers() {
+const std::string& GraphicsEnv::getDebugLayers() {
return mDebugLayers;
}
+const std::string& GraphicsEnv::getDebugLayersGLES() {
+ return mDebugLayersGLES;
+}
+
void GraphicsEnv::setDebugLayers(const std::string layers) {
mDebugLayers = layers;
}
+void GraphicsEnv::setDebugLayersGLES(const std::string layers) {
+ mDebugLayersGLES = layers;
+}
+
android_namespace_t* GraphicsEnv::getDriverNamespace() {
static std::once_flag once;
std::call_once(once, [this]() {
- if (mDriverPath.empty())
- return;
- // If the sphal namespace isn't configured for a device, don't support updatable drivers.
- // We need a parent namespace to inherit the default search path from.
- auto sphalNamespace = android_get_exported_namespace("sphal");
- if (!sphalNamespace) return;
+ if (mDriverPath.empty()) return;
+
+ auto vndkNamespace = android_get_exported_namespace("vndk");
+ if (!vndkNamespace) return;
+
mDriverNamespace = android_create_namespace("gfx driver",
- nullptr, // ld_library_path
+ mDriverPath.c_str(), // ld_library_path
mDriverPath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
nullptr, // permitted_when_isolated_path
- sphalNamespace);
+ nullptr);
+
+ const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
+ if (llndkLibraries.empty()) {
+ mDriverNamespace = nullptr;
+ return;
+ }
+ if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) {
+ ALOGE("Failed to link default namespace[%s]", dlerror());
+ mDriverNamespace = nullptr;
+ return;
+ }
+
+ const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
+ if (vndkspLibraries.empty()) {
+ mDriverNamespace = nullptr;
+ return;
+ }
+ if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) {
+ ALOGE("Failed to link vndk namespace[%s]", dlerror());
+ mDriverNamespace = nullptr;
+ return;
+ }
+
+ if (mSphalLibraries.empty()) return;
+
+ // Make additional libraries in sphal to be accessible
+ auto sphalNamespace = android_get_exported_namespace("sphal");
+ if (!sphalNamespace) {
+ ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace",
+ mSphalLibraries.c_str());
+ mDriverNamespace = nullptr;
+ return;
+ }
+
+ if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) {
+ ALOGE("Failed to link sphal namespace[%s]", dlerror());
+ mDriverNamespace = nullptr;
+ return;
+ }
});
+
return mDriverNamespace;
}
-} // namespace android
+android_namespace_t* GraphicsEnv::getAngleNamespace() {
+ std::lock_guard<std::mutex> lock(mNamespaceMutex);
-extern "C" android_namespace_t* android_getDriverNamespace() {
- return android::GraphicsEnv::getInstance().getDriverNamespace();
+ if (mAngleNamespace) {
+ return mAngleNamespace;
+ }
+
+ if (mAnglePath.empty()) {
+ ALOGV("mAnglePath is empty, not creating ANGLE namespace");
+ return nullptr;
+ }
+
+ mAngleNamespace = android_create_namespace("ANGLE",
+ nullptr, // ld_library_path
+ mAnglePath.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_SHARED |
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr, // permitted_when_isolated_path
+ nullptr);
+
+ ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default");
+
+ return mAngleNamespace;
}
+
+} // namespace android
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
new file mode 100644
index 0000000..1dc1c0e
--- /dev/null
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "GpuService"
+
+#include <graphicsenv/IGpuService.h>
+
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class BpGpuService : public BpInterface<IGpuService> {
+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,
+ GraphicsEnv::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ data.writeUtf8AsUtf16(driverPackageName);
+ data.writeUtf8AsUtf16(driverVersionName);
+ data.writeUint64(driverVersionCode);
+ data.writeInt64(driverBuildTime);
+ data.writeUtf8AsUtf16(appPackageName);
+ data.writeInt32(static_cast<int32_t>(driver));
+ data.writeBool(isDriverLoaded);
+ data.writeInt64(driverLoadingTime);
+
+ remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
+ if (!outStats) return UNEXPECTED_NULL;
+
+ Parcel data, reply;
+ status_t status;
+
+ if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK)
+ return status;
+
+ if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) !=
+ OK)
+ return status;
+
+ int32_t result = 0;
+ if ((status = reply.readInt32(&result)) != OK) return status;
+ if (result != OK) return result;
+
+ outStats->clear();
+ return reply.readParcelableVector(outStats);
+ }
+
+ virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
+ if (!outStats) return UNEXPECTED_NULL;
+
+ Parcel data, reply;
+ status_t status;
+
+ if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) {
+ return status;
+ }
+
+ if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) !=
+ OK) {
+ return status;
+ }
+
+ int32_t result = 0;
+ if ((status = reply.readInt32(&result)) != OK) return status;
+ if (result != OK) return result;
+
+ outStats->clear();
+ return reply.readParcelableVector(outStats);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
+
+status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ ALOGV("onTransact code[0x%X]", code);
+
+ status_t status;
+ switch (code) {
+ case SET_GPU_STATS: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPackageName;
+ if ((status = data.readUtf8FromUtf16(&driverPackageName)) != OK) return status;
+
+ std::string driverVersionName;
+ if ((status = data.readUtf8FromUtf16(&driverVersionName)) != OK) return status;
+
+ uint64_t driverVersionCode;
+ if ((status = data.readUint64(&driverVersionCode)) != OK) return status;
+
+ int64_t driverBuildTime;
+ if ((status = data.readInt64(&driverBuildTime)) != OK) return status;
+
+ std::string appPackageName;
+ if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status;
+
+ int32_t driver;
+ if ((status = data.readInt32(&driver)) != OK) return status;
+
+ bool isDriverLoaded;
+ if ((status = data.readBool(&isDriverLoaded)) != OK) return status;
+
+ int64_t driverLoadingTime;
+ if ((status = data.readInt64(&driverLoadingTime)) != OK) return status;
+
+ setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
+ appPackageName, static_cast<GraphicsEnv::Driver>(driver), isDriverLoaded,
+ driverLoadingTime);
+
+ return OK;
+ }
+ case GET_GPU_STATS_GLOBAL_INFO: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::vector<GpuStatsGlobalInfo> stats;
+ const status_t result = getGpuStatsGlobalInfo(&stats);
+
+ if ((status = reply->writeInt32(result)) != OK) return status;
+ if (result != OK) return result;
+
+ if ((status = reply->writeParcelableVector(stats)) != OK) return status;
+
+ return OK;
+ }
+ case GET_GPU_STATS_APP_INFO: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::vector<GpuStatsAppInfo> stats;
+ const status_t result = getGpuStatsAppInfo(&stats);
+
+ if ((status = reply->writeInt32(result)) != OK) return status;
+ if (result != OK) return result;
+
+ if ((status = reply->writeParcelableVector(stats)) != OK) return status;
+
+ return OK;
+ }
+ case SHELL_COMMAND_TRANSACTION: {
+ int in = data.readFileDescriptor();
+ int out = data.readFileDescriptor();
+ int err = data.readFileDescriptor();
+
+ std::vector<String16> args;
+ data.readString16Vector(&args);
+
+ sp<IBinder> unusedCallback;
+ if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK) return status;
+
+ sp<IResultReceiver> resultReceiver;
+ if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK) return status;
+
+ status = shellCommand(in, out, err, args);
+ if (resultReceiver != nullptr) resultReceiver->send(status);
+
+ return OK;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
new file mode 100644
index 0000000..a92ca70
--- /dev/null
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 <string>
+#include <vector>
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+/*
+ * class for transporting gpu global stats from GpuService to authorized
+ * recipents. This class is intended to be a data container.
+ */
+class GpuStatsGlobalInfo : public Parcelable {
+public:
+ GpuStatsGlobalInfo() = default;
+ GpuStatsGlobalInfo(const GpuStatsGlobalInfo&) = default;
+ virtual ~GpuStatsGlobalInfo() = default;
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
+ std::string toString() const;
+
+ std::string driverPackageName = "";
+ std::string driverVersionName = "";
+ uint64_t driverVersionCode = 0;
+ int64_t driverBuildTime = 0;
+ int32_t glLoadingCount = 0;
+ int32_t glLoadingFailureCount = 0;
+ int32_t vkLoadingCount = 0;
+ int32_t vkLoadingFailureCount = 0;
+};
+
+/*
+ * class for transporting gpu app stats from GpuService to authorized recipents.
+ * This class is intended to be a data container.
+ */
+class GpuStatsAppInfo : public Parcelable {
+public:
+ GpuStatsAppInfo() = default;
+ GpuStatsAppInfo(const GpuStatsAppInfo&) = default;
+ virtual ~GpuStatsAppInfo() = default;
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
+ std::string toString() const;
+
+ std::string appPackageName = "";
+ uint64_t driverVersionCode = 0;
+ std::vector<int64_t> glDriverLoadingTime = {};
+ std::vector<int64_t> vkDriverLoadingTime = {};
+};
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 784e2c8..cb4239f 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_UI_GRAPHICS_ENV_H
#define ANDROID_UI_GRAPHICS_ENV_H 1
+#include <mutex>
#include <string>
+#include <vector>
struct android_namespace_t;
@@ -27,45 +29,115 @@
class GraphicsEnv {
public:
+ enum Api {
+ API_GL = 0,
+ API_VK = 1,
+ };
+
+ enum Driver {
+ NONE = 0,
+ GL = 1,
+ GL_UPDATED = 2,
+ VULKAN = 3,
+ VULKAN_UPDATED = 4,
+ ANGLE = 5,
+ };
+
+private:
+ struct GpuStats {
+ std::string driverPackageName;
+ std::string driverVersionName;
+ uint64_t driverVersionCode;
+ int64_t driverBuildTime;
+ std::string appPackageName;
+ Driver glDriverToLoad;
+ Driver glDriverFallback;
+ Driver vkDriverToLoad;
+ Driver vkDriverFallback;
+
+ GpuStats()
+ : driverPackageName(""),
+ driverVersionName(""),
+ driverVersionCode(0),
+ driverBuildTime(0),
+ appPackageName(""),
+ glDriverToLoad(Driver::NONE),
+ glDriverFallback(Driver::NONE),
+ vkDriverToLoad(Driver::NONE),
+ vkDriverFallback(Driver::NONE) {}
+ };
+
+public:
static GraphicsEnv& getInstance();
+ int getCanLoadSystemLibraries();
+
// Set a search path for loading graphics drivers. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
// (drivers must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /data/app/com.example.driver/base.apk!/lib/arm64-v8a
- void setDriverPath(const std::string path);
+ // Also set additional required sphal libraries to the linker for loading
+ // graphics drivers. The string is a list of libraries separated by ':',
+ // which is required by android_link_namespaces.
+ void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
android_namespace_t* getDriverNamespace();
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t versionCode, int64_t driverBuildTime,
+ const std::string& appPackageName);
+ void setDriverToLoad(Driver driver);
+ void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+ void clearDriverLoadingInfo(Api api);
+ void sendGpuStatsLocked(Driver driver, bool isDriverLoaded, int64_t driverLoadingTime);
+
+ bool shouldUseAngle(std::string appName);
+ bool shouldUseAngle();
+ // Set a search path for loading ANGLE libraries. The path is a list of
+ // directories separated by ':'. A directory can be contained in a zip file
+ // (libraries must be stored uncompressed and page aligned); such elements
+ // in the search path must have a '!' after the zip filename, e.g.
+ // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
+ void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
+ const int rulesFd, const long rulesOffset, const long rulesLength);
+ android_namespace_t* getAngleNamespace();
+ std::string& getAngleAppName();
void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
NativeLoaderNamespace* getAppNamespace();
- const std::string getLayerPaths();
+
+ const std::string& getLayerPaths();
void setDebugLayers(const std::string layers);
- const std::string getDebugLayers();
+ void setDebugLayersGLES(const std::string layers);
+ const std::string& getDebugLayers();
+ const std::string& getDebugLayersGLES();
private:
+ enum UseAngle { UNKNOWN, YES, NO };
+
+ void* loadLibrary(std::string name);
+ bool checkAngleRules(void* so);
+ void updateUseAngle();
+
GraphicsEnv() = default;
std::string mDriverPath;
+ std::string mSphalLibraries;
+ std::mutex mStatsLock;
+ GpuStats mGpuStats;
+ std::string mAnglePath;
+ std::string mAngleAppName;
+ std::string mAngleDeveloperOptIn;
+ std::vector<char> mRulesBuffer;
+ UseAngle mUseAngle = UNKNOWN;
std::string mDebugLayers;
+ std::string mDebugLayersGLES;
std::string mLayerPaths;
+ std::mutex mNamespaceMutex;
android_namespace_t* mDriverNamespace = nullptr;
+ android_namespace_t* mAngleNamespace = nullptr;
NativeLoaderNamespace* mAppNamespace = nullptr;
};
} // namespace android
-/* FIXME
- * Export an un-mangled function that just does
- * return android::GraphicsEnv::getInstance().getDriverNamespace();
- * This allows libEGL to get the function pointer via dlsym, since it can't
- * directly link against libgui. In a future release, we'll fix this so that
- * libgui does not depend on graphics API libraries, and libEGL can link
- * against it. The current dependencies from libgui -> libEGL are:
- * - the GLConsumer class, which should be moved to its own library
- * - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
- * will be removed soon.
- */
-extern "C" android_namespace_t* android_getDriverNamespace();
-
#endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
new file mode 100644
index 0000000..ac022b5
--- /dev/null
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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 <vector>
+
+#include <binder/IInterface.h>
+#include <cutils/compiler.h>
+#include <graphicsenv/GpuStatsInfo.h>
+#include <graphicsenv/GraphicsEnv.h>
+
+namespace android {
+
+/*
+ * This class defines the Binder IPC interface for GPU-related queries and
+ * control.
+ */
+class IGpuService : public IInterface {
+public:
+ DECLARE_META_INTERFACE(GpuService)
+
+ // set GPU stats from GraphicsEnvironment.
+ virtual void setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, uint64_t driverVersionCode,
+ int64_t driverBuildTime, const std::string& appPackageName,
+ GraphicsEnv::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) = 0;
+
+ // get GPU global stats from GpuStats module.
+ virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0;
+
+ // get GPU app stats from GpuStats module.
+ virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const = 0;
+};
+
+class BnGpuService : public BnInterface<IGpuService> {
+public:
+ enum IGpuServiceTag {
+ SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
+ GET_GPU_STATS_GLOBAL_INFO,
+ GET_GPU_STATS_APP_INFO,
+ // Always append new enum to the end.
+ };
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+
+protected:
+ virtual status_t shellCommand(int in, int out, int err, std::vector<String16>& args) = 0;
+};
+
+} // namespace android
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b29c1d5..4c2e653 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -31,44 +31,7 @@
"-Werror",
],
cppflags: [
- "-Weverything",
-
- // The static constructors and destructors in this library have not been noted to
- // introduce significant overheads
- "-Wno-exit-time-destructors",
- "-Wno-global-constructors",
-
- // We only care about compiling as C++14
- "-Wno-c++98-compat-pedantic",
-
- // We don't need to enumerate every case in a switch as long as a default case
- // is present
- "-Wno-switch-enum",
-
- // Allow calling variadic macros without a __VA_ARGS__ list
- "-Wno-gnu-zero-variadic-macro-arguments",
-
- // Don't warn about struct padding
- "-Wno-padded",
-
- // We are aware of the risks inherent in comparing floats for equality
- "-Wno-float-equal",
-
- // Pure abstract classes trigger this warning
- "-Wno-weak-vtables",
-
- // Allow four-character integer literals
- "-Wno-four-char-constants",
-
- // Allow documentation warnings
- "-Wno-documentation",
-
- // Allow implicit instantiation for templated class function
- "-Wno-undefined-func-template",
-
- // Allow explicitly marking struct as packed even when unnecessary
- "-Wno-packed",
-
+ "-Wextra",
"-DDEBUG_ONLY_CODE=0",
],
@@ -91,6 +54,7 @@
"BufferQueueConsumer.cpp",
"BufferQueueCore.cpp",
"BufferQueueProducer.cpp",
+ "BufferQueueThreadState.cpp",
"BufferSlot.cpp",
"ConsumerBase.cpp",
"CpuConsumer.cpp",
@@ -104,9 +68,12 @@
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
+ "IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
+ "ITransactionCompletedListener.cpp",
"LayerDebugInfo.cpp",
+ "LayerMetadata.cpp",
"LayerState.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
@@ -116,40 +83,58 @@
"SyncFeatures.cpp",
"view/Surface.cpp",
"bufferqueue/1.0/B2HProducerListener.cpp",
- "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
+ "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
+ "bufferqueue/1.0/H2BProducerListener.cpp",
+ "bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
+ "bufferqueue/2.0/B2HProducerListener.cpp",
+ "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
+ "bufferqueue/2.0/H2BProducerListener.cpp",
+ "bufferqueue/2.0/types.cpp",
],
shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
"android.hardware.graphics.common@1.1",
- "libsync",
+ "android.hardware.graphics.common@1.2",
+ "android.hidl.token@1.0-utils",
+ "libbase",
"libbinder",
- "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
- "libpdx_default_transport",
+ "libbufferhub",
+ "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
"libcutils",
"libEGL",
"libGLESv2",
- "libui",
- "libutils",
- "libnativewindow",
- "liblog",
"libhidlbase",
"libhidltransport",
- "android.hidl.token@1.0-utils",
- "android.hardware.graphics.bufferqueue@1.0",
- "android.hardware.configstore@1.0",
- "android.hardware.configstore-utils",
+ "libhwbinder",
+ "libinput",
+ "liblog",
+ "libnativewindow",
+ "libpdx_default_transport",
+ "libsync",
+ "libui",
+ "libutils",
+ "libvndksupport",
],
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
- cflags: ["-DNO_BUFFERHUB"],
+ cflags: [
+ "-DNO_BUFFERHUB",
+ "-DNO_INPUT",
+ ],
exclude_srcs: [
"BufferHubConsumer.cpp",
"BufferHubProducer.cpp",
],
exclude_shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libbufferhub",
"libbufferhubqueue",
+ "libinput",
"libpdx_default_transport",
],
},
@@ -157,8 +142,8 @@
header_libs: [
"libdvr_headers",
- "libnativebase_headers",
"libgui_headers",
+ "libnativebase_headers",
"libpdx_headers",
],
@@ -167,9 +152,11 @@
"libEGL",
"libnativewindow",
"libui",
- "android.hidl.token@1.0-utils",
"android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
"android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
+ "android.hidl.token@1.0-utils",
],
export_header_lib_headers: [
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index ae5cca2..4be014f 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <log/log.h>
#include <system/window.h>
+#include <ui/BufferHubBuffer.h>
namespace android {
@@ -63,13 +64,13 @@
} else if (buffers_[slot].mGraphicBuffer != nullptr) {
ALOGE("requestBuffer: slot %d is not empty.", slot);
return BAD_VALUE;
- } else if (buffers_[slot].mBufferProducer == nullptr) {
+ } else if (buffers_[slot].mProducerBuffer == nullptr) {
ALOGE("requestBuffer: slot %d is not dequeued.", slot);
return BAD_VALUE;
}
- const auto& buffer_producer = buffers_[slot].mBufferProducer;
- sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
+ const auto& producer_buffer = buffers_[slot].mProducerBuffer;
+ sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
buffers_[slot].mGraphicBuffer = graphic_buffer;
buffers_[slot].mRequestBufferCalled = true;
@@ -157,19 +158,19 @@
}
size_t slot = 0;
- std::shared_ptr<BufferProducer> buffer_producer;
+ std::shared_ptr<ProducerBuffer> producer_buffer;
for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
LocalHandle fence;
auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
if (!buffer_status) return NO_MEMORY;
- buffer_producer = buffer_status.take();
- if (!buffer_producer) return NO_MEMORY;
+ producer_buffer = buffer_status.take();
+ if (!producer_buffer) return NO_MEMORY;
- if (width == buffer_producer->width() && height == buffer_producer->height() &&
- uint32_t(format) == buffer_producer->format()) {
- // The producer queue returns a buffer producer matches the request.
+ if (width == producer_buffer->width() && height == producer_buffer->height() &&
+ uint32_t(format) == producer_buffer->format()) {
+ // The producer queue returns a producer buffer matches the request.
break;
}
@@ -178,8 +179,8 @@
ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
"from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
"re-allocattion.",
- width, height, format, slot, buffer_producer->width(), buffer_producer->height(),
- buffer_producer->format());
+ width, height, format, slot, producer_buffer->width(), producer_buffer->height(),
+ producer_buffer->format());
// Mark the slot as reallocating, so that later we can set
// BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
buffers_[slot].mIsReallocating = true;
@@ -224,23 +225,172 @@
return ret;
}
-status_t BufferHubProducer::detachBuffer(int /* slot */) {
- ALOGE("BufferHubProducer::detachBuffer not implemented.");
+status_t BufferHubProducer::detachBuffer(int slot) {
+ ALOGV("detachBuffer: slot=%d", slot);
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ return DetachBufferLocked(static_cast<size_t>(slot));
+}
+
+status_t BufferHubProducer::DetachBufferLocked(size_t slot) {
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("detachBuffer: BufferHubProducer is not connected.");
+ return NO_INIT;
+ }
+
+ if (slot >= static_cast<size_t>(max_buffer_count_)) {
+ ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_);
+ return BAD_VALUE;
+ } else if (!buffers_[slot].mBufferState.isDequeued()) {
+ ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot,
+ buffers_[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (!buffers_[slot].mRequestBufferCalled) {
+ ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot);
+ return BAD_VALUE;
+ }
+ std::shared_ptr<ProducerBuffer> producer_buffer = queue_->GetBuffer(slot);
+ if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) {
+ ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot);
+ return BAD_VALUE;
+ }
+ sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
+ if (graphic_buffer == nullptr) {
+ ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot);
+ return BAD_VALUE;
+ }
+
+ // Remove the ProducerBuffer from the ProducerQueue.
+ status_t error = RemoveBuffer(slot);
+ if (error != NO_ERROR) {
+ ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error);
+ return error;
+ }
+
+ // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject
+ // the handle into the GraphicBuffer object at the requested slot.
+ auto status_or_handle = producer_buffer->Detach();
+ if (!status_or_handle.ok()) {
+ ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot,
+ status_or_handle.error());
+ return BAD_VALUE;
+ }
+
+ // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
+ // be directly backed by BufferHub.
return INVALID_OPERATION;
}
-status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */,
- sp<Fence>* /* out_fence */) {
- ALOGE("BufferHubProducer::detachNextBuffer not implemented.");
- return INVALID_OPERATION;
+status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) {
+ ALOGV("detachNextBuffer.");
+
+ if (out_buffer == nullptr || out_fence == nullptr) {
+ ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer,
+ out_fence);
+ return BAD_VALUE;
+ }
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("detachNextBuffer: BufferHubProducer is not connected.");
+ return NO_INIT;
+ }
+
+ // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in
+ // sequence, except for two things:
+ //
+ // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the
+ // function just returns whatever ProducerBuffer is available from the ProducerQueue and no
+ // buffer allocation or re-allocation will happen.
+ // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return
+ // an error instead.
+ size_t slot = 0;
+ LocalHandle fence;
+
+ // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error
+ // immediately if ProducerQueue::Dequeue() fails.
+ auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence);
+ if (!status_or_buffer.ok()) {
+ ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error());
+ return NO_MEMORY;
+ }
+
+ std::shared_ptr<ProducerBuffer> producer_buffer = status_or_buffer.take();
+ if (producer_buffer == nullptr) {
+ ALOGE("detachNextBuffer: Dequeued buffer is null.");
+ return NO_MEMORY;
+ }
+
+ // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to
+ // be available for producer's use. It's either in free state (if the buffer has never been used
+ // before) or in queued state (if the buffer has been dequeued and queued back to
+ // BufferHubQueue).
+ if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) {
+ ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot,
+ buffers_[slot].mBufferState.string());
+ return BAD_VALUE;
+ }
+ if (buffers_[slot].mProducerBuffer == nullptr) {
+ ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot);
+ return BAD_VALUE;
+ }
+ if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) {
+ ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: "
+ "%d, expected: %d.",
+ slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id());
+ return BAD_VALUE;
+ }
+
+ ALOGV("detachNextBuffer: slot=%zu", slot);
+ buffers_[slot].mBufferState.freeQueued();
+ buffers_[slot].mBufferState.dequeue();
+
+ // Second, request the buffer.
+ sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
+ buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer();
+
+ // Finally, detach the buffer and then return.
+ status_t error = DetachBufferLocked(slot);
+ if (error == NO_ERROR) {
+ *out_fence = new Fence(fence.Release());
+ *out_buffer = graphic_buffer;
+ }
+ return error;
}
-status_t BufferHubProducer::attachBuffer(int* /* out_slot */,
- const sp<GraphicBuffer>& /* buffer */) {
- // With this BufferHub backed implementation, we assume (for now) all buffers
- // are allocated and owned by the BufferHub. Thus the attempt of transfering
- // ownership of a buffer to the buffer queue is intentionally unsupported.
- LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported.");
+status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) {
+ // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only
+ // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer.
+ ALOGV("queueBuffer: buffer=%p", buffer.get());
+
+ if (out_slot == nullptr) {
+ ALOGE("attachBuffer: out_slot cannot be NULL.");
+ return BAD_VALUE;
+ }
+ if (buffer == nullptr) {
+ ALOGE("attachBuffer: invalid GraphicBuffer.");
+ return BAD_VALUE;
+ }
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("attachBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ // Before attaching the buffer, caller is supposed to call
+ // IGraphicBufferProducer::setGenerationNumber to inform the
+ // BufferHubProducer the next generation number.
+ if (buffer->getGenerationNumber() != generation_number_) {
+ ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.",
+ buffer->getGenerationNumber(), generation_number_);
+ return BAD_VALUE;
+ }
+
+ // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
+ // be directly backed by BufferHub.
return INVALID_OPERATION;
}
@@ -302,11 +452,11 @@
return BAD_VALUE;
}
- // Post the buffer producer with timestamp in the metadata.
- const auto& buffer_producer = buffers_[slot].mBufferProducer;
+ // Post the producer buffer with timestamp in the metadata.
+ const auto& producer_buffer = buffers_[slot].mProducerBuffer;
// Check input crop is not out of boundary of current buffer.
- Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
+ Rect buffer_rect(producer_buffer->width(), producer_buffer->height());
Rect cropped_rect(Rect::EMPTY_RECT);
crop.intersect(buffer_rect, &cropped_rect);
if (cropped_rect != crop) {
@@ -327,11 +477,11 @@
meta_data.scaling_mode = int32_t(scaling_mode);
meta_data.transform = int32_t(transform);
- buffer_producer->PostAsync(&meta_data, fence_fd);
+ producer_buffer->PostAsync(&meta_data, fence_fd);
buffers_[slot].mBufferState.queue();
- output->width = buffer_producer->width();
- output->height = buffer_producer->height();
+ output->width = producer_buffer->width();
+ output->height = producer_buffer->height();
output->transformHint = 0; // default value, we don't use it yet.
// |numPendingBuffers| counts of the number of buffers that has been enqueued
@@ -369,8 +519,8 @@
return BAD_VALUE;
}
- auto buffer_producer = buffers_[slot].mBufferProducer;
- queue_->Enqueue(buffer_producer, size_t(slot), 0ULL);
+ auto producer_buffer = buffers_[slot].mProducerBuffer;
+ queue_->Enqueue(producer_buffer, size_t(slot), 0U);
buffers_[slot].mBufferState.cancel();
buffers_[slot].mFence = fence;
ALOGV("cancelBuffer: slot %d", slot);
@@ -641,12 +791,12 @@
}
size_t slot = status.get();
- auto buffer_producer = queue_->GetBuffer(slot);
+ auto producer_buffer = queue_->GetBuffer(slot);
- LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, "Failed to get buffer producer at slot: %zu",
- slot);
+ LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr,
+ "Failed to get the producer buffer at slot: %zu", slot);
- buffers_[slot].mBufferProducer = buffer_producer;
+ buffers_[slot].mProducerBuffer = producer_buffer;
return NO_ERROR;
}
@@ -654,26 +804,28 @@
status_t BufferHubProducer::RemoveBuffer(size_t slot) {
auto status = queue_->RemoveBuffer(slot);
if (!status) {
- ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s",
- status.GetErrorMessage().c_str());
+ ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.",
+ slot, status.GetErrorMessage().c_str());
return INVALID_OPERATION;
}
// Reset in memory objects related the the buffer.
- buffers_[slot].mBufferProducer = nullptr;
- buffers_[slot].mGraphicBuffer = nullptr;
+ buffers_[slot].mProducerBuffer = nullptr;
buffers_[slot].mBufferState.detachProducer();
+ buffers_[slot].mFence = Fence::NO_FENCE;
+ buffers_[slot].mGraphicBuffer = nullptr;
+ buffers_[slot].mRequestBufferCalled = false;
return NO_ERROR;
}
status_t BufferHubProducer::FreeAllBuffers() {
for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
// Reset in memory objects related the the buffer.
- buffers_[slot].mGraphicBuffer = nullptr;
+ buffers_[slot].mProducerBuffer = nullptr;
buffers_[slot].mBufferState.reset();
- buffers_[slot].mRequestBufferCalled = false;
- buffers_[slot].mBufferProducer = nullptr;
buffers_[slot].mFence = Fence::NO_FENCE;
+ buffers_[slot].mGraphicBuffer = nullptr;
+ buffers_[slot].mRequestBufferCalled = false;
}
auto status = queue_->FreeAllBuffers();
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index f50379b..5beba02 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -39,8 +39,8 @@
}
BufferItem::BufferItem() :
- mGraphicBuffer(NULL),
- mFence(NULL),
+ mGraphicBuffer(nullptr),
+ mFence(nullptr),
mCrop(Rect::INVALID_RECT),
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
@@ -91,11 +91,11 @@
size_t BufferItem::getFlattenedSize() const {
size_t size = sizeof(uint32_t); // Flags
- if (mGraphicBuffer != 0) {
+ if (mGraphicBuffer != nullptr) {
size += mGraphicBuffer->getFlattenedSize();
size = FlattenableUtils::align<4>(size);
}
- if (mFence != 0) {
+ if (mFence != nullptr) {
size += mFence->getFlattenedSize();
size = FlattenableUtils::align<4>(size);
}
@@ -107,10 +107,10 @@
size_t BufferItem::getFdCount() const {
size_t count = 0;
- if (mGraphicBuffer != 0) {
+ if (mGraphicBuffer != nullptr) {
count += mGraphicBuffer->getFdCount();
}
- if (mFence != 0) {
+ if (mFence != nullptr) {
count += mFence->getFdCount();
}
return count;
@@ -137,13 +137,13 @@
FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
flags = 0;
- if (mGraphicBuffer != 0) {
+ if (mGraphicBuffer != nullptr) {
status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
flags |= 1;
}
- if (mFence != 0) {
+ if (mFence != nullptr) {
status_t err = mFence->flatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 89bc0c4..f50bc20 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -107,7 +107,7 @@
void BufferItemConsumer::freeBufferLocked(int slotIndex) {
sp<BufferFreedListener> listener = mBufferFreedListener.promote();
- if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) {
+ if (listener != nullptr && mSlots[slotIndex].mGraphicBuffer != nullptr) {
// Fire callback if we have a listener registered and the buffer being freed is valid.
BI_LOGV("actually calling onBufferFreed");
listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer);
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index a8da134..5fb3f0b 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -38,7 +38,7 @@
void BufferQueue::ProxyConsumerListener::onDisconnect() {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onDisconnect();
}
}
@@ -46,7 +46,7 @@
void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onFrameAvailable(item);
}
}
@@ -54,21 +54,21 @@
void BufferQueue::ProxyConsumerListener::onFrameReplaced(
const BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onFrameReplaced(item);
}
}
void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
}
void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onSidebandStreamChanged();
}
}
@@ -85,21 +85,21 @@
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) {
- LOG_ALWAYS_FATAL_IF(outProducer == NULL,
+ LOG_ALWAYS_FATAL_IF(outProducer == nullptr,
"BufferQueue: outProducer must not be NULL");
- LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
+ LOG_ALWAYS_FATAL_IF(outConsumer == nullptr,
"BufferQueue: outConsumer must not be NULL");
sp<BufferQueueCore> core(new BufferQueueCore());
- LOG_ALWAYS_FATAL_IF(core == NULL,
+ LOG_ALWAYS_FATAL_IF(core == nullptr,
"BufferQueue: failed to create BufferQueueCore");
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
- LOG_ALWAYS_FATAL_IF(producer == NULL,
+ LOG_ALWAYS_FATAL_IF(producer == nullptr,
"BufferQueue: failed to create BufferQueueProducer");
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
- LOG_ALWAYS_FATAL_IF(consumer == NULL,
+ LOG_ALWAYS_FATAL_IF(consumer == nullptr,
"BufferQueue: failed to create BufferQueueConsumer");
*outProducer = producer;
@@ -109,8 +109,8 @@
#ifndef NO_BUFFERHUB
void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer) {
- LOG_ALWAYS_FATAL_IF(outProducer == NULL, "BufferQueue: outProducer must not be NULL");
- LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL");
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
@@ -118,16 +118,16 @@
dvr::ProducerQueueConfigBuilder configBuilder;
std::shared_ptr<dvr::ProducerQueue> producerQueue =
dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{});
- LOG_ALWAYS_FATAL_IF(producerQueue == NULL, "BufferQueue: failed to create ProducerQueue.");
+ LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue.");
std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue();
- LOG_ALWAYS_FATAL_IF(consumerQueue == NULL, "BufferQueue: failed to create ConsumerQueue.");
+ LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue.");
producer = BufferHubProducer::Create(producerQueue);
consumer = BufferHubConsumer::Create(consumerQueue);
- LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer");
- LOG_ALWAYS_FATAL_IF(consumer == NULL, "BufferQueue: failed to create BufferQueueConsumer");
+ LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer");
+ LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer");
*outProducer = producer;
*outConsumer = consumer;
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index d70e142..f2d5c8e 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -34,9 +34,10 @@
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
-#include <binder/IPCThreadState.h>
+#include <private/gui/BufferQueueThreadState.h>
#ifndef __ANDROID_VNDK__
#include <binder/PermissionCache.h>
+#include <vndksupport/linker.h>
#endif
#include <system/window.h>
@@ -255,7 +256,7 @@
// mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
// on the consumer side
if (outBuffer->mAcquireCalled) {
- outBuffer->mGraphicBuffer = NULL;
+ outBuffer->mGraphicBuffer = nullptr;
}
mCore->mQueue.erase(front);
@@ -272,7 +273,7 @@
VALIDATE_CONSISTENCY();
}
- if (listener != NULL) {
+ if (listener != nullptr) {
for (int i = 0; i < numDroppedBuffers; ++i) {
listener->onBufferReleased();
}
@@ -321,10 +322,10 @@
const sp<android::GraphicBuffer>& buffer) {
ATRACE_CALL();
- if (outSlot == NULL) {
+ if (outSlot == nullptr) {
BQ_LOGE("attachBuffer: outSlot must not be NULL");
return BAD_VALUE;
- } else if (buffer == NULL) {
+ } else if (buffer == nullptr) {
BQ_LOGE("attachBuffer: cannot attach NULL buffer");
return BAD_VALUE;
}
@@ -413,7 +414,7 @@
ATRACE_BUFFER_INDEX(slot);
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
- releaseFence == NULL) {
+ releaseFence == nullptr) {
BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
releaseFence.get());
return BAD_VALUE;
@@ -465,7 +466,7 @@
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBufferReleased();
}
@@ -476,7 +477,7 @@
const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
ATRACE_CALL();
- if (consumerListener == NULL) {
+ if (consumerListener == nullptr) {
BQ_LOGE("connect: consumerListener may not be NULL");
return BAD_VALUE;
}
@@ -504,13 +505,13 @@
Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mConsumerListener == NULL) {
+ if (mCore->mConsumerListener == nullptr) {
BQ_LOGE("disconnect: no consumer is connected");
return BAD_VALUE;
}
mCore->mIsAbandoned = true;
- mCore->mConsumerListener = NULL;
+ mCore->mConsumerListener = nullptr;
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
@@ -521,7 +522,7 @@
status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
ATRACE_CALL();
- if (outSlotMask == NULL) {
+ if (outSlotMask == nullptr) {
BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL");
return BAD_VALUE;
}
@@ -673,7 +674,7 @@
}
}
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -758,21 +759,31 @@
return savedErrno ? -savedErrno : UNKNOWN_ERROR;
}
- const IPCThreadState* ipc = IPCThreadState::self();
- const uid_t uid = ipc->getCallingUid();
+ bool denied = false;
+ const uid_t uid = BufferQueueThreadState::getCallingUid();
#ifndef __ANDROID_VNDK__
// permission check can't be done for vendors as vendors have no access to
- // the PermissionController
- const pid_t pid = ipc->getCallingPid();
- if ((uid != shellUid) &&
- !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
- outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
- "from pid=%d, uid=%d\n", pid, uid);
+ // the PermissionController. We need to do a runtime check as well, since
+ // the system variant of libgui can be loaded in a vendor process. For eg:
+ // if a HAL uses an llndk library that depends on libgui (libmediandk etc).
+ if (!android_is_in_vendor_process()) {
+ const pid_t pid = BufferQueueThreadState::getCallingPid();
+ if ((uid != shellUid) &&
+ !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
+ outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+ "from pid=%d, uid=%d\n",
+ pid, uid);
+ denied = true;
+ }
+ }
#else
if (uid != shellUid) {
+ denied = true;
+ }
#endif
+ if (denied) {
android_errorWriteWithInfoLog(0x534e4554, "27046057",
- static_cast<int32_t>(uid), NULL, 0);
+ static_cast<int32_t>(uid), nullptr, 0);
return PERMISSION_DENIED;
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index bb703da..960b194 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -349,7 +349,7 @@
BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot);
usleep(PAUSE_TIME);
}
- if (mSlots[slot].mGraphicBuffer != NULL) {
+ if (mSlots[slot].mGraphicBuffer != nullptr) {
BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer",
slot);
usleep(PAUSE_TIME);
@@ -371,7 +371,7 @@
BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot);
usleep(PAUSE_TIME);
}
- if (mSlots[slot].mGraphicBuffer != NULL) {
+ if (mSlots[slot].mGraphicBuffer != nullptr) {
BQ_LOGE("Slot %d is in mFreeSlots but has a buffer",
slot);
usleep(PAUSE_TIME);
@@ -394,7 +394,7 @@
BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot);
usleep(PAUSE_TIME);
}
- if (mSlots[slot].mGraphicBuffer == NULL) {
+ if (mSlots[slot].mGraphicBuffer == nullptr) {
BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot);
usleep(PAUSE_TIME);
}
@@ -418,7 +418,7 @@
BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot);
usleep(PAUSE_TIME);
}
- if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) {
+ if (mSlots[slot].mGraphicBuffer == nullptr && !mIsAllocating) {
BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot);
usleep(PAUSE_TIME);
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index c96a2dd..a462b03 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -35,6 +35,7 @@
#include <gui/GLConsumer.h>
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
+#include <private/gui/BufferQueueThreadState.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -166,7 +167,7 @@
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -221,7 +222,7 @@
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
return NO_ERROR;
@@ -449,11 +450,11 @@
mSlots[found].mBufferState.dequeue();
- if ((buffer == NULL) ||
+ if ((buffer == nullptr) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
{
mSlots[found].mAcquireCalled = false;
- mSlots[found].mGraphicBuffer = NULL;
+ mSlots[found].mGraphicBuffer = nullptr;
mSlots[found].mRequestBufferCalled = false;
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
@@ -471,7 +472,7 @@
BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64,
mCore->mBufferAge);
- if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
+ if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) {
BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
"slot=%d w=%d h=%d format=%u",
found, buffer->width, buffer->height, buffer->format);
@@ -612,7 +613,7 @@
listener = mCore->mConsumerListener;
}
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -623,10 +624,10 @@
sp<Fence>* outFence) {
ATRACE_CALL();
- if (outBuffer == NULL) {
+ if (outBuffer == nullptr) {
BQ_LOGE("detachNextBuffer: outBuffer must not be NULL");
return BAD_VALUE;
- } else if (outFence == NULL) {
+ } else if (outFence == nullptr) {
BQ_LOGE("detachNextBuffer: outFence must not be NULL");
return BAD_VALUE;
}
@@ -670,7 +671,7 @@
listener = mCore->mConsumerListener;
}
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -681,10 +682,10 @@
const sp<android::GraphicBuffer>& buffer) {
ATRACE_CALL();
- if (outSlot == NULL) {
+ if (outSlot == nullptr) {
BQ_LOGE("attachBuffer: outSlot must not be NULL");
return BAD_VALUE;
- } else if (buffer == NULL) {
+ } else if (buffer == nullptr) {
BQ_LOGE("attachBuffer: cannot attach NULL buffer");
return BAD_VALUE;
}
@@ -766,7 +767,7 @@
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
- if (acquireFence == NULL) {
+ if (acquireFence == nullptr) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
@@ -972,9 +973,9 @@
mCallbackCondition.wait(mCallbackMutex);
}
- if (frameAvailableListener != NULL) {
+ if (frameAvailableListener != nullptr) {
frameAvailableListener->onFrameAvailable(item);
- } else if (frameReplacedListener != NULL) {
+ } else if (frameReplacedListener != nullptr) {
frameReplacedListener->onFrameReplaced(item);
}
@@ -1039,7 +1040,7 @@
BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
"(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
- } else if (fence == NULL) {
+ } else if (fence == nullptr) {
BQ_LOGE("cancelBuffer: fence is NULL");
return BAD_VALUE;
}
@@ -1069,7 +1070,7 @@
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
- if (outValue == NULL) {
+ if (outValue == nullptr) {
BQ_LOGE("query: outValue was NULL");
return BAD_VALUE;
}
@@ -1145,12 +1146,12 @@
return NO_INIT;
}
- if (mCore->mConsumerListener == NULL) {
+ if (mCore->mConsumerListener == nullptr) {
BQ_LOGE("connect: BufferQueue has no consumer");
return NO_INIT;
}
- if (output == NULL) {
+ if (output == nullptr) {
BQ_LOGE("connect: output was NULL");
return BAD_VALUE;
}
@@ -1188,10 +1189,10 @@
output->nextFrameNumber = mCore->mFrameCounter + 1;
output->bufferReplaced = false;
- if (listener != NULL) {
+ if (listener != nullptr) {
// Set up a death notification so that we can disconnect
// automatically if the remote producer dies
- if (IInterface::asBinder(listener)->remoteBinder() != NULL) {
+ if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
if (status != NO_ERROR) {
@@ -1210,7 +1211,7 @@
status = BAD_VALUE;
break;
}
- mCore->mConnectedPid = IPCThreadState::self()->getCallingPid();
+ mCore->mConnectedPid = BufferQueueThreadState::getCallingPid();
mCore->mBufferHasBeenQueued = false;
mCore->mDequeueBufferCannotBlock = false;
if (mDequeueTimeout < 0) {
@@ -1233,7 +1234,7 @@
Mutex::Autolock lock(mCore->mMutex);
if (mode == DisconnectMode::AllLocal) {
- if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) {
+ if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) {
return NO_ERROR;
}
api = BufferQueueCore::CURRENTLY_CONNECTED_API;
@@ -1268,7 +1269,7 @@
mCore->freeAllBuffersLocked();
// Remove our death notification callback if we have one
- if (mCore->mLinkedToDeath != NULL) {
+ if (mCore->mLinkedToDeath != nullptr) {
sp<IBinder> token =
IInterface::asBinder(mCore->mLinkedToDeath);
// This can fail if we're here because of the death
@@ -1278,8 +1279,8 @@
}
mCore->mSharedBufferSlot =
BufferQueueCore::INVALID_BUFFER_SLOT;
- mCore->mLinkedToDeath = NULL;
- mCore->mConnectedProducerListener = NULL;
+ mCore->mLinkedToDeath = nullptr;
+ mCore->mConnectedProducerListener = nullptr;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mConnectedPid = -1;
mCore->mSidebandStream.clear();
@@ -1302,7 +1303,7 @@
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
listener->onDisconnect();
}
@@ -1318,7 +1319,7 @@
listener = mCore->mConsumerListener;
} // Autolock scope
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onSidebandStreamChanged();
}
return NO_ERROR;
@@ -1536,7 +1537,7 @@
Mutex::Autolock lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
}
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
new file mode 100644
index 0000000..3b531ec
--- /dev/null
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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 <binder/IPCThreadState.h>
+#include <hwbinder/IPCThreadState.h>
+#include <private/gui/BufferQueueThreadState.h>
+#include <unistd.h>
+
+namespace android {
+
+uid_t BufferQueueThreadState::getCallingUid() {
+ if (hardware::IPCThreadState::self()->isServingCall()) {
+ return hardware::IPCThreadState::self()->getCallingUid();
+ }
+ return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t BufferQueueThreadState::getCallingPid() {
+ if (hardware::IPCThreadState::self()->isServingCall()) {
+ return hardware::IPCThreadState::self()->getCallingPid();
+ }
+ return IPCThreadState::self()->getCallingPid();
+}
+
+} // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index f9e292e..abd9921 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -96,7 +96,7 @@
void ConsumerBase::freeBufferLocked(int slotIndex) {
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
- mSlots[slotIndex].mGraphicBuffer = 0;
+ mSlots[slotIndex].mGraphicBuffer = nullptr;
mSlots[slotIndex].mFence = Fence::NO_FENCE;
mSlots[slotIndex].mFrameNumber = 0;
}
@@ -110,7 +110,7 @@
listener = mFrameAvailableListener.promote();
}
- if (listener != NULL) {
+ if (listener != nullptr) {
CB_LOGV("actually calling onFrameAvailable");
listener->onFrameAvailable(item);
}
@@ -125,7 +125,7 @@
listener = mFrameAvailableListener.promote();
}
- if (listener != NULL) {
+ if (listener != nullptr) {
CB_LOGV("actually calling onFrameReplaced");
listener->onFrameReplaced(item);
}
@@ -352,8 +352,8 @@
return err;
}
- if (item->mGraphicBuffer != NULL) {
- if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
+ if (item->mGraphicBuffer != nullptr) {
+ if (mSlots[item->mSlot].mGraphicBuffer != nullptr) {
freeBufferLocked(item->mSlot);
}
mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
@@ -468,7 +468,7 @@
if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
return false;
}
- return (mSlots[slot].mGraphicBuffer != NULL &&
+ return (mSlots[slot].mGraphicBuffer != nullptr &&
mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle);
}
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 1757ec1..f5cf1c4 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -34,9 +34,9 @@
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- if (sf != NULL) {
+ if (sf != nullptr) {
mEventConnection = sf->createDisplayEventConnection(vsyncSource);
- if (mEventConnection != NULL) {
+ if (mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
}
@@ -47,13 +47,13 @@
}
status_t DisplayEventReceiver::initCheck() const {
- if (mDataChannel != NULL)
+ if (mDataChannel != nullptr)
return NO_ERROR;
return NO_INIT;
}
int DisplayEventReceiver::getFd() const {
- if (mDataChannel == NULL)
+ if (mDataChannel == nullptr)
return NO_INIT;
return mDataChannel->getFd();
@@ -63,7 +63,7 @@
if (int32_t(count) < 0)
return BAD_VALUE;
- if (mEventConnection != NULL) {
+ if (mEventConnection != nullptr) {
mEventConnection->setVsyncRate(count);
return NO_ERROR;
}
@@ -71,7 +71,7 @@
}
status_t DisplayEventReceiver::requestNextVsync() {
- if (mEventConnection != NULL) {
+ if (mEventConnection != nullptr) {
mEventConnection->requestNextVsync();
return NO_ERROR;
}
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index 8a14359..c04d907 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -18,10 +18,10 @@
#define LOG_TAG "FrameEvents"
+#include <android-base/stringprintf.h>
#include <cutils/compiler.h> // For CC_[UN]LIKELY
#include <inttypes.h>
#include <utils/Log.h>
-#include <utils/String8.h>
#include <algorithm>
#include <limits>
@@ -29,6 +29,7 @@
namespace android {
+using base::StringAppendF;
// ============================================================================
// FrameEvents
@@ -86,50 +87,49 @@
releaseFence->getSignalTime();
}
-static void dumpFenceTime(String8& outString, const char* name,
- bool pending, const FenceTime& fenceTime) {
- outString.appendFormat("--- %s", name);
+static void dumpFenceTime(std::string& outString, const char* name, bool pending,
+ const FenceTime& fenceTime) {
+ StringAppendF(&outString, "--- %s", name);
nsecs_t signalTime = fenceTime.getCachedSignalTime();
if (Fence::isValidTimestamp(signalTime)) {
- outString.appendFormat("%" PRId64 "\n", signalTime);
+ StringAppendF(&outString, "%" PRId64 "\n", signalTime);
} else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) {
- outString.appendFormat("Pending\n");
+ outString.append("Pending\n");
} else if (&fenceTime == FenceTime::NO_FENCE.get()){
- outString.appendFormat("N/A\n");
+ outString.append("N/A\n");
} else {
- outString.appendFormat("Error\n");
+ outString.append("Error\n");
}
}
-void FrameEvents::dump(String8& outString) const
-{
+void FrameEvents::dump(std::string& outString) const {
if (!valid) {
return;
}
- outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
- outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime);
- outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+ StringAppendF(&outString, "-- Frame %" PRIu64 "\n", frameNumber);
+ StringAppendF(&outString, "--- Posted \t%" PRId64 "\n", postedTime);
+ StringAppendF(&outString, "--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
- outString.appendFormat("--- Latched \t");
+ outString.append("--- Latched \t");
if (FrameEvents::isValidTimestamp(latchTime)) {
- outString.appendFormat("%" PRId64 "\n", latchTime);
+ StringAppendF(&outString, "%" PRId64 "\n", latchTime);
} else {
- outString.appendFormat("Pending\n");
+ outString.append("Pending\n");
}
- outString.appendFormat("--- Refresh (First)\t");
+ outString.append("--- Refresh (First)\t");
if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) {
- outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ StringAppendF(&outString, "%" PRId64 "\n", firstRefreshStartTime);
} else {
- outString.appendFormat("Pending\n");
+ outString.append("Pending\n");
}
- outString.appendFormat("--- Refresh (Last)\t");
+ outString.append("--- Refresh (Last)\t");
if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) {
- outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ StringAppendF(&outString, "%" PRId64 "\n", lastRefreshStartTime);
} else {
- outString.appendFormat("Pending\n");
+ outString.append("Pending\n");
}
dumpFenceTime(outString, "Acquire \t",
@@ -139,11 +139,11 @@
dumpFenceTime(outString, "Display Present \t",
!addPostCompositeCalled, *displayPresentFence);
- outString.appendFormat("--- DequeueReady \t");
+ outString.append("--- DequeueReady \t");
if (FrameEvents::isValidTimestamp(dequeueReadyTime)) {
- outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+ StringAppendF(&outString, "%" PRId64 "\n", dequeueReadyTime);
} else {
- outString.appendFormat("Pending\n");
+ outString.append("Pending\n");
}
dumpFenceTime(outString, "Release \t",
@@ -206,11 +206,11 @@
return lhs.valid;
}
-void FrameEventHistory::dump(String8& outString) const {
+void FrameEventHistory::dump(std::string& outString) const {
auto earliestFrame = std::min_element(
mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
if (!earliestFrame->valid) {
- outString.appendFormat("-- N/A\n");
+ outString.append("-- N/A\n");
return;
}
for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 885efec..faf02f3 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -46,7 +46,6 @@
#include <utils/Trace.h>
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-#define CROP_EXT_STR "EGL_ANDROID_image_crop"
#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
#define EGL_PROTECTED_CONTENT_EXT 0x32C0
@@ -82,26 +81,6 @@
Mutex GLConsumer::sStaticInitLock;
sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer;
-static bool hasEglAndroidImageCropImpl() {
- EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
- size_t cropExtLen = strlen(CROP_EXT_STR);
- size_t extsLen = strlen(exts);
- bool equal = !strcmp(CROP_EXT_STR, exts);
- bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1);
- bool atEnd = (cropExtLen+1) < extsLen &&
- !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1));
- bool inMiddle = strstr(exts, " " CROP_EXT_STR " ");
- return equal || atStart || atEnd || inMiddle;
-}
-
-static bool hasEglAndroidImageCrop() {
- // Only compute whether the extension is present once the first time this
- // function is called.
- static bool hasIt = hasEglAndroidImageCropImpl();
- return hasIt;
-}
-
static bool hasEglProtectedContentImpl() {
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
@@ -122,10 +101,6 @@
return hasIt;
}
-static bool isEglImageCroppable(const Rect& crop) {
- return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0);
-}
-
GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp),
@@ -291,7 +266,7 @@
return err;
}
- if (mReleasedTexImage == NULL) {
+ if (mReleasedTexImage == nullptr) {
mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
}
@@ -321,7 +296,7 @@
sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() {
Mutex::Autolock _l(sStaticInitLock);
- if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) {
+ if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
// The first time, create the debug texture in case the application
// continues to use it.
sp<GraphicBuffer> buffer = new GraphicBuffer(
@@ -357,7 +332,7 @@
// If item->mGraphicBuffer is not null, this buffer has not been acquired
// before, so any prior EglImage created is using a stale buffer. This
// replaces any old EglImage with a new one (using the new buffer).
- if (item->mGraphicBuffer != NULL) {
+ if (item->mGraphicBuffer != nullptr) {
int slot = item->mSlot;
mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
}
@@ -406,7 +381,7 @@
// ConsumerBase.
// We may have to do this even when item.mGraphicBuffer == NULL (which
// means the buffer was previously acquired).
- err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
+ err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
if (err != NO_ERROR) {
GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
mEglDisplay, slot);
@@ -430,8 +405,8 @@
}
GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
- mCurrentTexture, mCurrentTextureImage != NULL ?
- mCurrentTextureImage->graphicBufferHandle() : 0,
+ mCurrentTexture, mCurrentTextureImage != nullptr ?
+ mCurrentTextureImage->graphicBufferHandle() : nullptr,
slot, mSlots[slot].mGraphicBuffer->handle);
// Hang onto the pointer so that it isn't freed in the call to
@@ -491,13 +466,12 @@
glBindTexture(mTexTarget, mTexName);
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
- mCurrentTextureImage == NULL) {
+ mCurrentTextureImage == nullptr) {
GLC_LOGE("bindTextureImage: no currently-bound texture");
return NO_INIT;
}
- status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
- mCurrentCrop);
+ status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
if (err != NO_ERROR) {
GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
mEglDisplay, mCurrentTexture);
@@ -511,9 +485,7 @@
// forcing the creation of a new image.
if ((error = glGetError()) != GL_NO_ERROR) {
glBindTexture(mTexTarget, mTexName);
- status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay,
- mCurrentCrop,
- true);
+ status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
if (result != NO_ERROR) {
GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
mEglDisplay, mCurrentTexture);
@@ -655,7 +627,7 @@
mTexName = tex;
mAttached = true;
- if (mCurrentTextureImage != NULL) {
+ if (mCurrentTextureImage != nullptr) {
// This may wait for a buffer a second time. This is likely required if
// this is a different context, since otherwise the wait could be skipped
// by bouncing through another context. For the same context the extra
@@ -676,7 +648,7 @@
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
if (SyncFeatures::getInstance().useNativeFenceSync()) {
EGLSyncKHR sync = eglCreateSyncKHR(dpy,
- EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+ EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
if (sync == EGL_NO_SYNC_KHR) {
GLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x",
eglGetError());
@@ -720,7 +692,7 @@
// Create a fence for the outstanding accesses in the current
// OpenGL ES context.
- fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+ fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
if (fence == EGL_NO_SYNC_KHR) {
GLC_LOGE("syncForReleaseLocked: error creating fence: %#x",
eglGetError());
@@ -752,11 +724,11 @@
bool needsRecompute = mFilteringEnabled != enabled;
mFilteringEnabled = enabled;
- if (needsRecompute && mCurrentTextureImage==NULL) {
+ if (needsRecompute && mCurrentTextureImage==nullptr) {
GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL");
}
- if (needsRecompute && mCurrentTextureImage != NULL) {
+ if (needsRecompute && mCurrentTextureImage != nullptr) {
computeCurrentTransformMatrixLocked();
}
}
@@ -769,8 +741,7 @@
GLC_LOGD("computeCurrentTransformMatrixLocked: "
"mCurrentTextureImage is NULL");
}
- computeTransformMatrix(mCurrentTransformMatrix, buf,
- isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop,
+ computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop,
mCurrentTransform, mFilteringEnabled);
}
@@ -863,10 +834,10 @@
xform = crop * xform;
}
- // SurfaceFlinger expects the top of its window textures to be at a Y
- // coordinate of 0, so GLConsumer must behave the same way. We don't
- // want to expose this to applications, however, so we must add an
- // additional vertical flip to the transform after all the other transforms.
+ // GLConsumer uses the GL convention where (0, 0) is the bottom-left
+ // corner and (1, 1) is the top-right corner. Add an additional vertical
+ // flip after all other transforms to map from GL convention to buffer
+ // queue memory layout, where (0, 0) is the top-left corner.
xform = mtxFlipV * xform;
memcpy(outTransform, xform.asArray(), sizeof(xform));
@@ -938,7 +909,7 @@
}
return (mCurrentTextureImage == nullptr) ?
- NULL : mCurrentTextureImage->graphicBuffer();
+ nullptr : mCurrentTextureImage->graphicBuffer();
}
Rect GLConsumer::getCurrentCrop() const {
@@ -1063,8 +1034,7 @@
GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) :
mGraphicBuffer(graphicBuffer),
mEglImage(EGL_NO_IMAGE_KHR),
- mEglDisplay(EGL_NO_DISPLAY),
- mCropRect(Rect::EMPTY_RECT) {
+ mEglDisplay(EGL_NO_DISPLAY) {
}
GLConsumer::EglImage::~EglImage() {
@@ -1077,13 +1047,11 @@
}
status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
- const Rect& cropRect,
bool forceCreation) {
// If there's an image and it's no longer valid, destroy it.
bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
bool displayInvalid = mEglDisplay != eglDisplay;
- bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
- if (haveImage && (displayInvalid || cropInvalid || forceCreation)) {
+ if (haveImage && (displayInvalid || forceCreation)) {
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("createIfNeeded: eglDestroyImageKHR failed");
}
@@ -1095,14 +1063,12 @@
// If there's no image, create one.
if (mEglImage == EGL_NO_IMAGE_KHR) {
mEglDisplay = eglDisplay;
- mCropRect = cropRect;
- mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
+ mEglImage = createImage(mEglDisplay, mGraphicBuffer);
}
// Fail if we can't create a valid image.
if (mEglImage == EGL_NO_IMAGE_KHR) {
mEglDisplay = EGL_NO_DISPLAY;
- mCropRect.makeInvalid();
const sp<GraphicBuffer>& buffer = mGraphicBuffer;
ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
@@ -1119,38 +1085,19 @@
}
EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy,
- const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
+ const sp<GraphicBuffer>& graphicBuffer) {
EGLClientBuffer cbuf =
static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
const bool createProtectedImage =
(graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) &&
hasEglProtectedContent();
EGLint attrs[] = {
- EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
- EGL_IMAGE_CROP_LEFT_ANDROID, crop.left,
- EGL_IMAGE_CROP_TOP_ANDROID, crop.top,
- EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right,
- EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom,
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
createProtectedImage ? EGL_TRUE : EGL_NONE,
EGL_NONE,
};
- if (!crop.isValid()) {
- // No crop rect to set, so leave the crop out of the attrib array. Make
- // sure to propagate the protected content attrs if they are set.
- attrs[2] = attrs[10];
- attrs[3] = attrs[11];
- attrs[4] = EGL_NONE;
- } else if (!isEglImageCroppable(crop)) {
- // The crop rect is not at the origin, so we can't set the crop on the
- // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
- // extension. In the future we can add a layered extension that
- // removes this restriction if there is hardware that can support it.
- attrs[2] = attrs[10];
- attrs[3] = attrs[11];
- attrs[4] = EGL_NONE;
- }
- eglInitialize(dpy, 0, 0);
+ eglInitialize(dpy, nullptr, nullptr);
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
if (image == EGL_NO_IMAGE_KHR) {
diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp
index bc0c83c..3ec20ee 100644
--- a/libs/gui/GuiConfig.cpp
+++ b/libs/gui/GuiConfig.cpp
@@ -18,8 +18,7 @@
namespace android {
-void appendGuiConfigString(String8& configStr)
-{
+void appendGuiConfigString(std::string& configStr) {
static const char* config =
" [libgui"
#ifdef DONT_USE_FENCE_SYNC
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index b715e43..add3ef0 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -15,6 +15,7 @@
*/
#include <gui/HdrMetadata.h>
+#include <limits>
namespace android {
@@ -26,6 +27,10 @@
if (validTypes & CTA861_3) {
size += sizeof(cta8613);
}
+ if (validTypes & HDR10PLUS) {
+ size += sizeof(size_t);
+ size += hdr10plus.size();
+ }
return size;
}
@@ -41,6 +46,12 @@
if (validTypes & CTA861_3) {
FlattenableUtils::write(buffer, size, cta8613);
}
+ if (validTypes & HDR10PLUS) {
+ size_t metadataSize = hdr10plus.size();
+ FlattenableUtils::write(buffer, size, metadataSize);
+ memcpy(buffer, hdr10plus.data(), metadataSize);
+ FlattenableUtils::advance(buffer, size, metadataSize);
+ }
return NO_ERROR;
}
@@ -62,6 +73,22 @@
}
FlattenableUtils::read(buffer, size, cta8613);
}
+ if (validTypes & HDR10PLUS) {
+ if (size < sizeof(size_t)) {
+ return NO_MEMORY;
+ }
+
+ size_t metadataSize;
+ FlattenableUtils::read(buffer, size, metadataSize);
+
+ if (size < metadataSize) {
+ return NO_MEMORY;
+ }
+
+ hdr10plus.resize(metadataSize);
+ memcpy(hdr10plus.data(), buffer, metadataSize);
+ FlattenableUtils::advance(buffer, size, metadataSize);
+ }
return NO_ERROR;
}
@@ -91,6 +118,10 @@
}
}
+ if ((validTypes & HDR10PLUS) == HDR10PLUS) {
+ if (hdr10plus != rhs.hdr10plus) return false;
+ }
+
return true;
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 74ab5ac..9dde15d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -30,16 +30,21 @@
#ifndef NO_BUFFERHUB
#include <gui/BufferHubProducer.h>
#endif
+
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
-#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
-
namespace android {
// ----------------------------------------------------------------------------
-using ::android::hardware::graphics::bufferqueue::V1_0::utils::
+using H2BGraphicBufferProducerV1_0 =
+ ::android::hardware::graphics::bufferqueue::V1_0::utils::
+ H2BGraphicBufferProducer;
+using H2BGraphicBufferProducerV2_0 =
+ ::android::hardware::graphics::bufferqueue::V2_0::utils::
H2BGraphicBufferProducer;
enum {
@@ -190,10 +195,10 @@
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence) {
- if (outBuffer == NULL) {
+ if (outBuffer == nullptr) {
ALOGE("detachNextBuffer: outBuffer must not be NULL");
return BAD_VALUE;
- } else if (outFence == NULL) {
+ } else if (outFence == nullptr) {
ALOGE("detachNextBuffer: outFence must not be NULL");
return BAD_VALUE;
}
@@ -301,7 +306,7 @@
int api, bool producerControlledByApp, QueueBufferOutput* output) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
- if (listener != NULL) {
+ if (listener != nullptr) {
data.writeInt32(1);
data.writeStrongBinder(IInterface::asBinder(listener));
} else {
@@ -534,7 +539,9 @@
BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
class HpGraphicBufferProducer : public HpInterface<
- BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+ BpGraphicBufferProducer,
+ H2BGraphicBufferProducerV1_0,
+ H2BGraphicBufferProducerV2_0> {
public:
explicit HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
@@ -651,7 +658,7 @@
}
};
-IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
"android.gui.IGraphicBufferProducer");
// ----------------------------------------------------------------------
@@ -738,8 +745,8 @@
int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
- reply->writeInt32(buffer != 0);
- if (buffer != 0) {
+ reply->writeInt32(buffer != nullptr);
+ if (buffer != nullptr) {
reply->write(*buffer);
}
reply->writeInt32(result);
@@ -797,12 +804,12 @@
int32_t result = detachNextBuffer(&buffer, &fence);
reply->writeInt32(result);
if (result == NO_ERROR) {
- reply->writeInt32(buffer != NULL);
- if (buffer != NULL) {
+ reply->writeInt32(buffer != nullptr);
+ if (buffer != nullptr) {
reply->write(*buffer);
}
- reply->writeInt32(fence != NULL);
- if (fence != NULL) {
+ reply->writeInt32(fence != nullptr);
+ if (fence != nullptr) {
reply->write(*fence);
}
}
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 62abfa8..936063a 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -15,7 +15,8 @@
*/
#include <binder/Parcel.h>
-
+#include <gui/bufferqueue/1.0/H2BProducerListener.h>
+#include <gui/bufferqueue/2.0/H2BProducerListener.h>
#include <gui/IProducerListener.h>
namespace android {
@@ -61,7 +62,24 @@
// translation unit (see clang warning -Wweak-vtables)
BpProducerListener::~BpProducerListener() {}
-IMPLEMENT_META_INTERFACE(ProducerListener, "android.gui.IProducerListener")
+class HpProducerListener : public HpInterface<
+ BpProducerListener,
+ hardware::graphics::bufferqueue::V1_0::utils::H2BProducerListener,
+ hardware::graphics::bufferqueue::V2_0::utils::H2BProducerListener> {
+public:
+ explicit HpProducerListener(const sp<IBinder>& base) : PBase{base} {}
+
+ virtual void onBufferReleased() override {
+ mBase->onBufferReleased();
+ }
+
+ virtual bool needsReleaseNotify() override {
+ return mBase->needsReleaseNotify();
+ }
+};
+
+IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener,
+ "android.gui.IProducerListener")
status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
new file mode 100644
index 0000000..40cbfce
--- /dev/null
+++ b/libs/gui/IRegionSamplingListener.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "IRegionSamplingListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/IRegionSamplingListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
+ LAST = ON_SAMPLE_COLLECTED,
+};
+
+} // Anonymous namespace
+
+class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
+public:
+ explicit BpRegionSamplingListener(const sp<IBinder>& impl)
+ : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
+
+ ~BpRegionSamplingListener() override;
+
+ void onSampleCollected(float medianLuma) override {
+ callRemoteAsync<decltype(
+ &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
+ }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpRegionSamplingListener::~BpRegionSamplingListener() = default;
+
+IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
+
+status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_SAMPLE_COLLECTED:
+ return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
+ }
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index d2d27e8..247dc8d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
@@ -63,21 +64,11 @@
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual sp<ISurfaceComposerClient> createScopedConnection(
- const sp<IGraphicBufferProducer>& parent)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(parent));
- remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply);
- return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
- }
-
- virtual void setTransactionState(
- const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays,
- uint32_t flags)
- {
+ virtual void setTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& commands,
+ int64_t desiredPresentTime) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -92,6 +83,9 @@
}
data.writeUint32(flags);
+ data.writeStrongBinder(applyToken);
+ commands.write(data);
+ data.writeInt64(desiredPresentTime);
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@@ -103,59 +97,65 @@
}
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
- ISurfaceComposer::Rotation rotation) {
+ const ui::Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ ISurfaceComposer::Rotation rotation, bool captureSecureLayers) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
+ data.writeInt32(static_cast<int32_t>(reqDataspace));
+ data.writeInt32(static_cast<int32_t>(reqPixelFormat));
data.write(sourceCrop);
data.writeUint32(reqWidth);
data.writeUint32(reqHeight);
- data.writeInt32(minLayerZ);
- data.writeInt32(maxLayerZ);
data.writeInt32(static_cast<int32_t>(useIdentityTransform));
data.writeInt32(static_cast<int32_t>(rotation));
- status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
-
- if (err != NO_ERROR) {
- return err;
+ data.writeInt32(static_cast<int32_t>(captureSecureLayers));
+ status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("captureScreen failed to transact: %d", result);
+ return result;
}
-
- err = reply.readInt32();
- if (err != NO_ERROR) {
- return err;
+ result = reply.readInt32();
+ if (result != NO_ERROR) {
+ ALOGE("captureScreen failed to readInt32: %d", result);
+ return result;
}
*outBuffer = new GraphicBuffer();
reply.read(**outBuffer);
- return err;
+
+ return result;
}
virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
- sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+ sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
float frameScale, bool childrenOnly) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(layerHandleBinder);
+ data.writeInt32(static_cast<int32_t>(reqDataspace));
+ data.writeInt32(static_cast<int32_t>(reqPixelFormat));
data.write(sourceCrop);
data.writeFloat(frameScale);
data.writeBool(childrenOnly);
- status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
-
- if (err != NO_ERROR) {
- return err;
+ status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("captureLayers failed to transact: %d", result);
+ return result;
}
-
- err = reply.readInt32();
- if (err != NO_ERROR) {
- return err;
+ result = reply.readInt32();
+ if (result != NO_ERROR) {
+ ALOGE("captureLayers failed to readInt32: %d", result);
+ return result;
}
*outBuffer = new GraphicBuffer();
reply.read(**outBuffer);
- return err;
+ return result;
}
virtual bool authenticateSurfaceTexture(
@@ -277,12 +277,25 @@
remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply);
}
- virtual sp<IBinder> getBuiltInDisplay(int32_t id)
- {
+ virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeInt32(id);
- remote()->transact(BnSurfaceComposer::GET_BUILT_IN_DISPLAY, data, &reply);
+ if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) ==
+ NO_ERROR) {
+ std::vector<PhysicalDisplayId> displayIds;
+ if (reply.readUint64Vector(&displayIds) == NO_ERROR) {
+ return displayIds;
+ }
+ }
+
+ return {};
+ }
+
+ virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeUint64(displayId);
+ remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply);
return reply.readStrongBinder();
}
@@ -332,34 +345,6 @@
return result;
}
- virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getDisplayViewport failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getDisplayViewport failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_VIEWPORT, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getDisplayViewport failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result == NO_ERROR) {
- result = reply.read(*outViewport);
- if (result != NO_ERROR) {
- ALOGE("getDisplayViewport failed to read: %d", result);
- return result;
- }
- }
- return result;
- }
-
virtual int getActiveConfig(const sp<IBinder>& display)
{
Parcel data, reply;
@@ -372,10 +357,26 @@
virtual status_t setActiveConfig(const sp<IBinder>& display, int id)
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(display);
- data.writeInt32(id);
- remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply);
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveConfig failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = data.writeInt32(id);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveConfig failed to writeInt32: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setActiveConfig failed to transact: %d", result);
+ return result;
+ }
return reply.readInt32();
}
@@ -409,6 +410,32 @@
return result;
}
+ virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& primaries) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32();
+ if (result == NO_ERROR) {
+ memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)),
+ sizeof(ui::DisplayPrimaries));
+ }
+ return result;
+ }
+
virtual ColorMode getActiveColorMode(const sp<IBinder>& display) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -457,8 +484,16 @@
virtual status_t clearAnimationFrameStats() {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply);
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("clearAnimationFrameStats failed to transact: %d", result);
+ return result;
+ }
return reply.readInt32();
}
@@ -563,6 +598,321 @@
outLayers->clear();
return reply.readParcelableVector(outLayers);
}
+
+ virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+ ui::PixelFormat* defaultPixelFormat,
+ ui::Dataspace* wideColorGamutDataspace,
+ ui::PixelFormat* wideColorGamutPixelFormat) const {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = static_cast<status_t>(reply.readInt32());
+ if (error == NO_ERROR) {
+ *defaultDataspace = static_cast<ui::Dataspace>(reply.readInt32());
+ *defaultPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32());
+ *wideColorGamutDataspace = static_cast<ui::Dataspace>(reply.readInt32());
+ *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32());
+ }
+ return error;
+ }
+
+ virtual status_t getColorManagement(bool* outGetColorManagement) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply);
+ bool result;
+ status_t err = reply.readBool(&result);
+ if (err == NO_ERROR) {
+ *outGetColorManagement = result;
+ }
+ return err;
+ }
+
+ virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const {
+ if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE;
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+
+ status_t error =
+ remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+ data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ uint32_t value = 0;
+ error = reply.readUint32(&value);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ *outFormat = static_cast<ui::PixelFormat>(value);
+
+ error = reply.readUint32(&value);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ *outDataspace = static_cast<ui::Dataspace>(value);
+
+ error = reply.readUint32(&value);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ *outComponentMask = static_cast<uint8_t>(value);
+ return error;
+ }
+
+ virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+ uint8_t componentMask,
+ uint64_t maxFrames) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+ data.writeBool(enable);
+ data.writeByte(static_cast<int8_t>(componentMask));
+ data.writeUint64(maxFrames);
+ status_t result =
+ remote()->transact(BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, data,
+ &reply);
+ return result;
+ }
+
+ virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+ uint64_t timestamp,
+ DisplayedFrameStats* outStats) const {
+ if (!outStats) return BAD_VALUE;
+
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+ data.writeUint64(maxFrames);
+ data.writeUint64(timestamp);
+
+ status_t result =
+ remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply);
+
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ result = reply.readUint64(&outStats->numFrames);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ result = reply.readUint64Vector(&outStats->component_0_sample);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readUint64Vector(&outStats->component_1_sample);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readUint64Vector(&outStats->component_2_sample);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readUint64Vector(&outStats->component_3_sample);
+ return result;
+ }
+
+ virtual status_t getProtectedContentSupport(bool* outSupported) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t error =
+ remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply.readBool(outSupported);
+ return error;
+ }
+
+ virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+ bool* outIsWideColorDisplay) const {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = data.writeStrongBinder(token);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ error = remote()->transact(BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply.readBool(outIsWideColorDisplay);
+ return error;
+ }
+
+ virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write interface token");
+ return error;
+ }
+ error = data.write(samplingArea);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write sampling area");
+ return error;
+ }
+ error = data.writeStrongBinder(stopLayerHandle);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write stop layer handle");
+ return error;
+ }
+ error = data.writeStrongBinder(IInterface::asBinder(listener));
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write listener");
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("removeRegionSamplingListener: Failed to write interface token");
+ return error;
+ }
+ error = data.writeStrongBinder(IInterface::asBinder(listener));
+ if (error != NO_ERROR) {
+ ALOGE("removeRegionSamplingListener: Failed to write listener");
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data,
+ &reply);
+ if (error != NO_ERROR) {
+ ALOGE("removeRegionSamplingListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = data.writeInt32Vector(allowedConfigs);
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to transact: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs) {
+ if (!outAllowedConfigs) return BAD_VALUE;
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getAllowedDisplayConfigs failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32Vector(outAllowedConfigs);
+ if (result != NO_ERROR) {
+ ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayBrightnessSupport: failed to write interface token: %d", error);
+ return error;
+ }
+ error = data.writeStrongBinder(displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayBrightnessSupport: failed to write display token: %d", error);
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayBrightnessSupport: failed to transact: %d", error);
+ return error;
+ }
+ bool support;
+ error = reply.readBool(&support);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayBrightnessSupport: failed to read support: %d", error);
+ return error;
+ }
+ *outSupport = support;
+ return NO_ERROR;
+ }
+
+ virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("setDisplayBrightness: failed to write interface token: %d", error);
+ return error;
+ }
+ error = data.writeStrongBinder(displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("setDisplayBrightness: failed to write display token: %d", error);
+ return error;
+ }
+ error = data.writeFloat(brightness);
+ if (error != NO_ERROR) {
+ ALOGE("setDisplayBrightness: failed to write brightness: %d", error);
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("setDisplayBrightness: failed to transact: %d", error);
+ return error;
+ }
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -583,14 +933,6 @@
reply->writeStrongBinder(b);
return NO_ERROR;
}
- case CREATE_SCOPED_CONNECTION: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IGraphicBufferProducer> bufferProducer =
- interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
- sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer));
- reply->writeStrongBinder(b);
- return NO_ERROR;
- }
case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -598,10 +940,10 @@
if (count > data.dataSize()) {
return BAD_VALUE;
}
- ComposerState s;
Vector<ComposerState> state;
state.setCapacity(count);
for (size_t i = 0; i < count; i++) {
+ ComposerState s;
if (s.read(data) == BAD_VALUE) {
return BAD_VALUE;
}
@@ -623,7 +965,13 @@
}
uint32_t stateFlags = data.readUint32();
- setTransactionState(state, displays, stateFlags);
+ sp<IBinder> applyToken = data.readStrongBinder();
+ InputWindowCommands inputWindowCommands;
+ inputWindowCommands.read(data);
+
+ int64_t desiredPresentTime = data.readInt64();
+ setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+ desiredPresentTime);
return NO_ERROR;
}
case BOOT_FINISHED: {
@@ -634,19 +982,20 @@
case CAPTURE_SCREEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = data.readStrongBinder();
+ ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
+ ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
sp<GraphicBuffer> outBuffer;
Rect sourceCrop(Rect::EMPTY_RECT);
data.read(sourceCrop);
uint32_t reqWidth = data.readUint32();
uint32_t reqHeight = data.readUint32();
- int32_t minLayerZ = data.readInt32();
- int32_t maxLayerZ = data.readInt32();
bool useIdentityTransform = static_cast<bool>(data.readInt32());
int32_t rotation = data.readInt32();
+ bool captureSecureLayers = static_cast<bool>(data.readInt32());
- status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight,
- minLayerZ, maxLayerZ, useIdentityTransform,
- static_cast<ISurfaceComposer::Rotation>(rotation));
+ status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat,
+ sourceCrop, reqWidth, reqHeight, useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation), captureSecureLayers);
reply->writeInt32(res);
if (res == NO_ERROR) {
reply->write(*outBuffer);
@@ -656,14 +1005,16 @@
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> layerHandleBinder = data.readStrongBinder();
+ ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32());
+ ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32());
sp<GraphicBuffer> outBuffer;
Rect sourceCrop(Rect::EMPTY_RECT);
data.read(sourceCrop);
float frameScale = data.readFloat();
bool childrenOnly = data.readBool();
- status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale,
- childrenOnly);
+ status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace,
+ reqPixelFormat, sourceCrop, frameScale, childrenOnly);
reply->writeInt32(res);
if (res == NO_ERROR) {
reply->write(*outBuffer);
@@ -718,10 +1069,10 @@
destroyDisplay(display);
return NO_ERROR;
}
- case GET_BUILT_IN_DISPLAY: {
+ case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- int32_t id = data.readInt32();
- sp<IBinder> display(getBuiltInDisplay(id));
+ PhysicalDisplayId displayId = data.readUint64();
+ sp<IBinder> display = getPhysicalDisplayToken(displayId);
reply->writeStrongBinder(display);
return NO_ERROR;
}
@@ -752,26 +1103,6 @@
}
return NO_ERROR;
}
- case GET_DISPLAY_VIEWPORT: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- Rect outViewport;
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getDisplayViewport failed to readStrongBinder: %d", result);
- return result;
- }
- result = getDisplayViewport(display, &outViewport);
- result = reply->writeInt32(result);
- if (result == NO_ERROR) {
- result = reply->write(outViewport);
- if (result != NO_ERROR) {
- ALOGE("getDisplayViewport failed to write: %d", result);
- return result;
- }
- }
- return NO_ERROR;
- }
case GET_ACTIVE_CONFIG: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = data.readStrongBinder();
@@ -806,6 +1137,26 @@
}
return NO_ERROR;
}
+ case GET_DISPLAY_NATIVE_PRIMARIES: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ ui::DisplayPrimaries primaries;
+ sp<IBinder> display = nullptr;
+
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result);
+ return result;
+ }
+
+ result = getDisplayNativePrimaries(display, primaries);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries,
+ sizeof(ui::DisplayPrimaries));
+ }
+
+ return NO_ERROR;
+ }
case GET_ACTIVE_COLOR_MODE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -906,12 +1257,225 @@
}
return result;
}
+ case GET_COMPOSITION_PREFERENCE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ ui::Dataspace defaultDataspace;
+ ui::PixelFormat defaultPixelFormat;
+ ui::Dataspace wideColorGamutDataspace;
+ ui::PixelFormat wideColorGamutPixelFormat;
+ status_t error =
+ getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+ &wideColorGamutDataspace, &wideColorGamutPixelFormat);
+ reply->writeInt32(error);
+ if (error == NO_ERROR) {
+ reply->writeInt32(static_cast<int32_t>(defaultDataspace));
+ reply->writeInt32(static_cast<int32_t>(defaultPixelFormat));
+ reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace));
+ reply->writeInt32(static_cast<int32_t>(wideColorGamutPixelFormat));
+ }
+ return error;
+ }
+ case GET_COLOR_MANAGEMENT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ bool result;
+ status_t error = getColorManagement(&result);
+ if (error == NO_ERROR) {
+ reply->writeBool(result);
+ }
+ return error;
+ }
+ case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ sp<IBinder> display = data.readStrongBinder();
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ uint8_t component = 0;
+ auto result =
+ getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component);
+ if (result == NO_ERROR) {
+ reply->writeUint32(static_cast<uint32_t>(format));
+ reply->writeUint32(static_cast<uint32_t>(dataspace));
+ reply->writeUint32(static_cast<uint32_t>(component));
+ }
+ return result;
+ }
+ case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ sp<IBinder> display = nullptr;
+ bool enable = false;
+ int8_t componentMask = 0;
+ uint64_t maxFrames = 0;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setDisplayContentSamplingEnabled failure in reading Display token: %d",
+ result);
+ return result;
+ }
+
+ result = data.readBool(&enable);
+ if (result != NO_ERROR) {
+ ALOGE("setDisplayContentSamplingEnabled failure in reading enable: %d", result);
+ return result;
+ }
+
+ result = data.readByte(static_cast<int8_t*>(&componentMask));
+ if (result != NO_ERROR) {
+ ALOGE("setDisplayContentSamplingEnabled failure in reading component mask: %d",
+ result);
+ return result;
+ }
+
+ result = data.readUint64(&maxFrames);
+ if (result != NO_ERROR) {
+ ALOGE("setDisplayContentSamplingEnabled failure in reading max frames: %d", result);
+ return result;
+ }
+
+ return setDisplayContentSamplingEnabled(display, enable,
+ static_cast<uint8_t>(componentMask), maxFrames);
+ }
+ case GET_DISPLAYED_CONTENT_SAMPLE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ sp<IBinder> display = data.readStrongBinder();
+ uint64_t maxFrames = 0;
+ uint64_t timestamp = 0;
+
+ status_t result = data.readUint64(&maxFrames);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayedContentSample failure in reading max frames: %d", result);
+ return result;
+ }
+
+ result = data.readUint64(×tamp);
+ if (result != NO_ERROR) {
+ ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result);
+ return result;
+ }
+
+ DisplayedFrameStats stats;
+ result = getDisplayedContentSample(display, maxFrames, timestamp, &stats);
+ if (result == NO_ERROR) {
+ reply->writeUint64(stats.numFrames);
+ reply->writeUint64Vector(stats.component_0_sample);
+ reply->writeUint64Vector(stats.component_1_sample);
+ reply->writeUint64Vector(stats.component_2_sample);
+ reply->writeUint64Vector(stats.component_3_sample);
+ }
+ return result;
+ }
+ case GET_PROTECTED_CONTENT_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ bool result;
+ status_t error = getProtectedContentSupport(&result);
+ if (error == NO_ERROR) {
+ reply->writeBool(result);
+ }
+ return error;
+ }
+ case IS_WIDE_COLOR_DISPLAY: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t error = data.readStrongBinder(&display);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ bool result;
+ error = isWideColorDisplay(display, &result);
+ if (error == NO_ERROR) {
+ reply->writeBool(result);
+ }
+ return error;
+ }
+ case GET_PHYSICAL_DISPLAY_IDS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ return reply->writeUint64Vector(getPhysicalDisplayIds());
+ }
+ case ADD_REGION_SAMPLING_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ Rect samplingArea;
+ status_t result = data.read(samplingArea);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read sampling area");
+ return result;
+ }
+ sp<IBinder> stopLayerHandle;
+ result = data.readNullableStrongBinder(&stopLayerHandle);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read stop layer handle");
+ return result;
+ }
+ sp<IRegionSamplingListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read listener");
+ return result;
+ }
+ return addRegionSamplingListener(samplingArea, stopLayerHandle, listener);
+ }
+ case REMOVE_REGION_SAMPLING_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IRegionSamplingListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeRegionSamplingListener: Failed to read listener");
+ return result;
+ }
+ return removeRegionSamplingListener(listener);
+ }
+ case SET_ALLOWED_DISPLAY_CONFIGS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken = data.readStrongBinder();
+ std::vector<int32_t> allowedConfigs;
+ data.readInt32Vector(&allowedConfigs);
+ status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs);
+ reply->writeInt32(result);
+ return result;
+ }
+ case GET_ALLOWED_DISPLAY_CONFIGS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken = data.readStrongBinder();
+ std::vector<int32_t> allowedConfigs;
+ status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs);
+ reply->writeInt32Vector(allowedConfigs);
+ reply->writeInt32(result);
+ return result;
+ }
+ case GET_DISPLAY_BRIGHTNESS_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("getDisplayBrightnessSupport: failed to read display token: %d", error);
+ return error;
+ }
+ bool support = false;
+ error = getDisplayBrightnessSupport(displayToken, &support);
+ reply->writeBool(support);
+ return error;
+ }
+ case SET_DISPLAY_BRIGHTNESS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken;
+ status_t error = data.readNullableStrongBinder(&displayToken);
+ if (error != NO_ERROR) {
+ ALOGE("setDisplayBrightness: failed to read display token: %d", error);
+ return error;
+ }
+ float brightness = -1.0f;
+ error = data.readFloat(&brightness);
+ if (error != NO_ERROR) {
+ ALOGE("setDisplayBrightness: failed to read brightness: %d", error);
+ return error;
+ }
+ return setDisplayBrightness(displayToken, brightness);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
}
}
-// ----------------------------------------------------------------------------
-
-};
+} // namespace android
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index a6890ee..129558b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -31,7 +31,7 @@
enum class Tag : uint32_t {
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
- DESTROY_SURFACE,
+ CREATE_WITH_SURFACE_PARENT,
CLEAR_LAYER_FRAME_STATS,
GET_LAYER_FRAME_STATS,
LAST = GET_LAYER_FRAME_STATS,
@@ -47,19 +47,26 @@
~BpSurfaceComposerClient() override;
status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
- uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
- int32_t ownerUid, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) override {
+ uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
name, width, height,
format, flags, parent,
- windowType, ownerUid,
+ std::move(metadata),
handle, gbp);
}
- status_t destroySurface(const sp<IBinder>& handle) override {
- return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE,
- handle);
+ status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t flags,
+ const sp<IGraphicBufferProducer>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) override {
+ return callRemote<decltype(
+ &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
+ name, width, height, format,
+ flags, parent,
+ std::move(metadata), handle,
+ gbp);
}
status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -92,8 +99,8 @@
switch (tag) {
case Tag::CREATE_SURFACE:
return callLocal(data, reply, &ISurfaceComposerClient::createSurface);
- case Tag::DESTROY_SURFACE:
- return callLocal(data, reply, &ISurfaceComposerClient::destroySurface);
+ case Tag::CREATE_WITH_SURFACE_PARENT:
+ return callLocal(data, reply, &ISurfaceComposerClient::createWithSurfaceParent);
case Tag::CLEAR_LAYER_FRAME_STATS:
return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
case Tag::GET_LAYER_FRAME_STATS:
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
new file mode 100644
index 0000000..ce88d7b
--- /dev/null
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2018 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 "ITransactionCompletedListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/ITransactionCompletedListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
+ LAST = ON_TRANSACTION_COMPLETED,
+};
+
+} // Anonymous namespace
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+ status_t err = output->writeStrongBinder(surfaceControl);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->writeInt64(acquireTime);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (previousReleaseFence) {
+ err = output->writeBool(true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->write(*previousReleaseFence);
+ } else {
+ err = output->writeBool(false);
+ }
+ return err;
+}
+
+status_t SurfaceStats::readFromParcel(const Parcel* input) {
+ status_t err = input->readStrongBinder(&surfaceControl);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = input->readInt64(&acquireTime);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ bool hasFence = false;
+ err = input->readBool(&hasFence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (hasFence) {
+ previousReleaseFence = new Fence();
+ err = input->read(*previousReleaseFence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t TransactionStats::writeToParcel(Parcel* output) const {
+ status_t err = output->writeInt64(latchTime);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (presentFence) {
+ err = output->writeBool(true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->write(*presentFence);
+ } else {
+ err = output->writeBool(false);
+ }
+ if (err != NO_ERROR) {
+ return err;
+ }
+ return output->writeParcelableVector(surfaceStats);
+}
+
+status_t TransactionStats::readFromParcel(const Parcel* input) {
+ status_t err = input->readInt64(&latchTime);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ bool hasFence = false;
+ err = input->readBool(&hasFence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (hasFence) {
+ presentFence = new Fence();
+ err = input->read(*presentFence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+ return input->readParcelableVector(&surfaceStats);
+}
+
+status_t ListenerStats::writeToParcel(Parcel* output) const {
+ status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size()));
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ for (const auto& [callbackIds, stats] : transactionStats) {
+ err = output->writeParcelable(stats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->writeInt64Vector(callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t ListenerStats::readFromParcel(const Parcel* input) {
+ int32_t transactionStats_size = input->readInt32();
+
+ for (int i = 0; i < transactionStats_size; i++) {
+ TransactionStats stats;
+ std::vector<CallbackId> callbackIds;
+
+ status_t err = input->readParcelable(&stats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = input->readInt64Vector(&callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transactionStats.emplace(callbackIds, stats);
+ }
+ return NO_ERROR;
+}
+
+ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+ const std::unordered_set<CallbackId>& callbackIds) {
+ ListenerStats listenerStats;
+ listenerStats.listener = listener;
+ TransactionStats transactionStats;
+ listenerStats.transactionStats.emplace(std::piecewise_construct,
+ std::forward_as_tuple(callbackIds.begin(),
+ callbackIds.end()),
+ std::forward_as_tuple(transactionStats));
+ return listenerStats;
+}
+
+class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
+public:
+ explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
+ : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
+ }
+
+ ~BpTransactionCompletedListener() override;
+
+ void onTransactionCompleted(ListenerStats stats) override {
+ callRemoteAsync<decltype(&ITransactionCompletedListener::
+ onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
+ stats);
+ }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
+
+IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
+
+status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_TRANSACTION_COMPLETED:
+ return callLocalAsync(data, reply,
+ &ITransactionCompletedListener::onTransactionCompleted);
+ }
+}
+
+}; // namespace android
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index d3dc16d..cdde9a2 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -16,13 +16,14 @@
#include <gui/LayerDebugInfo.h>
+#include <android-base/stringprintf.h>
+
#include <ui/DebugUtils.h>
#include <binder/Parcel.h>
-#include <utils/String8.h>
-
using namespace android;
+using android::base::StringAppendF;
#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)
@@ -42,7 +43,6 @@
RETURN_ON_ERROR(parcel->writeInt32(mWidth));
RETURN_ON_ERROR(parcel->writeInt32(mHeight));
RETURN_ON_ERROR(parcel->write(mCrop));
- RETURN_ON_ERROR(parcel->write(mFinalCrop));
RETURN_ON_ERROR(parcel->writeFloat(mColor.r));
RETURN_ON_ERROR(parcel->writeFloat(mColor.g));
RETURN_ON_ERROR(parcel->writeFloat(mColor.b));
@@ -81,7 +81,6 @@
RETURN_ON_ERROR(parcel->readInt32(&mWidth));
RETURN_ON_ERROR(parcel->readInt32(&mHeight));
RETURN_ON_ERROR(parcel->read(mCrop));
- RETURN_ON_ERROR(parcel->read(mFinalCrop));
mColor.r = parcel->readFloat();
RETURN_ON_ERROR(parcel->errorCheck());
mColor.g = parcel->readFloat();
@@ -110,39 +109,37 @@
}
std::string to_string(const LayerDebugInfo& info) {
- String8 result;
+ std::string result;
- result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
+ StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
info.mTransparentRegion.dump(result, "TransparentRegion");
info.mVisibleRegion.dump(result, "VisibleRegion");
info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
- result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
- info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY),
- info.mWidth, info.mHeight);
+ StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+ info.mLayerStack, info.mZ, static_cast<double>(info.mX),
+ static_cast<double>(info.mY), info.mWidth, info.mHeight);
- result.appendFormat("crop=%s, finalCrop=%s, ",
- to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str());
- result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
- result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
- result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
- result.appendFormat("color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
- static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
- static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
- info.mFlags);
- result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]",
- static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]),
- static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1]));
+ StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str());
+ StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
+ StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
+ StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
+ StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
+ static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
+ static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
+ info.mFlags);
+ StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(info.mMatrix[0][0]),
+ static_cast<double>(info.mMatrix[0][1]), static_cast<double>(info.mMatrix[1][0]),
+ static_cast<double>(info.mMatrix[1][1]));
result.append("\n");
- result.appendFormat(" parent=%s\n", info.mParentName.c_str());
- result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],",
- info.mActiveBufferWidth, info.mActiveBufferHeight,
- info.mActiveBufferStride,
- decodePixelFormat(info.mActiveBufferFormat).c_str());
- result.appendFormat(" queued-frames=%d, mRefreshPending=%d",
- info.mNumQueuedFrames, info.mRefreshPending);
+ StringAppendF(&result, " parent=%s\n", info.mParentName.c_str());
+ StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth,
+ info.mActiveBufferHeight, info.mActiveBufferStride,
+ decodePixelFormat(info.mActiveBufferFormat).c_str());
+ StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", info.mNumQueuedFrames,
+ info.mRefreshPending);
result.append("\n");
- return std::string(result.c_str());
+ return result;
}
} // android
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
new file mode 100644
index 0000000..04d2871
--- /dev/null
+++ b/libs/gui/LayerMetadata.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <gui/LayerMetadata.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+LayerMetadata::LayerMetadata() = default;
+
+LayerMetadata::LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map)
+ : mMap(std::move(map)) {}
+
+LayerMetadata::LayerMetadata(const LayerMetadata& other) = default;
+
+LayerMetadata::LayerMetadata(LayerMetadata&& other) = default;
+
+bool LayerMetadata::merge(const LayerMetadata& other, bool eraseEmpty) {
+ bool changed = false;
+ for (const auto& entry : other.mMap) {
+ auto it = mMap.find(entry.first);
+ if (it != mMap.cend() && it->second != entry.second) {
+ if (eraseEmpty && entry.second.empty()) {
+ mMap.erase(it);
+ } else {
+ it->second = entry.second;
+ }
+ changed = true;
+ } else if (it == mMap.cend() && !entry.second.empty()) {
+ mMap[entry.first] = entry.second;
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+status_t LayerMetadata::writeToParcel(Parcel* parcel) const {
+ parcel->writeInt32(static_cast<int>(mMap.size()));
+ status_t status = OK;
+ for (const auto& entry : mMap) {
+ status = parcel->writeUint32(entry.first);
+ if (status != OK) {
+ break;
+ }
+ status = parcel->writeByteVector(entry.second);
+ if (status != OK) {
+ break;
+ }
+ }
+ return status;
+}
+
+status_t LayerMetadata::readFromParcel(const Parcel* parcel) {
+ int size = parcel->readInt32();
+ status_t status = OK;
+ mMap.clear();
+ for (int i = 0; i < size; ++i) {
+ uint32_t key = parcel->readUint32();
+ status = parcel->readByteVector(&mMap[key]);
+ if (status != OK) {
+ break;
+ }
+ }
+ return status;
+}
+
+LayerMetadata& LayerMetadata::operator=(const LayerMetadata& other) {
+ mMap = other.mMap;
+ return *this;
+}
+
+LayerMetadata& LayerMetadata::operator=(LayerMetadata&& other) {
+ mMap = std::move(other.mMap);
+ return *this;
+}
+
+bool LayerMetadata::has(uint32_t key) const {
+ return mMap.count(key);
+}
+
+int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const {
+ if (!has(key)) return fallback;
+ const std::vector<uint8_t>& data = mMap.at(key);
+ if (data.size() < sizeof(uint32_t)) return fallback;
+ Parcel p;
+ p.setData(data.data(), data.size());
+ return p.readInt32();
+}
+
+void LayerMetadata::setInt32(uint32_t key, int32_t value) {
+ std::vector<uint8_t>& data = mMap[key];
+ Parcel p;
+ p.writeInt32(value);
+ data.resize(p.dataSize());
+ memcpy(data.data(), p.data(), p.dataSize());
+}
+
+std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
+ if (!has(key)) return std::string();
+ switch (key) {
+ case METADATA_OWNER_UID:
+ return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
+ case METADATA_WINDOW_TYPE:
+ return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
+ case METADATA_TASK_ID:
+ return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+ default:
+ return StringPrintf("%d%s%dbytes", key, separator,
+ static_cast<int>(mMap.at(key).size()));
+ }
+}
+
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 01acc2d..84ba644 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define LOG_TAG "LayerState"
+
+#include <inttypes.h>
+
#include <utils/Errors.h>
#include <binder/Parcel.h>
#include <gui/ISurfaceComposerClient.h>
@@ -25,7 +29,7 @@
status_t layer_state_t::write(Parcel& output) const
{
output.writeStrongBinder(surface);
- output.writeUint32(what);
+ output.writeUint64(what);
output.writeFloat(x);
output.writeFloat(y);
output.writeInt32(z);
@@ -37,26 +41,74 @@
output.writeUint32(mask);
*reinterpret_cast<layer_state_t::matrix22_t *>(
output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
- output.write(crop);
- output.write(finalCrop);
- output.writeStrongBinder(barrierHandle);
+ output.write(crop_legacy);
+ output.writeStrongBinder(barrierHandle_legacy);
output.writeStrongBinder(reparentHandle);
- output.writeUint64(frameNumber);
+ output.writeUint64(frameNumber_legacy);
output.writeInt32(overrideScalingMode);
- output.writeStrongBinder(IInterface::asBinder(barrierGbp));
+ output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy));
output.writeStrongBinder(relativeLayerHandle);
output.writeStrongBinder(parentHandleForChild);
output.writeFloat(color.r);
output.writeFloat(color.g);
output.writeFloat(color.b);
+#ifndef NO_INPUT
+ inputInfo.write(output);
+#endif
output.write(transparentRegion);
+ output.writeUint32(transform);
+ output.writeBool(transformToDisplayInverse);
+ output.write(crop);
+ output.write(frame);
+ if (buffer) {
+ output.writeBool(true);
+ output.write(*buffer);
+ } else {
+ output.writeBool(false);
+ }
+ if (acquireFence) {
+ output.writeBool(true);
+ output.write(*acquireFence);
+ } else {
+ output.writeBool(false);
+ }
+ output.writeUint32(static_cast<uint32_t>(dataspace));
+ output.write(hdrMetadata);
+ output.write(surfaceDamageRegion);
+ output.writeInt32(api);
+ if (sidebandStream) {
+ output.writeBool(true);
+ output.writeNativeHandle(sidebandStream->handle());
+ } else {
+ output.writeBool(false);
+ }
+
+ memcpy(output.writeInplace(16 * sizeof(float)),
+ colorTransform.asArray(), 16 * sizeof(float));
+ output.writeFloat(cornerRadius);
+
+ if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ output.writeStrongBinder(IInterface::asBinder(listener));
+ output.writeInt64Vector(callbackIds);
+ }
+ }
+
+ output.writeStrongBinder(cachedBuffer.token);
+ output.writeInt32(cachedBuffer.bufferId);
+ output.writeParcelable(metadata);
+
+ output.writeFloat(bgColorAlpha);
+ output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
+ output.writeBool(colorSpaceAgnostic);
+
return NO_ERROR;
}
status_t layer_state_t::read(const Parcel& input)
{
surface = input.readStrongBinder();
- what = input.readUint32();
+ what = input.readUint64();
x = input.readFloat();
y = input.readFloat();
z = input.readInt32();
@@ -72,20 +124,62 @@
} else {
return BAD_VALUE;
}
- input.read(crop);
- input.read(finalCrop);
- barrierHandle = input.readStrongBinder();
+ input.read(crop_legacy);
+ barrierHandle_legacy = input.readStrongBinder();
reparentHandle = input.readStrongBinder();
- frameNumber = input.readUint64();
+ frameNumber_legacy = input.readUint64();
overrideScalingMode = input.readInt32();
- barrierGbp =
- interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
+ barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
relativeLayerHandle = input.readStrongBinder();
parentHandleForChild = input.readStrongBinder();
color.r = input.readFloat();
color.g = input.readFloat();
color.b = input.readFloat();
+
+#ifndef NO_INPUT
+ inputInfo = InputWindowInfo::read(input);
+#endif
+
input.read(transparentRegion);
+ transform = input.readUint32();
+ transformToDisplayInverse = input.readBool();
+ input.read(crop);
+ input.read(frame);
+ buffer = new GraphicBuffer();
+ if (input.readBool()) {
+ input.read(*buffer);
+ }
+ acquireFence = new Fence();
+ if (input.readBool()) {
+ input.read(*acquireFence);
+ }
+ dataspace = static_cast<ui::Dataspace>(input.readUint32());
+ input.read(hdrMetadata);
+ input.read(surfaceDamageRegion);
+ api = input.readInt32();
+ if (input.readBool()) {
+ sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
+ }
+
+ colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
+ cornerRadius = input.readFloat();
+
+ int32_t listenersSize = input.readInt32();
+ for (int32_t i = 0; i < listenersSize; i++) {
+ auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder());
+ std::vector<CallbackId> callbackIds;
+ input.readInt64Vector(&callbackIds);
+ listenerCallbacks.emplace_back(listener, callbackIds);
+ }
+
+ cachedBuffer.token = input.readStrongBinder();
+ cachedBuffer.bufferId = input.readInt32();
+ input.readParcelable(&metadata);
+
+ bgColorAlpha = input.readFloat();
+ bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
+ colorSpaceAgnostic = input.readBool();
+
return NO_ERROR;
}
@@ -194,19 +288,19 @@
what |= eLayerStackChanged;
layerStack = other.layerStack;
}
- if (other.what & eCropChanged) {
- what |= eCropChanged;
- crop = other.crop;
+ if (other.what & eCropChanged_legacy) {
+ what |= eCropChanged_legacy;
+ crop_legacy = other.crop_legacy;
}
- if (other.what & eDeferTransaction) {
- what |= eDeferTransaction;
- barrierHandle = other.barrierHandle;
- barrierGbp = other.barrierGbp;
- frameNumber = other.frameNumber;
+ if (other.what & eCornerRadiusChanged) {
+ what |= eCornerRadiusChanged;
+ cornerRadius = other.cornerRadius;
}
- if (other.what & eFinalCropChanged) {
- what |= eFinalCropChanged;
- finalCrop = other.finalCrop;
+ if (other.what & eDeferTransaction_legacy) {
+ what |= eDeferTransaction_legacy;
+ barrierHandle_legacy = other.barrierHandle_legacy;
+ barrierGbp_legacy = other.barrierGbp_legacy;
+ frameNumber_legacy = other.frameNumber_legacy;
}
if (other.what & eOverrideScalingModeChanged) {
what |= eOverrideScalingModeChanged;
@@ -234,6 +328,124 @@
if (other.what & eDestroySurface) {
what |= eDestroySurface;
}
+ if (other.what & eTransformChanged) {
+ what |= eTransformChanged;
+ transform = other.transform;
+ }
+ if (other.what & eTransformToDisplayInverseChanged) {
+ what |= eTransformToDisplayInverseChanged;
+ transformToDisplayInverse = other.transformToDisplayInverse;
+ }
+ if (other.what & eCropChanged) {
+ what |= eCropChanged;
+ crop = other.crop;
+ }
+ if (other.what & eFrameChanged) {
+ what |= eFrameChanged;
+ frame = other.frame;
+ }
+ if (other.what & eBufferChanged) {
+ what |= eBufferChanged;
+ buffer = other.buffer;
+ }
+ if (other.what & eAcquireFenceChanged) {
+ what |= eAcquireFenceChanged;
+ acquireFence = other.acquireFence;
+ }
+ if (other.what & eDataspaceChanged) {
+ what |= eDataspaceChanged;
+ dataspace = other.dataspace;
+ }
+ if (other.what & eHdrMetadataChanged) {
+ what |= eHdrMetadataChanged;
+ hdrMetadata = other.hdrMetadata;
+ }
+ if (other.what & eSurfaceDamageRegionChanged) {
+ what |= eSurfaceDamageRegionChanged;
+ surfaceDamageRegion = other.surfaceDamageRegion;
+ }
+ if (other.what & eApiChanged) {
+ what |= eApiChanged;
+ api = other.api;
+ }
+ if (other.what & eSidebandStreamChanged) {
+ what |= eSidebandStreamChanged;
+ sidebandStream = other.sidebandStream;
+ }
+ if (other.what & eColorTransformChanged) {
+ what |= eColorTransformChanged;
+ colorTransform = other.colorTransform;
+ }
+ if (other.what & eListenerCallbacksChanged) {
+ what |= eListenerCallbacksChanged;
+ listenerCallbacks = other.listenerCallbacks;
+ }
+
+#ifndef NO_INPUT
+ if (other.what & eInputInfoChanged) {
+ what |= eInputInfoChanged;
+ inputInfo = other.inputInfo;
+ }
+#endif
+
+ if (other.what & eCachedBufferChanged) {
+ what |= eCachedBufferChanged;
+ cachedBuffer = other.cachedBuffer;
+ }
+ if (other.what & eBackgroundColorChanged) {
+ what |= eBackgroundColorChanged;
+ color = other.color;
+ bgColorAlpha = other.bgColorAlpha;
+ bgColorDataspace = other.bgColorDataspace;
+ }
+ if (other.what & eMetadataChanged) {
+ what |= eMetadataChanged;
+ metadata.merge(other.metadata);
+ }
+ if ((other.what & what) != other.what) {
+ ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
+ "other.what=0x%" PRIu64 " what=0x%" PRIu64,
+ other.what, what);
+ }
+}
+
+// ------------------------------- InputWindowCommands ----------------------------------------
+
+void InputWindowCommands::merge(const InputWindowCommands& other) {
+ transferTouchFocusCommands
+ .insert(transferTouchFocusCommands.end(),
+ std::make_move_iterator(other.transferTouchFocusCommands.begin()),
+ std::make_move_iterator(other.transferTouchFocusCommands.end()));
+
+ syncInputWindows |= other.syncInputWindows;
+}
+
+void InputWindowCommands::clear() {
+ transferTouchFocusCommands.clear();
+ syncInputWindows = false;
+}
+
+void InputWindowCommands::write(Parcel& output) const {
+ output.writeUint32(static_cast<uint32_t>(transferTouchFocusCommands.size()));
+ for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) {
+ output.writeStrongBinder(transferTouchFocusCommand.fromToken);
+ output.writeStrongBinder(transferTouchFocusCommand.toToken);
+ }
+
+ output.writeBool(syncInputWindows);
+}
+
+void InputWindowCommands::read(const Parcel& input) {
+ size_t count = input.readUint32();
+ transferTouchFocusCommands.clear();
+ for (size_t i = 0; i < count; i++) {
+ TransferTouchFocusCommand transferTouchFocusCommand;
+ transferTouchFocusCommand.fromToken = input.readStrongBinder();
+ transferTouchFocusCommand.toToken = input.readStrongBinder();
+ transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
+ }
+
+ syncInputWindows = input.readBool();
}
}; // namespace android
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
index 52c9067..2f8e104 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -38,11 +38,11 @@
status_t StreamSplitter::createSplitter(
const sp<IGraphicBufferConsumer>& inputQueue,
sp<StreamSplitter>* outSplitter) {
- if (inputQueue == NULL) {
+ if (inputQueue == nullptr) {
ALOGE("createSplitter: inputQueue must not be NULL");
return BAD_VALUE;
}
- if (outSplitter == NULL) {
+ if (outSplitter == nullptr) {
ALOGE("createSplitter: outSplitter must not be NULL");
return BAD_VALUE;
}
@@ -74,7 +74,7 @@
status_t StreamSplitter::addOutput(
const sp<IGraphicBufferProducer>& outputQueue) {
- if (outputQueue == NULL) {
+ if (outputQueue == nullptr) {
ALOGE("addOutput: outputQueue must not be NULL");
return BAD_VALUE;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2de14c8..93b4191 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -20,6 +20,11 @@
#include <gui/Surface.h>
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <thread>
+
#include <inttypes.h>
#include <android/native_window.h>
@@ -39,9 +44,6 @@
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-
namespace android {
using ui::ColorMode;
@@ -156,7 +158,7 @@
ATRACE_CALL();
DisplayStatInfo stats;
- status_t result = composerService()->getDisplayStats(NULL, &stats);
+ status_t result = composerService()->getDisplayStats(nullptr, &stats);
if (result != NO_ERROR) {
return result;
}
@@ -321,48 +323,27 @@
return NO_ERROR;
}
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-
status_t Surface::getWideColorSupport(bool* supported) {
ATRACE_CALL();
- sp<IBinder> display(
- composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
- Vector<ColorMode> colorModes;
- status_t err =
- composerService()->getDisplayColorModes(display, &colorModes);
-
- if (err)
- return err;
-
- bool wideColorBoardConfig =
- getBool<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
-
- *supported = false;
- for (ColorMode colorMode : colorModes) {
- switch (colorMode) {
- case ColorMode::DISPLAY_P3:
- case ColorMode::ADOBE_RGB:
- case ColorMode::DCI_P3:
- if (wideColorBoardConfig) {
- *supported = true;
- }
- break;
- default:
- break;
- }
+ const sp<IBinder> display = composerService()->getInternalDisplayToken();
+ if (display == nullptr) {
+ return NAME_NOT_FOUND;
}
- return NO_ERROR;
+ *supported = false;
+ status_t error = composerService()->isWideColorDisplay(display, supported);
+ return error;
}
status_t Surface::getHdrSupport(bool* supported) {
ATRACE_CALL();
- sp<IBinder> display(
- composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const sp<IBinder> display = composerService()->getInternalDisplayToken();
+ if (display == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
HdrCapabilities hdrCapabilities;
status_t err =
composerService()->getHdrCapabilities(display, &hdrCapabilities);
@@ -474,6 +455,82 @@
return NO_ERROR;
}
+class FenceMonitor {
+public:
+ explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) {
+ std::thread thread(&FenceMonitor::loop, this);
+ pthread_setname_np(thread.native_handle(), mName);
+ thread.detach();
+ }
+
+ void queueFence(const sp<Fence>& fence) {
+ char message[64];
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued);
+ ATRACE_NAME(message);
+ // Need an increment on both to make the trace number correct.
+ mFencesQueued++;
+ mFencesSignaled++;
+ return;
+ }
+ snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued);
+ ATRACE_NAME(message);
+
+ mQueue.push_back(fence);
+ mCondition.notify_one();
+ mFencesQueued++;
+ ATRACE_INT(mName, int32_t(mQueue.size()));
+ }
+
+private:
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+ void loop() {
+ while (true) {
+ threadLoop();
+ }
+ }
+#pragma clang diagnostic pop
+
+ void threadLoop() {
+ sp<Fence> fence;
+ uint32_t fenceNum;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+ fence = mQueue[0];
+ fenceNum = mFencesSignaled;
+ }
+ {
+ char message[64];
+ snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum);
+ ATRACE_NAME(message);
+
+ status_t result = fence->waitForever(message);
+ if (result != OK) {
+ ALOGE("Error waiting for fence: %d", result);
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.pop_front();
+ mFencesSignaled++;
+ ATRACE_INT(mName, int32_t(mQueue.size()));
+ }
+ }
+
+ const char* mName;
+ uint32_t mFencesQueued;
+ uint32_t mFencesSignaled;
+ std::deque<sp<Fence>> mQueue;
+ std::condition_variable mCondition;
+ std::mutex mMutex;
+};
+
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("Surface::dequeueBuffer");
@@ -501,7 +558,7 @@
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
- if (gbuf != NULL) {
+ if (gbuf != nullptr) {
*buffer = gbuf.get();
*fenceFd = -1;
return OK;
@@ -541,7 +598,12 @@
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
- ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
+ ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
+
+ if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+ static FenceMonitor hwcReleaseThread("HWC release");
+ hwcReleaseThread.queueFence(fence);
+ }
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
@@ -619,7 +681,7 @@
int Surface::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- if (mSlots[i].buffer != NULL &&
+ if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
}
@@ -754,7 +816,7 @@
// The consumer doesn't send it back to prevent us from having two
// file descriptors of the same fence.
mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
- std::make_shared<FenceTime>(std::move(fence)));
+ std::make_shared<FenceTime>(fence));
// Cache timestamps of signaled fences so we can close their file
// descriptors.
@@ -785,6 +847,11 @@
mQueueBufferCondition.broadcast();
+ if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+ static FenceMonitor gpuCompletionThread("GPU completion");
+ gpuCompletionThread.queueFence(fence);
+ }
+
return err;
}
@@ -965,6 +1032,9 @@
case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA:
res = dispatchSetBuffersCta8613Metadata(args);
break;
+ case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA:
+ res = dispatchSetBuffersHdr10PlusMetadata(args);
+ break;
case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
res = dispatchSetSurfaceDamage(args);
break;
@@ -1120,6 +1190,12 @@
return setBuffersCta8613Metadata(metadata);
}
+int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) {
+ const size_t size = va_arg(args, size_t);
+ const uint8_t* metadata = va_arg(args, const uint8_t*);
+ return setBuffersHdr10PlusMetadata(size, metadata);
+}
+
int Surface::dispatchSetSurfaceDamage(va_list args) {
android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
size_t numRects = va_arg(args, size_t);
@@ -1268,7 +1344,7 @@
ATRACE_CALL();
ALOGV("Surface::detachNextBuffer");
- if (outBuffer == NULL || outFence == NULL) {
+ if (outBuffer == nullptr || outFence == nullptr) {
return BAD_VALUE;
}
@@ -1277,8 +1353,8 @@
mRemovedBuffers.clear();
}
- sp<GraphicBuffer> buffer(NULL);
- sp<Fence> fence(NULL);
+ sp<GraphicBuffer> buffer(nullptr);
+ sp<Fence> fence(nullptr);
status_t result = mGraphicBufferProducer->detachNextBuffer(
&buffer, &fence);
if (result != NO_ERROR) {
@@ -1286,19 +1362,19 @@
}
*outBuffer = buffer;
- if (fence != NULL && fence->isValid()) {
+ if (fence != nullptr && fence->isValid()) {
*outFence = fence;
} else {
*outFence = Fence::NO_FENCE;
}
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- if (mSlots[i].buffer != NULL &&
+ if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->getId() == buffer->getId()) {
if (mReportRemovedBuffers) {
mRemovedBuffers.push_back(mSlots[i].buffer);
}
- mSlots[i].buffer = NULL;
+ mSlots[i].buffer = nullptr;
}
}
@@ -1349,7 +1425,7 @@
ATRACE_CALL();
Rect realRect(Rect::EMPTY_RECT);
- if (rect == NULL || rect->isEmpty()) {
+ if (rect == nullptr || rect->isEmpty()) {
realRect.clear();
} else {
realRect = *rect;
@@ -1568,6 +1644,19 @@
return NO_ERROR;
}
+int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
+ ALOGV("Surface::setBuffersBlobMetadata");
+ Mutex::Autolock lock(mMutex);
+ if (size > 0) {
+ mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
+ mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
+ } else {
+ mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS;
+ mHdrMetadata.hdr10plus.clear();
+ }
+ return NO_ERROR;
+}
+
Dataspace Surface::getBuffersDataSpace() {
ALOGV("Surface::getBuffersDataSpace");
Mutex::Autolock lock(mMutex);
@@ -1576,7 +1665,7 @@
void Surface::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- mSlots[i].buffer = 0;
+ mSlots[i].buffer = nullptr;
}
}
@@ -1616,12 +1705,12 @@
// src and dst with, height and format must be identical. no verification
// is done here.
status_t err;
- uint8_t* src_bits = NULL;
+ uint8_t* src_bits = nullptr;
err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(),
reinterpret_cast<void**>(&src_bits));
ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
- uint8_t* dst_bits = NULL;
+ uint8_t* dst_bits = nullptr;
err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
reinterpret_cast<void**>(&dst_bits), *dstFenceFd);
ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
@@ -1669,7 +1758,7 @@
status_t Surface::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
- if (mLockedBuffer != 0) {
+ if (mLockedBuffer != nullptr) {
ALOGE("Surface::lock failed, already locked");
return INVALID_OPERATION;
}
@@ -1701,7 +1790,7 @@
// figure out if we can copy the frontbuffer back
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
- const bool canCopyBack = (frontBuffer != 0 &&
+ const bool canCopyBack = (frontBuffer != nullptr &&
backBuffer->width == frontBuffer->width &&
backBuffer->height == frontBuffer->height &&
backBuffer->format == frontBuffer->format);
@@ -1763,7 +1852,7 @@
status_t Surface::unlockAndPost()
{
- if (mLockedBuffer == 0) {
+ if (mLockedBuffer == nullptr) {
ALOGE("Surface::unlockAndPost failed, no locked buffer");
return INVALID_OPERATION;
}
@@ -1777,7 +1866,7 @@
mLockedBuffer->handle, strerror(-err));
mPostedBuffer = mLockedBuffer;
- mLockedBuffer = 0;
+ mLockedBuffer = nullptr;
return err;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f3c6fd2..b0e8275 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -25,7 +25,9 @@
#include <utils/String8.h>
#include <utils/threads.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <system/graphics.h>
@@ -40,8 +42,15 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#ifndef NO_INPUT
+#include <input/InputWindow.h>
+#endif
+
#include <private/gui/ComposerService.h>
+// This server size should always be smaller than the server cache size
+#define BUFFER_CACHE_MAX_SIZE 64
+
namespace android {
using ui::ColorMode;
@@ -60,7 +69,7 @@
while (getService(name, &mComposerService) != NO_ERROR) {
usleep(250000);
}
- assert(mComposerService != NULL);
+ assert(mComposerService != nullptr);
// Create the death listener.
class DeathObserver : public IBinder::DeathRecipient {
@@ -81,9 +90,9 @@
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
ComposerService& instance = ComposerService::getInstance();
Mutex::Autolock _l(instance.mLock);
- if (instance.mComposerService == NULL) {
+ if (instance.mComposerService == nullptr) {
ComposerService::getInstance().connectLocked();
- assert(instance.mComposerService != NULL);
+ assert(instance.mComposerService != nullptr);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
@@ -92,19 +101,254 @@
void ComposerService::composerServiceDied()
{
Mutex::Autolock _l(mLock);
- mComposerService = NULL;
- mDeathObserver = NULL;
+ mComposerService = nullptr;
+ mDeathObserver = nullptr;
+}
+
+class DefaultComposerClient: public Singleton<DefaultComposerClient> {
+ Mutex mLock;
+ sp<SurfaceComposerClient> mClient;
+ friend class Singleton<ComposerService>;
+public:
+ static sp<SurfaceComposerClient> getComposerClient() {
+ DefaultComposerClient& dc = DefaultComposerClient::getInstance();
+ Mutex::Autolock _l(dc.mLock);
+ if (dc.mClient == nullptr) {
+ dc.mClient = new SurfaceComposerClient;
+ }
+ return dc.mClient;
+ }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(DefaultComposerClient);
+
+
+sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() {
+ return DefaultComposerClient::getComposerClient();
}
// ---------------------------------------------------------------------------
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
- mForceSynchronous(other.mForceSynchronous),
- mTransactionNestCount(other.mTransactionNestCount),
- mAnimation(other.mAnimation),
- mEarlyWakeup(other.mEarlyWakeup) {
+// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
+// to be able to return a sp<> to its instance to pass to SurfaceFlinger.
+// ANDROID_SINGLETON_STATIC_INSTANCE only allows a reference to an instance.
+
+// 0 is an invalid callback id
+TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
+
+CallbackId TransactionCompletedListener::getNextIdLocked() {
+ return mCallbackIdCounter++;
+}
+
+sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
+ static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener;
+ return sInstance;
+}
+
+sp<ITransactionCompletedListener> TransactionCompletedListener::getIInstance() {
+ return static_cast<sp<ITransactionCompletedListener>>(getInstance());
+}
+
+void TransactionCompletedListener::startListeningLocked() {
+ if (mListening) {
+ return;
+ }
+ ProcessState::self()->startThreadPool();
+ mListening = true;
+}
+
+CallbackId TransactionCompletedListener::addCallbackFunction(
+ const TransactionCompletedCallback& callbackFunction,
+ const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+ surfaceControls) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ startListeningLocked();
+
+ CallbackId callbackId = getNextIdLocked();
+ mCallbacks[callbackId].callbackFunction = callbackFunction;
+
+ auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
+
+ for (const auto& surfaceControl : surfaceControls) {
+ callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
+ }
+
+ return callbackId;
+}
+
+void TransactionCompletedListener::addSurfaceControlToCallbacks(
+ const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId>& callbackIds) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ for (auto callbackId : callbackIds) {
+ mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
+ std::forward_as_tuple(
+ surfaceControl->getHandle()),
+ std::forward_as_tuple(surfaceControl));
+ }
+}
+
+void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
+ * callbackIds, except for when Transactions are merged together. This probably cannot be
+ * solved before this point because the Transactions could be merged together and applied in a
+ * different process.
+ *
+ * Fortunately, we get all the callbacks for this listener for the same frame together at the
+ * same time. This means if any Transactions were merged together, we will get their callbacks
+ * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the
+ * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
+ * that could possibly exist for the callbacks.
+ */
+ std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+ for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+ for (auto callbackId : callbackIds) {
+ auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+ }
+ }
+
+ for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+ for (auto callbackId : callbackIds) {
+ auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ if (!callbackFunction) {
+ ALOGE("cannot call null callback function, skipping");
+ continue;
+ }
+ std::vector<SurfaceControlStats> surfaceControlStats;
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
+ surfaceStats.acquireTime,
+ surfaceStats.previousReleaseFence);
+ }
+
+ callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+ surfaceControlStats);
+ mCallbacks.erase(callbackId);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+class BufferCache : public Singleton<BufferCache> {
+public:
+ BufferCache() : token(new BBinder()) {}
+
+ sp<IBinder> getToken() {
+ return IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ }
+
+ int32_t getId(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ auto itr = mBuffers.find(buffer);
+ if (itr == mBuffers.end()) {
+ return -1;
+ }
+ itr->second.counter = getCounter();
+ return itr->second.id;
+ }
+
+ int32_t cache(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ int32_t bufferId = getNextAvailableId();
+
+ mBuffers[buffer].id = bufferId;
+ mBuffers[buffer].counter = getCounter();
+ return bufferId;
+ }
+
+private:
+ int32_t evictDestroyedBuffer() REQUIRES(mMutex) {
+ auto itr = mBuffers.begin();
+ while (itr != mBuffers.end()) {
+ auto& buffer = itr->first;
+ if (buffer == nullptr || buffer.promote() == nullptr) {
+ int32_t bufferId = itr->second.id;
+ mBuffers.erase(itr);
+ return bufferId;
+ }
+ itr++;
+ }
+ return -1;
+ }
+
+ int32_t evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+ if (mBuffers.size() < 0) {
+ return -1;
+ }
+ auto itr = mBuffers.begin();
+ uint64_t minCounter = itr->second.counter;
+ auto minBuffer = itr;
+ itr++;
+
+ while (itr != mBuffers.end()) {
+ uint64_t counter = itr->second.counter;
+ if (counter < minCounter) {
+ minCounter = counter;
+ minBuffer = itr;
+ }
+ itr++;
+ }
+ int32_t minBufferId = minBuffer->second.id;
+ mBuffers.erase(minBuffer);
+ return minBufferId;
+ }
+
+ int32_t getNextAvailableId() REQUIRES(mMutex) {
+ static int32_t id = 0;
+ if (id + 1 < BUFFER_CACHE_MAX_SIZE) {
+ return id++;
+ }
+
+ // There are no more valid cache ids. To set additional buffers, evict existing buffers
+ // and reuse their cache ids.
+ int32_t bufferId = evictDestroyedBuffer();
+ if (bufferId > 0) {
+ return bufferId;
+ }
+ return evictLeastRecentlyUsedBuffer();
+ }
+
+ uint64_t getCounter() REQUIRES(mMutex) {
+ static uint64_t counter = 0;
+ return counter++;
+ }
+
+ struct Metadata {
+ // The cache id of a buffer that can be set to ISurfaceComposer. When ISurfaceComposer
+ // recieves this id, it can retrieve the buffer from its cache. Caching GraphicBuffers
+ // is important because sending them across processes is expensive.
+ int32_t id = 0;
+ // When a buffer is set, a counter is incremented and stored in the cache's metadata.
+ // When an buffer must be evicted, the entry with the lowest counter value is chosen.
+ uint64_t counter = 0;
+ };
+
+ std::mutex mMutex;
+ std::map<wp<GraphicBuffer>, Metadata> mBuffers GUARDED_BY(mMutex);
+
+ // Used by ISurfaceComposer to identify which process is sending the cached buffer.
+ sp<IBinder> token;
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache);
+
+// ---------------------------------------------------------------------------
+
+SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
+ : mForceSynchronous(other.mForceSynchronous),
+ mTransactionNestCount(other.mTransactionNestCount),
+ mAnimation(other.mAnimation),
+ mEarlyWakeup(other.mEarlyWakeup),
+ mDesiredPresentTime(other.mDesiredPresentTime) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
+ mInputWindowCommands = other.mInputWindowCommands;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
@@ -127,9 +371,39 @@
}
other.mDisplayStates.clear();
+ for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
+ auto& [callbackIds, surfaceControls] = callbackInfo;
+ mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
+ callbackIds.begin()),
+ std::make_move_iterator(callbackIds.end()));
+ mListenerCallbacks[listener]
+ .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
+ std::make_move_iterator(surfaceControls.end()));
+ }
+ other.mListenerCallbacks.clear();
+
+ mInputWindowCommands.merge(other.mInputWindowCommands);
+ other.mInputWindowCommands.clear();
+
return *this;
}
+void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle,
+ const sp<ISurfaceComposerClient>& client) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ Vector<ComposerState> composerStates;
+ Vector<DisplayState> displayStates;
+
+ ComposerState s;
+ s.client = client;
+ s.state.surface = handle;
+ s.state.what |= layer_state_t::eReparent;
+ s.state.parentHandleForChild = nullptr;
+
+ composerStates.add(s);
+ sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1);
+}
+
status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
if (mStatus != NO_ERROR) {
return mStatus;
@@ -137,6 +411,32 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ // For every listener with registered callbacks
+ for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
+ auto& [callbackIds, surfaceControls] = callbackInfo;
+ if (callbackIds.empty()) {
+ continue;
+ }
+
+ // If the listener does not have any SurfaceControls set on this Transaction, send the
+ // callback now
+ if (surfaceControls.empty()) {
+ listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds));
+ }
+
+ // If the listener has any SurfaceControls set on this Transaction update the surface state
+ for (const auto& surfaceControl : surfaceControls) {
+ layer_state_t* s = getLayerState(surfaceControl);
+ if (!s) {
+ ALOGE("failed to get layer state");
+ continue;
+ }
+ s->what |= layer_state_t::eListenerCallbacksChanged;
+ s->listenerCallbacks.emplace_back(listener, std::move(callbackIds));
+ }
+ }
+ mListenerCallbacks.clear();
+
Vector<ComposerState> composerStates;
Vector<DisplayState> displayStates;
uint32_t flags = 0;
@@ -166,7 +466,10 @@
mAnimation = false;
mEarlyWakeup = false;
- sf->setTransactionState(composerStates, displayStates, flags);
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
+ mDesiredPresentTime);
+ mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
}
@@ -182,8 +485,20 @@
return ComposerService::getComposerService()->destroyDisplay(display);
}
-sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) {
- return ComposerService::getComposerService()->getBuiltInDisplay(id);
+std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() {
+ return ComposerService::getComposerService()->getPhysicalDisplayIds();
+}
+
+std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
+ return ComposerService::getComposerService()->getInternalDisplayId();
+}
+
+sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) {
+ return ComposerService::getComposerService()->getPhysicalDisplayToken(displayId);
+}
+
+sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() {
+ return ComposerService::getComposerService()->getInternalDisplayToken();
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
@@ -206,6 +521,15 @@
return &(mComposerStates[sc].state);
}
+void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
+ const sp<SurfaceControl>& sc) {
+ auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+ callbackInfo.surfaceControls.insert(sc);
+
+ TransactionCompletedListener::getInstance()
+ ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
const sp<SurfaceControl>& sc, float x, float y) {
layer_state_t* s = getLayerState(sc);
@@ -216,6 +540,8 @@
s->what |= layer_state_t::ePositionChanged;
s->x = x;
s->y = y;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -240,9 +566,7 @@
s->w = w;
s->h = h;
- // Resizing a surface makes the transaction synchronous.
- mForceSynchronous = true;
-
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -255,6 +579,8 @@
}
s->what |= layer_state_t::eLayerChanged;
s->z = z;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -267,6 +593,8 @@
s->what |= layer_state_t::eRelativeLayerChanged;
s->relativeLayerHandle = relativeTo;
s->z = z;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -286,6 +614,8 @@
s->flags &= ~mask;
s->flags |= (flags & mask);
s->mask |= mask;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -299,6 +629,8 @@
}
s->what |= layer_state_t::eTransparentRegionChanged;
s->transparentRegion = transparentRegion;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -311,6 +643,8 @@
}
s->what |= layer_state_t::eAlphaChanged;
s->alpha = alpha;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -323,6 +657,22 @@
}
s->what |= layer_state_t::eLayerStackChanged;
s->layerStack = layerStack;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata(
+ const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eMetadataChanged;
+ s->metadata.mMap[key] = std::move(data);
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -341,57 +691,68 @@
matrix.dsdy = dsdy;
matrix.dtdy = dtdy;
s->matrix = matrix;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy(
const sp<SurfaceControl>& sc, const Rect& crop) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eCropChanged;
- s->crop = crop;
+ s->what |= layer_state_t::eCropChanged_legacy;
+ s->crop_legacy = crop;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCornerRadius(
+ const sp<SurfaceControl>& sc, float cornerRadius) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eFinalCropChanged;
- s->finalCrop = crop;
+ s->what |= layer_state_t::eCornerRadiusChanged;
+ s->cornerRadius = cornerRadius;
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil(
- const sp<SurfaceControl>& sc,
- const sp<IBinder>& handle, uint64_t frameNumber) {
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+ const sp<IBinder>& handle,
+ uint64_t frameNumber) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eDeferTransaction;
- s->barrierHandle = handle;
- s->frameNumber = frameNumber;
+ s->what |= layer_state_t::eDeferTransaction_legacy;
+ s->barrierHandle_legacy = handle;
+ s->frameNumber_legacy = frameNumber;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil(
- const sp<SurfaceControl>& sc,
- const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+ const sp<Surface>& barrierSurface,
+ uint64_t frameNumber) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eDeferTransaction;
- s->barrierGbp = barrierSurface->getIGraphicBufferProducer();
- s->frameNumber = frameNumber;
+ s->what |= layer_state_t::eDeferTransaction_legacy;
+ s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
+ s->frameNumber_legacy = frameNumber;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -405,6 +766,8 @@
}
s->what |= layer_state_t::eReparentChildren;
s->reparentHandle = newParentHandle;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -418,6 +781,8 @@
}
s->what |= layer_state_t::eReparent;
s->parentHandleForChild = newParentHandle;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -431,6 +796,227 @@
}
s->what |= layer_state_t::eColorChanged;
s->color = color;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundColor(
+ const sp<SurfaceControl>& sc, const half3& color, float alpha, ui::Dataspace dataspace) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBackgroundColorChanged;
+ s->color = color;
+ s->bgColorAlpha = alpha;
+ s->bgColorDataspace = dataspace;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform(
+ const sp<SurfaceControl>& sc, uint32_t transform) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eTransformChanged;
+ s->transform = transform;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
+ bool transformToDisplayInverse) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eTransformToDisplayInverseChanged;
+ s->transformToDisplayInverse = transformToDisplayInverse;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
+ const sp<SurfaceControl>& sc, const Rect& crop) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eCropChanged;
+ s->crop = crop;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
+ const sp<SurfaceControl>& sc, const Rect& frame) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eFrameChanged;
+ s->frame = frame;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
+ const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ int32_t bufferId = BufferCache::getInstance().getId(buffer);
+ if (bufferId < 0) {
+ bufferId = BufferCache::getInstance().cache(buffer);
+
+ s->what |= layer_state_t::eBufferChanged;
+ s->buffer = buffer;
+ }
+
+ s->what |= layer_state_t::eCachedBufferChanged;
+ s->cachedBuffer.token = BufferCache::getInstance().getToken();
+ s->cachedBuffer.bufferId = bufferId;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
+ const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eAcquireFenceChanged;
+ s->acquireFence = fence;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
+ const sp<SurfaceControl>& sc, ui::Dataspace dataspace) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eDataspaceChanged;
+ s->dataspace = dataspace;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata(
+ const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eHdrMetadataChanged;
+ s->hdrMetadata = hdrMetadata;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfaceDamageRegion(
+ const sp<SurfaceControl>& sc, const Region& surfaceDamageRegion) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eSurfaceDamageRegionChanged;
+ s->surfaceDamageRegion = surfaceDamageRegion;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApi(
+ const sp<SurfaceControl>& sc, int32_t api) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eApiChanged;
+ s->api = api;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSidebandStream(
+ const sp<SurfaceControl>& sc, const sp<NativeHandle>& sidebandStream) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eSidebandStreamChanged;
+ s->sidebandStream = sidebandStream;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
+ nsecs_t desiredPresentTime) {
+ mDesiredPresentTime = desiredPresentTime;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorSpaceAgnostic(
+ const sp<SurfaceControl>& sc, const bool agnostic) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eColorSpaceAgnosticChanged;
+ s->colorSpaceAgnostic = agnostic;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+ auto listener = TransactionCompletedListener::getInstance();
+
+ auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
+ const auto& surfaceControls =
+ mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
+
+ CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
+
+ mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
+ callbackId);
return *this;
}
@@ -441,6 +1027,8 @@
mStatus = BAD_INDEX;
}
s->what |= layer_state_t::eDetachChildren;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -468,6 +1056,8 @@
s->what |= layer_state_t::eOverrideScalingModeChanged;
s->overrideScalingMode = overrideScalingMode;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -479,17 +1069,100 @@
return *this;
}
s->what |= layer_state_t::eGeometryAppliesWithResize;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroySurface(
- const sp<SurfaceControl>& sc) {
+#ifndef NO_INPUT
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
+ const sp<SurfaceControl>& sc,
+ const InputWindowInfo& info) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eDestroySurface;
+ s->inputInfo = info;
+ s->what |= layer_state_t::eInputInfoChanged;
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transferTouchFocus(
+ const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+ InputWindowCommands::TransferTouchFocusCommand transferTouchFocusCommand;
+ transferTouchFocusCommand.fromToken = fromToken;
+ transferTouchFocusCommand.toToken = toToken;
+ mInputWindowCommands.transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
+ mInputWindowCommands.syncInputWindows = true;
+ return *this;
+}
+
+#endif
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform(
+ const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eColorTransformChanged;
+ s->colorTransform = mat4(matrix, translation);
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
+ const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
+ setCrop_legacy(sc, source);
+
+ int x = dst.left;
+ int y = dst.top;
+ float xScale = dst.getWidth() / static_cast<float>(source.getWidth());
+ float yScale = dst.getHeight() / static_cast<float>(source.getHeight());
+ float matrix[4] = {1, 0, 0, 1};
+
+ switch (transform) {
+ case NATIVE_WINDOW_TRANSFORM_FLIP_H:
+ matrix[0] = -xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = yScale;
+ x += source.getWidth();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_FLIP_V:
+ matrix[0] = xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = -yScale;
+ y += source.getHeight();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_ROT_90:
+ matrix[0] = 0; matrix[1] = -yScale;
+ matrix[2] = xScale; matrix[3] = 0;
+ x += source.getHeight();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_ROT_180:
+ matrix[0] = -xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = -yScale;
+ x += source.getWidth();
+ y += source.getHeight();
+ break;
+ case NATIVE_WINDOW_TRANSFORM_ROT_270:
+ matrix[0] = 0; matrix[1] = yScale;
+ matrix[2] = -xScale; matrix[3] = 0;
+ y += source.getWidth();
+ break;
+ default:
+ matrix[0] = xScale; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = yScale;
+ break;
+ }
+ setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
+ setPosition(sc, x, y);
+
return *this;
}
@@ -559,11 +1232,6 @@
{
}
-SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root)
- : mStatus(NO_INIT), mParent(root)
-{
-}
-
SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& client)
: mStatus(NO_ERROR), mClient(client)
{
@@ -571,12 +1239,10 @@
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- if (sf != 0 && mStatus == NO_INIT) {
- auto rootProducer = mParent.promote();
+ if (sf != nullptr && mStatus == NO_INIT) {
sp<ISurfaceComposerClient> conn;
- conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) :
- sf->createConnection();
- if (conn != 0) {
+ conn = sf->createConnection();
+ if (conn != nullptr) {
mClient = conn;
mStatus = NO_ERROR;
}
@@ -606,39 +1272,49 @@
// this can be called more than once.
sp<ISurfaceComposerClient> client;
Mutex::Autolock _lm(mLock);
- if (mClient != 0) {
+ if (mClient != nullptr) {
client = mClient; // hold ref while lock is held
mClient.clear();
}
mStatus = NO_INIT;
}
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
- const String8& name,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- uint32_t flags,
- SurfaceControl* parent,
- int32_t windowType,
- int32_t ownerUid)
-{
+sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags,
+ SurfaceControl* parent,
+ LayerMetadata metadata) {
sp<SurfaceControl> s;
- createSurfaceChecked(name, w, h, format, &s, flags, parent, windowType, ownerUid);
+ createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
return s;
}
-status_t SurfaceComposerClient::createSurfaceChecked(
- const String8& name,
- uint32_t w,
- uint32_t h,
- PixelFormat format,
- sp<SurfaceControl>* outSurface,
- uint32_t flags,
- SurfaceControl* parent,
- int32_t windowType,
- int32_t ownerUid)
-{
+sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
+ uint32_t h, PixelFormat format,
+ uint32_t flags, Surface* parent,
+ LayerMetadata metadata) {
+ sp<SurfaceControl> sur;
+ status_t err = mStatus;
+
+ if (mStatus == NO_ERROR) {
+ sp<IBinder> handle;
+ sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
+ sp<IGraphicBufferProducer> gbp;
+
+ err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
+ std::move(metadata), &handle, &gbp);
+ ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
+ if (err == NO_ERROR) {
+ return new SurfaceControl(this, handle, gbp, true /* owned */);
+ }
+ }
+ return nullptr;
+}
+
+status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format,
+ sp<SurfaceControl>* outSurface, uint32_t flags,
+ SurfaceControl* parent,
+ LayerMetadata metadata) {
sp<SurfaceControl> sur;
status_t err = mStatus;
@@ -650,8 +1326,9 @@
if (parent != nullptr) {
parentHandle = parent->getHandle();
}
- err = mClient->createSurface(name, w, h, format, flags, parentHandle,
- windowType, ownerUid, &handle, &gbp);
+
+ err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
+ &handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
*outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
@@ -660,13 +1337,6 @@
return err;
}
-status_t SurfaceComposerClient::destroySurface(const sp<IBinder>& sid) {
- if (mStatus != NO_ERROR)
- return mStatus;
- status_t err = mClient->destroySurface(sid);
- return err;
-}
-
status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const {
if (mStatus != NO_ERROR) {
return mStatus;
@@ -718,10 +1388,6 @@
return NO_ERROR;
}
-status_t SurfaceComposerClient::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) {
- return ComposerService::getComposerService()->getDisplayViewport(display, outViewport);
-}
-
int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) {
return ComposerService::getComposerService()->getActiveConfig(display);
}
@@ -730,11 +1396,28 @@
return ComposerService::getComposerService()->setActiveConfig(display, id);
}
+status_t SurfaceComposerClient::setAllowedDisplayConfigs(
+ const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) {
+ return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken,
+ allowedConfigs);
+}
+
+status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs) {
+ return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken,
+ outAllowedConfigs);
+}
+
status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
Vector<ColorMode>* outColorModes) {
return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
}
+status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& outPrimaries) {
+ return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries);
+}
+
ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
return ComposerService::getComposerService()->getActiveColorMode(display);
}
@@ -749,6 +1432,20 @@
ComposerService::getComposerService()->setPowerMode(token, mode);
}
+status_t SurfaceComposerClient::getCompositionPreference(
+ ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat,
+ ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) {
+ return ComposerService::getComposerService()
+ ->getCompositionPreference(defaultDataspace, defaultPixelFormat,
+ wideColorGamutDataspace, wideColorGamutPixelFormat);
+}
+
+bool SurfaceComposerClient::getProtectedContentSupport() {
+ bool supported = false;
+ ComposerService::getComposerService()->getProtectedContentSupport(&supported);
+ return supported;
+}
+
status_t SurfaceComposerClient::clearAnimationFrameStats() {
return ComposerService::getComposerService()->clearAnimationFrameStats();
}
@@ -763,38 +1460,105 @@
outCapabilities);
}
+status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ return ComposerService::getComposerService()
+ ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace,
+ outComponentMask);
+}
+
+status_t SurfaceComposerClient::setDisplayContentSamplingEnabled(const sp<IBinder>& display,
+ bool enable, uint8_t componentMask,
+ uint64_t maxFrames) {
+ return ComposerService::getComposerService()->setDisplayContentSamplingEnabled(display, enable,
+ componentMask,
+ maxFrames);
+}
+
+status_t SurfaceComposerClient::getDisplayedContentSample(const sp<IBinder>& display,
+ uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) {
+ return ComposerService::getComposerService()->getDisplayedContentSample(display, maxFrames,
+ timestamp, outStats);
+}
+
+status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display,
+ bool* outIsWideColorDisplay) {
+ return ComposerService::getComposerService()->isWideColorDisplay(display,
+ outIsWideColorDisplay);
+}
+
+status_t SurfaceComposerClient::addRegionSamplingListener(
+ const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ return ComposerService::getComposerService()->addRegionSamplingListener(samplingArea,
+ stopLayerHandle,
+ listener);
+}
+
+status_t SurfaceComposerClient::removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& listener) {
+ return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
+}
+
+bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
+ bool support = false;
+ ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
+ return support;
+}
+
+status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken,
+ float brightness) {
+ return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
+}
+
// ----------------------------------------------------------------------------
-status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform, uint32_t rotation,
- sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ uint32_t rotation, bool captureSecureLayers, sp<GraphicBuffer>* outBuffer) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == NULL) return NO_INIT;
- status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight, minLayerZ,
- maxLayerZ, useIdentityTransform,
- static_cast<ISurfaceComposer::Rotation>(rotation));
+ if (s == nullptr) return NO_INIT;
+ status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
+ reqWidth, reqHeight, useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation),
+ captureSecureLayers);
if (ret != NO_ERROR) {
return ret;
}
return ret;
}
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ uint32_t rotation, sp<GraphicBuffer>* outBuffer) {
+ return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth,
+ reqHeight, useIdentityTransform, rotation, false, outBuffer);
+}
+
+status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle,
+ const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
float frameScale, sp<GraphicBuffer>* outBuffer) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == NULL) return NO_INIT;
- status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
- false /* childrenOnly */);
+ if (s == nullptr) return NO_INIT;
+ status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+ sourceCrop, frameScale, false /* childrenOnly */);
return ret;
}
-status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle,
+ const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
float frameScale, sp<GraphicBuffer>* outBuffer) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == NULL) return NO_INIT;
- status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
- true /* childrenOnly */);
+ if (s == nullptr) return NO_INIT;
+ status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+ sourceCrop, frameScale, true /* childrenOnly */);
return ret;
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 5eafbb3..55488da 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -54,19 +54,34 @@
{
}
+SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
+ mClient = other->mClient;
+ mHandle = other->mHandle;
+ mGraphicBufferProducer = other->mGraphicBufferProducer;
+ mOwned = false;
+}
+
SurfaceControl::~SurfaceControl()
{
- destroy();
+ // Avoid reparenting the server-side surface to null if we are not the owner of it,
+ // meaning that we retrieved it from another process.
+ if (mClient != nullptr && mHandle != nullptr && mOwned) {
+ SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient());
+ }
+ release();
}
void SurfaceControl::destroy()
{
- // Avoid destroying the server-side surface if we are not the owner of it, meaning that we
- // retrieved it from another process.
- if (isValid() && mOwned) {
- mClient->destroySurface(mHandle);
+ if (isValid()) {
+ SurfaceComposerClient::Transaction().reparent(this, nullptr).apply();
}
- // clear all references and trigger an IPC now, to make sure things
+ release();
+}
+
+void SurfaceControl::release()
+{
+ // Trigger an IPC now, to make sure things
// happen without delay, since these resources are quite heavy.
mClient.clear();
mHandle.clear();
@@ -74,19 +89,8 @@
IPCThreadState::self()->flushCommands();
}
-void SurfaceControl::clear()
-{
- // here, the window manager tells us explicitly that we should destroy
- // the surface's resource. Soon after this call, it will also release
- // its last reference (which will call the dtor); however, it is possible
- // that a client living in the same process still holds references which
- // would delay the call to the dtor -- that is why we need this explicit
- // "clear()" call.
- destroy();
-}
-
void SurfaceControl::disconnect() {
- if (mGraphicBufferProducer != NULL) {
+ if (mGraphicBufferProducer != nullptr) {
mGraphicBufferProducer->disconnect(
BufferQueueCore::CURRENTLY_CONNECTED_API);
}
@@ -95,28 +99,28 @@
bool SurfaceControl::isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs)
{
- if (lhs == 0 || rhs == 0)
+ if (lhs == nullptr || rhs == nullptr)
return false;
return lhs->mHandle == rhs->mHandle;
}
status_t SurfaceControl::clearLayerFrameStats() const {
status_t err = validate();
- if (err < 0) return err;
+ if (err != NO_ERROR) return err;
const sp<SurfaceComposerClient>& client(mClient);
return client->clearLayerFrameStats(mHandle);
}
status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const {
status_t err = validate();
- if (err < 0) return err;
+ if (err != NO_ERROR) return err;
const sp<SurfaceComposerClient>& client(mClient);
return client->getLayerFrameStats(mHandle, outStats);
}
status_t SurfaceControl::validate() const
{
- if (mHandle==0 || mClient==0) {
+ if (mHandle==nullptr || mClient==nullptr) {
ALOGE("invalid handle (%p) or client (%p)",
mHandle.get(), mClient.get());
return NO_INIT;
@@ -128,7 +132,7 @@
const sp<SurfaceControl>& control, Parcel* parcel)
{
sp<IGraphicBufferProducer> bp;
- if (control != NULL) {
+ if (control != nullptr) {
bp = control->mGraphicBufferProducer;
}
return parcel->writeStrongBinder(IInterface::asBinder(bp));
@@ -146,7 +150,7 @@
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
- if (mSurfaceData == 0) {
+ if (mSurfaceData == nullptr) {
return generateSurfaceLocked();
}
return mSurfaceData;
@@ -164,6 +168,12 @@
return mHandle;
}
+sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
+{
+ Mutex::Autolock _l(mLock);
+ return mGraphicBufferProducer;
+}
+
sp<SurfaceComposerClient> SurfaceControl::getClient() const
{
return mClient;
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index afa15c5..fcae05c 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -41,7 +41,7 @@
// This can only be called after EGL has been initialized; otherwise the
// check below will abort.
const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
- LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed");
+ LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed");
if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
// This makes GLConsumer use the EGL_ANDROID_native_fence_sync
// extension to create Android native fences to signal when all
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index b1e44bb..e64ba9b 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -100,7 +100,12 @@
*/
// convert: Return<Status> -> status_t
inline status_t toStatusT(Return<Status> const& t) {
- return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR;
+ if (t.isOk()) {
+ return static_cast<status_t>(static_cast<Status>(t));
+ } else if (t.isDeadObject()) {
+ return DEAD_OBJECT;
+ }
+ return UNKNOWN_ERROR;
}
/**
@@ -111,7 +116,7 @@
*/
// convert: Return<void> -> status_t
inline status_t toStatusT(Return<void> const& t) {
- return t.isOk() ? OK : UNKNOWN_ERROR;
+ return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR);
}
/**
diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
new file mode 100644
index 0000000..2712f42
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "H2BProducerListener@1.0"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/1.0/H2BProducerListener.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::Return;
+
+H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+ : CBase{base} {
+}
+
+void H2BProducerListener::onBufferReleased() {
+ if (!mBase->onBufferReleased().isOk()) {
+ LOG(ERROR) << "onBufferReleased: transaction failed.";
+ }
+}
+
+bool H2BProducerListener::needsReleaseNotify() {
+ Return<bool> transResult = mBase->needsReleaseNotify();
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "needsReleaseNotify: transaction failed.";
+ return false;
+ }
+ return static_cast<bool>(transResult);
+}
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
new file mode 100644
index 0000000..e039593
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "H2BGraphicBufferProducer@2.0"
+
+#include <android-base/logging.h>
+
+#include <android/hardware/graphics/bufferqueue/2.0/types.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/bufferqueue/2.0/H2BProducerListener.h>
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/types.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <vndk/hardware_buffer.h>
+
+namespace android {
+
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+// B2HGraphicBufferProducer
+// ========================
+
+B2HGraphicBufferProducer::B2HGraphicBufferProducer(
+ sp<BGraphicBufferProducer> const& base)
+ : mBase{base} {
+}
+
+Return<HStatus> B2HGraphicBufferProducer::setMaxDequeuedBufferCount(
+ int32_t maxDequeuedBuffers) {
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->setMaxDequeuedBufferCount(
+ static_cast<int>(maxDequeuedBuffers)),
+ &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<void> B2HGraphicBufferProducer::requestBuffer(
+ int32_t slot,
+ requestBuffer_cb _hidl_cb) {
+ sp<GraphicBuffer> bBuffer;
+ HStatus hStatus{};
+ HardwareBuffer hBuffer{};
+ uint32_t hGenerationNumber{};
+ bool converted =
+ b2h(mBase->requestBuffer(
+ static_cast<int>(slot), &bBuffer),
+ &hStatus) &&
+ b2h(bBuffer, &hBuffer, &hGenerationNumber);
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR,
+ hBuffer, hGenerationNumber);
+ return {};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::setAsyncMode(bool async) {
+ HStatus hStatus{};
+ bool converted = b2h(mBase->setAsyncMode(async), &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<void> B2HGraphicBufferProducer::dequeueBuffer(
+ DequeueBufferInput const& input,
+ dequeueBuffer_cb _hidl_cb) {
+ int bSlot{};
+ sp<BFence> bFence;
+ HStatus hStatus{};
+ DequeueBufferOutput hOutput{};
+ HFenceWrapper hFenceWrapper;
+ bool converted =
+ b2h(mBase->dequeueBuffer(
+ &bSlot,
+ &bFence,
+ input.width,
+ input.height,
+ static_cast<PixelFormat>(input.format),
+ input.usage,
+ &hOutput.bufferAge,
+ nullptr /* outTimestamps */),
+ &hStatus,
+ &hOutput.bufferNeedsReallocation,
+ &hOutput.releaseAllBuffers) &&
+ b2h(bFence, &hFenceWrapper);
+ hOutput.fence = hFenceWrapper.getHandle();
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR,
+ static_cast<int32_t>(bSlot),
+ hOutput);
+ return {};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::detachBuffer(int32_t slot) {
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->detachBuffer(static_cast<int>(slot)), &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<void> B2HGraphicBufferProducer::detachNextBuffer(
+ detachNextBuffer_cb _hidl_cb) {
+ sp<GraphicBuffer> bBuffer;
+ sp<BFence> bFence;
+ HStatus hStatus{};
+ HardwareBuffer hBuffer{};
+ HFenceWrapper hFenceWrapper;
+ bool converted =
+ b2h(mBase->detachNextBuffer(&bBuffer, &bFence), &hStatus) &&
+ b2h(bBuffer, &hBuffer) &&
+ b2h(bFence, &hFenceWrapper);
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR,
+ hBuffer,
+ hFenceWrapper.getHandle());
+ return {};
+}
+
+Return<void> B2HGraphicBufferProducer::attachBuffer(
+ HardwareBuffer const& hBuffer,
+ uint32_t generationNumber,
+ attachBuffer_cb _hidl_cb) {
+ sp<GraphicBuffer> bBuffer;
+ if (!h2b(hBuffer, &bBuffer) || !bBuffer) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR,
+ static_cast<int32_t>(SlotIndex::INVALID),
+ false);
+ return {};
+ }
+ bBuffer->setGenerationNumber(generationNumber);
+
+ int bSlot{};
+ HStatus hStatus{};
+ bool releaseAllBuffers{};
+ bool converted = b2h(
+ mBase->attachBuffer(&bSlot, bBuffer), &hStatus,
+ nullptr /* bufferNeedsReallocation */,
+ &releaseAllBuffers);
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR,
+ static_cast<int32_t>(bSlot),
+ releaseAllBuffers);
+ return {};
+}
+
+Return<void> B2HGraphicBufferProducer::queueBuffer(
+ int32_t slot,
+ QueueBufferInput const& hInput,
+ queueBuffer_cb _hidl_cb) {
+ using HOutput = QueueBufferOutput;
+ using BInput = BGraphicBufferProducer::QueueBufferInput;
+ using BOutput = BGraphicBufferProducer::QueueBufferOutput;
+
+ BInput bInput{
+ hInput.timestamp,
+ hInput.isAutoTimestamp,
+ static_cast<android_dataspace>(hInput.dataSpace),
+ {}, /* crop */
+ 0 /* scalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE */,
+ static_cast<uint32_t>(hInput.transform),
+ {}, /* fence */
+ static_cast<uint32_t>(hInput.stickyTransform),
+ false /* getFrameTimestamps */};
+
+ // Convert crop.
+ if (!h2b(hInput.crop, &bInput.crop)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{});
+ return {};
+ }
+
+ // Convert surfaceDamage.
+ if (!h2b(hInput.surfaceDamage, &bInput.surfaceDamage)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{});
+ return {};
+ }
+
+ // Convert fence.
+ if (!h2b(hInput.fence, &bInput.fence)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{});
+ return {};
+ }
+
+ BOutput bOutput{};
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->queueBuffer(static_cast<int>(slot), bInput, &bOutput),
+ &hStatus);
+
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR,
+ HOutput{bOutput.width,
+ bOutput.height,
+ static_cast<int32_t>(bOutput.transformHint),
+ bOutput.numPendingBuffers,
+ bOutput.nextFrameNumber,
+ bOutput.bufferReplaced});
+ return {};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::cancelBuffer(
+ int32_t slot,
+ hidl_handle const& fence) {
+ sp<BFence> bFence;
+ if (!h2b(fence.getNativeHandle(), &bFence)) {
+ return {HStatus::UNKNOWN_ERROR};
+ }
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->cancelBuffer(static_cast<int>(slot), bFence),
+ &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<void> B2HGraphicBufferProducer::query(int32_t what, query_cb _hidl_cb) {
+ int value{};
+ int result = mBase->query(static_cast<int>(what), &value);
+ _hidl_cb(static_cast<int32_t>(result), static_cast<int32_t>(value));
+ return {};
+}
+
+Return<void> B2HGraphicBufferProducer::connect(
+ sp<HProducerListener> const& hListener,
+ HConnectionType hConnectionType,
+ bool producerControlledByApp,
+ connect_cb _hidl_cb) {
+ using BOutput = BGraphicBufferProducer::QueueBufferOutput;
+ using HOutput = HGraphicBufferProducer::QueueBufferOutput;
+ sp<BProducerListener> bListener = new H2BProducerListener(hListener);
+ if (!bListener) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{});
+ return {};
+ }
+ int bConnectionType;
+ if (!h2b(hConnectionType, &bConnectionType)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{});
+ return {};
+ }
+ BOutput bOutput{};
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->connect(bListener,
+ bConnectionType,
+ producerControlledByApp,
+ &bOutput),
+ &hStatus);
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR,
+ HOutput{bOutput.width,
+ bOutput.height,
+ static_cast<int32_t>(bOutput.transformHint),
+ bOutput.numPendingBuffers,
+ bOutput.nextFrameNumber,
+ bOutput.bufferReplaced});
+ return {};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::disconnect(
+ HConnectionType hConnectionType) {
+ int bConnectionType;
+ if (!h2b(hConnectionType, &bConnectionType)) {
+ return {HStatus::UNKNOWN_ERROR};
+ }
+ HStatus hStatus{};
+ bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::allocateBuffers(
+ uint32_t width, uint32_t height,
+ uint32_t format, uint64_t usage) {
+ mBase->allocateBuffers(
+ width, height, static_cast<PixelFormat>(format), usage);
+ return {HStatus::OK};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::allowAllocation(bool allow) {
+ HStatus hStatus{};
+ bool converted = b2h(mBase->allowAllocation(allow), &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::setGenerationNumber(
+ uint32_t generationNumber) {
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->setGenerationNumber(generationNumber),
+ &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<HStatus> B2HGraphicBufferProducer::setDequeueTimeout(
+ int64_t timeoutNs) {
+ HStatus hStatus{};
+ bool converted = b2h(
+ mBase->setDequeueTimeout(static_cast<nsecs_t>(timeoutNs)),
+ &hStatus);
+ return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
+}
+
+Return<uint64_t> B2HGraphicBufferProducer::getUniqueId() {
+ uint64_t outId{};
+ HStatus hStatus{};
+ bool converted = b2h(mBase->getUniqueId(&outId), &hStatus);
+ return {converted ? outId : 0};
+}
+
+Return<void> B2HGraphicBufferProducer::getConsumerName(
+ getConsumerName_cb _hidl_cb) {
+ _hidl_cb(hidl_string{mBase->getConsumerName().c_str()});
+ return {};
+}
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp
new file mode 100644
index 0000000..c4c96eb
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 <gui/bufferqueue/2.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+// B2HProducerListener
+B2HProducerListener::B2HProducerListener(sp<BProducerListener> const& base)
+ : mBase{base},
+ mNeedsReleaseNotify{base ? base->needsReleaseNotify() : false} {
+}
+
+Return<void> B2HProducerListener::onBuffersReleased(uint32_t count) {
+ if (mNeedsReleaseNotify) {
+ for (; count > 0; --count) {
+ mBase->onBufferReleased();
+ }
+ }
+ return {};
+}
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
new file mode 100644
index 0000000..1023ef1
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,514 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "H2BGraphicBufferProducer@2.0"
+
+#include <android-base/logging.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/bufferqueue/2.0/B2HProducerListener.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/types.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <vndk/hardware_buffer.h>
+
+namespace android {
+
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+// H2BGraphicBufferProducer
+// ========================
+
+status_t H2BGraphicBufferProducer::requestBuffer(int slot,
+ sp<GraphicBuffer>* bBuffer) {
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->requestBuffer(slot,
+ [&converted, &bStatus, bBuffer](
+ HStatus hStatus,
+ HardwareBuffer const& hBuffer,
+ uint32_t generationNumber) {
+ converted =
+ h2b(hStatus, &bStatus) &&
+ h2b(hBuffer, bBuffer);
+ if (*bBuffer) {
+ (*bBuffer)->setGenerationNumber(generationNumber);
+ }
+ });
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "requestBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!converted) {
+ LOG(ERROR) << "requestBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount(
+ int maxDequeuedBuffers) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->setMaxDequeuedBufferCount(
+ static_cast<int32_t>(maxDequeuedBuffers));
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "setMaxDequeuedBufferCount: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "setMaxDequeuedBufferCount: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::setAsyncMode(bool async) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->setAsyncMode(async);
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "setAsyncMode: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "setAsyncMode: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::dequeueBuffer(
+ int* slot, sp<BFence>* fence,
+ uint32_t w, uint32_t h,
+ PixelFormat format, uint64_t usage,
+ uint64_t* outBufferAge, FrameEventHistoryDelta* /* outTimestamps */) {
+
+ using HInput = HGraphicBufferProducer::DequeueBufferInput;
+ HInput input{w, h, static_cast<uint32_t>(format), usage};
+
+ using HOutput = HGraphicBufferProducer::DequeueBufferOutput;
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->dequeueBuffer(input,
+ [&converted, &bStatus, slot, fence, outBufferAge] (
+ HStatus hStatus, int32_t hSlot, HOutput const& hOutput) {
+ converted = h2b(hStatus, &bStatus);
+ if (!converted || bStatus != OK) {
+ return;
+ }
+ *slot = hSlot;
+ *outBufferAge = hOutput.bufferAge;
+ bStatus =
+ (hOutput.bufferNeedsReallocation ?
+ BUFFER_NEEDS_REALLOCATION : 0) |
+ (hOutput.releaseAllBuffers ?
+ RELEASE_ALL_BUFFERS : 0);
+ converted = h2b(hOutput.fence, fence);
+ });
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "dequeueBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!converted) {
+ LOG(ERROR) << "dequeueBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachBuffer(int slot) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->detachBuffer(
+ static_cast<int32_t>(slot));
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "detachBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "detachBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachNextBuffer(
+ sp<GraphicBuffer>* outBuffer, sp<BFence>* outFence) {
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->detachNextBuffer(
+ [&converted, &bStatus, outBuffer, outFence] (
+ HStatus hStatus,
+ HardwareBuffer const& hBuffer,
+ hidl_handle const& hFence) {
+ converted = h2b(hStatus, &bStatus) &&
+ h2b(hBuffer, outBuffer) &&
+ h2b(hFence, outFence);
+ });
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "detachNextBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!converted) {
+ LOG(ERROR) << "detachNextBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::attachBuffer(
+ int* outSlot, sp<GraphicBuffer> const& buffer) {
+ HardwareBuffer hBuffer{};
+ uint32_t hGenerationNumber{};
+ if (!b2h(buffer, &hBuffer, &hGenerationNumber)) {
+ LOG(ERROR) << "attachBuffer: invalid input buffer.";
+ return BAD_VALUE;
+ }
+
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->attachBuffer(hBuffer, hGenerationNumber,
+ [&converted, &bStatus, outSlot](
+ HStatus hStatus, int32_t hSlot, bool releaseAllBuffers) {
+ converted = h2b(hStatus, &bStatus);
+ *outSlot = static_cast<int>(hSlot);
+ if (converted && releaseAllBuffers && bStatus == OK) {
+ bStatus = IGraphicBufferProducer::RELEASE_ALL_BUFFERS;
+ }
+ });
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "attachBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!converted) {
+ LOG(ERROR) << "attachBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::queueBuffer(
+ int slot,
+ QueueBufferInput const& input,
+ QueueBufferOutput* output) {
+ HRect hCrop{};
+ (void)b2h(input.crop, &hCrop);
+
+ using HInput = HGraphicBufferProducer::QueueBufferInput;
+ HInput hInput{
+ input.timestamp,
+ static_cast<bool>(input.isAutoTimestamp),
+ static_cast<int32_t>(input.dataSpace),
+ {}, // crop
+ static_cast<int32_t>(input.transform),
+ static_cast<int32_t>(input.stickyTransform),
+ {}, // fence
+ {} // surfaceDamage
+ };
+
+ // Convert crop.
+ if (!b2h(input.crop, &hInput.crop)) {
+ LOG(ERROR) << "queueBuffer: corrupted input crop rectangle.";
+ return UNKNOWN_ERROR;
+ }
+
+ // Convert surfaceDamage.
+ size_t numRects;
+ Rect const* rectArray = input.surfaceDamage.getArray(&numRects);
+ hInput.surfaceDamage.resize(numRects);
+ for (size_t i = 0; i < numRects; ++i) {
+ if (!b2h(rectArray[i], &hInput.surfaceDamage[i])) {
+ LOG(ERROR) << "queueBuffer: corrupted input surface damage.";
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Convert fence.
+ HFenceWrapper hFenceWrapper;
+ if (!b2h(input.fence, &hFenceWrapper)) {
+ LOG(ERROR) << "queueBuffer: corrupted input fence.";
+ return UNKNOWN_ERROR;
+ }
+ hInput.fence = hFenceWrapper.getHandle();
+
+ using HOutput = HGraphicBufferProducer::QueueBufferOutput;
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->queueBuffer(
+ static_cast<int32_t>(slot),
+ hInput,
+ [&converted, &bStatus, output](
+ HStatus hStatus,
+ HOutput const& hOutput) {
+ converted = h2b(hStatus, &bStatus);
+ output->width = hOutput.width;
+ output->height = hOutput.height;
+ output->transformHint =
+ static_cast<uint32_t>(hOutput.transformHint);
+ output->numPendingBuffers = hOutput.numPendingBuffers;
+ output->nextFrameNumber = hOutput.nextFrameNumber;
+ output->bufferReplaced = hOutput.bufferReplaced;
+ });
+
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "queueBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!converted) {
+ LOG(ERROR) << "queueBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::cancelBuffer(int slot, sp<BFence> const& fence) {
+ HFenceWrapper hFenceWrapper;
+ if (!b2h(fence, &hFenceWrapper)) {
+ LOG(ERROR) << "cancelBuffer: corrupted input fence.";
+ return UNKNOWN_ERROR;
+ }
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->cancelBuffer(
+ static_cast<int32_t>(slot),
+ hFenceWrapper.getHandle());
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "cancelBuffer: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "cancelBuffer: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+int H2BGraphicBufferProducer::query(int what, int* value) {
+ int result{};
+ Return<void> transResult = mBase->query(
+ static_cast<int32_t>(what),
+ [&result, value](int32_t r, int32_t v) {
+ result = static_cast<int>(r);
+ *value = static_cast<int>(v);
+ });
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "query: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ return result;
+}
+
+status_t H2BGraphicBufferProducer::connect(
+ sp<IProducerListener> const& listener, int api,
+ bool producerControlledByApp, QueueBufferOutput* output) {
+ HConnectionType hConnectionType;
+ if (!b2h(api, &hConnectionType)) {
+ LOG(ERROR) << "connect: corrupted input connection type.";
+ return UNKNOWN_ERROR;
+ }
+ sp<HProducerListener> hListener = nullptr;
+ if (listener && listener->needsReleaseNotify()) {
+ hListener = new B2HProducerListener(listener);
+ if (!hListener) {
+ LOG(ERROR) << "connect: failed to wrap listener.";
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ using HOutput = HGraphicBufferProducer::QueueBufferOutput;
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->connect(
+ hListener,
+ hConnectionType,
+ producerControlledByApp,
+ [&converted, &bStatus, output](
+ HStatus hStatus,
+ HOutput hOutput) {
+ converted = h2b(hStatus, &bStatus);
+ output->width = hOutput.width;
+ output->height = hOutput.height;
+ output->transformHint =
+ static_cast<uint32_t>(hOutput.transformHint);
+ output->numPendingBuffers = hOutput.numPendingBuffers;
+ output->nextFrameNumber = hOutput.nextFrameNumber;
+ output->bufferReplaced = hOutput.bufferReplaced;
+ });
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "connect: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!converted) {
+ LOG(ERROR) << "connect: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+
+}
+
+status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) {
+ HConnectionType hConnectionType;
+ if (mode == DisconnectMode::AllLocal) {
+ hConnectionType = HConnectionType::CURRENTLY_CONNECTED;
+ } else if (!b2h(api, &hConnectionType)) {
+ LOG(ERROR) << "connect: corrupted input connection type.";
+ return UNKNOWN_ERROR;
+ }
+
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->disconnect(hConnectionType);
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "disconnect: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "disconnect: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::setSidebandStream(
+ sp<NativeHandle> const& stream) {
+ if (stream) {
+ LOG(INFO) << "setSidebandStream: not supported.";
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+void H2BGraphicBufferProducer::allocateBuffers(
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint64_t usage) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->allocateBuffers(
+ width, height, static_cast<uint32_t>(format), usage);
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "allocateBuffer: transaction failed.";
+ return;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "allocateBuffer: corrupted transaction.";
+ return;
+ }
+}
+
+status_t H2BGraphicBufferProducer::allowAllocation(bool allow) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->allowAllocation(allow);
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "allowAllocation: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "allowAllocation: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::setGenerationNumber(
+ uint32_t generationNumber) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->setGenerationNumber(generationNumber);
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "setGenerationNumber: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "setGenerationNumber: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+String8 H2BGraphicBufferProducer::getConsumerName() const {
+ String8 bName;
+ Return<void> transResult = mBase->getConsumerName(
+ [&bName](hidl_string const& name) {
+ bName = name.c_str();
+ });
+ return bName;
+}
+
+status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
+ if (sharedBufferMode) {
+ LOG(INFO) << "setSharedBufferMode: not supported.";
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
+ if (autoRefresh) {
+ LOG(INFO) << "setAutoRefresh: not supported.";
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) {
+ status_t bStatus{};
+ Return<HStatus> transResult = mBase->setDequeueTimeout(
+ static_cast<int64_t>(timeout));
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "setDequeueTimeout: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ if (!h2b(static_cast<HStatus>(transResult), &bStatus)) {
+ LOG(ERROR) << "setDequeueTimeout: corrupted transaction.";
+ return FAILED_TRANSACTION;
+ }
+ return bStatus;
+}
+
+status_t H2BGraphicBufferProducer::getLastQueuedBuffer(
+ sp<GraphicBuffer>*,
+ sp<BFence>*,
+ float[16]) {
+ LOG(INFO) << "getLastQueuedBuffer: not supported.";
+ return INVALID_OPERATION;
+}
+
+void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta*) {
+ LOG(INFO) << "getFrameTimestamps: not supported.";
+}
+
+status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const {
+ Return<uint64_t> transResult = mBase->getUniqueId();
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "getUniqueId: transaction failed.";
+ return FAILED_TRANSACTION;
+ }
+ *outId = static_cast<uint64_t>(transResult);
+ return OK;
+}
+
+status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t*) const {
+ LOG(INFO) << "getConsumerUsage: not supported.";
+ return INVALID_OPERATION;
+}
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
new file mode 100644
index 0000000..b81a357
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "H2BProducerListener@2.0"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/2.0/H2BProducerListener.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+using ::android::hardware::Return;
+
+H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+ : CBase{base} {
+}
+
+void H2BProducerListener::onBufferReleased() {
+ if (mBase) {
+ Return<void> transResult = mBase->onBuffersReleased(1);
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "onBuffersReleased: transaction failed.";
+ }
+ }
+}
+
+bool H2BProducerListener::needsReleaseNotify() {
+ return static_cast<bool>(mBase);
+}
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp
new file mode 100644
index 0000000..a110517
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 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 <cutils/native_handle.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/types.h>
+#include <system/window.h>
+#include <vndk/hardware_buffer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+// Status
+// ======
+
+bool b2h(status_t from, HStatus* to,
+ bool* bufferNeedsReallocation, bool* releaseAllBuffers) {
+ switch (from) {
+ case OK:
+ *to = HStatus::OK; break;
+ case NO_MEMORY:
+ *to = HStatus::NO_MEMORY; break;
+ case NO_INIT:
+ *to = HStatus::NO_INIT; break;
+ case BAD_VALUE:
+ *to = HStatus::BAD_VALUE; break;
+ case DEAD_OBJECT:
+ *to = HStatus::DEAD_OBJECT; break;
+ case INVALID_OPERATION:
+ *to = HStatus::INVALID_OPERATION; break;
+ case TIMED_OUT:
+ *to = HStatus::TIMED_OUT; break;
+ case WOULD_BLOCK:
+ *to = HStatus::WOULD_BLOCK; break;
+ case UNKNOWN_ERROR:
+ *to = HStatus::UNKNOWN_ERROR; break;
+ default:
+ using BGBP = ::android::IGraphicBufferProducer;
+ status_t mask =
+ (bufferNeedsReallocation ? BGBP::BUFFER_NEEDS_REALLOCATION : 0)
+ | (releaseAllBuffers ? BGBP::RELEASE_ALL_BUFFERS : 0);
+ if (from & ~mask) {
+ *to = static_cast<HStatus>(from);
+ } else {
+ *to = HStatus::OK;
+ if (bufferNeedsReallocation) {
+ *bufferNeedsReallocation = from & BGBP::BUFFER_NEEDS_REALLOCATION;
+ }
+ if (releaseAllBuffers) {
+ *releaseAllBuffers = from & BGBP::RELEASE_ALL_BUFFERS;
+ }
+ }
+ }
+ return true;
+}
+
+bool h2b(HStatus from, status_t* to) {
+ switch (from) {
+ case HStatus::OK:
+ *to = OK; break;
+ case HStatus::NO_MEMORY:
+ *to = NO_MEMORY; break;
+ case HStatus::NO_INIT:
+ *to = NO_INIT; break;
+ case HStatus::BAD_VALUE:
+ *to = BAD_VALUE; break;
+ case HStatus::DEAD_OBJECT:
+ *to = DEAD_OBJECT; break;
+ case HStatus::INVALID_OPERATION:
+ *to = INVALID_OPERATION; break;
+ case HStatus::TIMED_OUT:
+ *to = TIMED_OUT; break;
+ case HStatus::WOULD_BLOCK:
+ *to = WOULD_BLOCK; break;
+ case HStatus::UNKNOWN_ERROR:
+ *to = UNKNOWN_ERROR; break;
+ default:
+ *to = static_cast<status_t>(from);
+ }
+ return true;
+}
+
+// Fence
+// =====
+
+HFenceWrapper::HFenceWrapper(native_handle_t* h) : mHandle{h} {
+}
+
+HFenceWrapper::~HFenceWrapper() {
+ native_handle_delete(mHandle);
+}
+
+HFenceWrapper& HFenceWrapper::set(native_handle_t* h) {
+ native_handle_delete(mHandle);
+ mHandle = h;
+ return *this;
+}
+
+HFenceWrapper& HFenceWrapper::operator=(native_handle_t* h) {
+ return set(h);
+}
+
+hidl_handle HFenceWrapper::getHandle() const {
+ return hidl_handle{mHandle};
+}
+
+HFenceWrapper::operator hidl_handle() const {
+ return getHandle();
+}
+
+bool b2h(sp<BFence> const& from, HFenceWrapper* to) {
+ if (!from) {
+ to->set(nullptr);
+ return true;
+ }
+ int fenceFd = from->get();
+ if (fenceFd == -1) {
+ to->set(nullptr);
+ return true;
+ }
+ native_handle_t* nh = native_handle_create(1, 0);
+ if (!nh) {
+ return false;
+ }
+ nh->data[0] = fenceFd;
+ to->set(nh);
+ return true;
+}
+
+bool h2b(native_handle_t const* from, sp<BFence>* to) {
+ if (!from || from->numFds == 0) {
+ *to = new ::android::Fence();
+ return true;
+ }
+ if (from->numFds != 1 || from->numInts != 0) {
+ return false;
+ }
+ *to = new BFence(dup(from->data[0]));
+ return true;
+}
+
+// ConnectionType
+// ==============
+
+bool b2h(int from, HConnectionType* to) {
+ *to = static_cast<HConnectionType>(from);
+ switch (from) {
+ case BufferQueueCore::CURRENTLY_CONNECTED_API:
+ *to = HConnectionType::CURRENTLY_CONNECTED; break;
+ case NATIVE_WINDOW_API_EGL:
+ *to = HConnectionType::EGL; break;
+ case NATIVE_WINDOW_API_CPU:
+ *to = HConnectionType::CPU; break;
+ case NATIVE_WINDOW_API_MEDIA:
+ *to = HConnectionType::MEDIA; break;
+ case NATIVE_WINDOW_API_CAMERA:
+ *to = HConnectionType::CAMERA; break;
+ }
+ return true;
+}
+
+bool h2b(HConnectionType from, int* to) {
+ *to = static_cast<int>(from);
+ switch (from) {
+ case HConnectionType::CURRENTLY_CONNECTED:
+ *to = BufferQueueCore::CURRENTLY_CONNECTED_API; break;
+ case HConnectionType::EGL:
+ *to = NATIVE_WINDOW_API_EGL; break;
+ case HConnectionType::CPU:
+ *to = NATIVE_WINDOW_API_CPU; break;
+ case HConnectionType::MEDIA:
+ *to = NATIVE_WINDOW_API_MEDIA; break;
+ case HConnectionType::CAMERA:
+ *to = NATIVE_WINDOW_API_CAMERA; break;
+ }
+ return true;
+}
+
+// Rect
+// ====
+
+bool b2h(BRect const& from, HRect* to) {
+ BRect* dst = reinterpret_cast<BRect*>(to->data());
+ dst->left = from.left;
+ dst->top = from.top;
+ dst->right = from.right;
+ dst->bottom = from.bottom;
+ return true;
+}
+
+bool h2b(HRect const& from, BRect* to) {
+ BRect const* src = reinterpret_cast<BRect const*>(from.data());
+ to->left = src->left;
+ to->top = src->top;
+ to->right = src->right;
+ to->bottom = src->bottom;
+ return true;
+}
+
+// Region
+// ======
+
+bool b2h(BRegion const& from, HRegion* to) {
+ size_t numRects;
+ BRect const* rectArray = from.getArray(&numRects);
+ to->resize(numRects);
+ for (size_t i = 0; i < numRects; ++i) {
+ if (!b2h(rectArray[i], &(*to)[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool h2b(HRegion const& from, BRegion* to) {
+ if (from.size() > 0) {
+ BRect bRect;
+ if (!h2b(from[0], &bRect)) {
+ return false;
+ }
+ to->set(bRect);
+ for (size_t i = 1; i < from.size(); ++i) {
+ if (!h2b(from[i], &bRect)) {
+ return false;
+ }
+ to->addRectUnchecked(
+ static_cast<int>(bRect.left),
+ static_cast<int>(bRect.top),
+ static_cast<int>(bRect.right),
+ static_cast<int>(bRect.bottom));
+ }
+ } else {
+ to->clear();
+ }
+ return true;
+}
+
+// GraphicBuffer
+// =============
+
+// The handle is not cloned. Its lifetime is tied to the original GraphicBuffer.
+bool b2h(sp<GraphicBuffer> const& from, HardwareBuffer* to,
+ uint32_t* toGenerationNumber) {
+ if (!from) {
+ return false;
+ }
+ AHardwareBuffer* hwBuffer = from->toAHardwareBuffer();
+ to->nativeHandle.setTo(
+ const_cast<native_handle_t*>(
+ AHardwareBuffer_getNativeHandle(hwBuffer)),
+ false);
+ AHardwareBuffer_describe(
+ hwBuffer,
+ reinterpret_cast<AHardwareBuffer_Desc*>(to->description.data()));
+ if (toGenerationNumber) {
+ *toGenerationNumber = from->getGenerationNumber();
+ }
+ return true;
+}
+
+// The handle is cloned.
+bool h2b(HardwareBuffer const& from, sp<GraphicBuffer>* to) {
+ AHardwareBuffer_Desc const* desc =
+ reinterpret_cast<AHardwareBuffer_Desc const*>(
+ from.description.data());
+ native_handle_t const* handle = from.nativeHandle;
+ AHardwareBuffer* hwBuffer;
+ if (AHardwareBuffer_createFromHandle(
+ desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+ &hwBuffer) != OK) {
+ return false;
+ }
+ *to = GraphicBuffer::fromAHardwareBuffer(hwBuffer);
+ return true;
+}
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h
index 23c9909..0e925ce 100644
--- a/libs/gui/include/gui/BufferHubProducer.h
+++ b/libs/gui/include/gui/BufferHubProducer.h
@@ -165,6 +165,10 @@
// buffers are acquired by the consumer, we can't .
status_t FreeAllBuffers();
+ // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been
+ // locked already.
+ status_t DetachBufferLocked(size_t slot);
+
// Concreate implementation backed by BufferHubBuffer.
std::shared_ptr<dvr::ProducerQueue> queue_;
@@ -199,10 +203,10 @@
// requested buffer usage or geometry differs from that of the buffer
// allocated to a slot.
struct BufferHubSlot : public BufferSlot {
- BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
+ BufferHubSlot() : mProducerBuffer(nullptr), mIsReallocating(false) {}
// BufferSlot comes from android framework, using m prefix to comply with
// the name convention with the reset of data fields from BufferSlot.
- std::shared_ptr<dvr::BufferProducer> mBufferProducer;
+ std::shared_ptr<dvr::ProducerBuffer> mProducerBuffer;
bool mIsReallocating;
};
BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity];
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index d375611..806fbe8 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -70,7 +70,7 @@
uint32_t chromaStep;
LockedBuffer() :
- data(NULL),
+ data(nullptr),
width(0),
height(0),
format(PIXEL_FORMAT_NONE),
@@ -82,8 +82,8 @@
dataSpace(HAL_DATASPACE_UNKNOWN),
frameNumber(0),
flexFormat(PIXEL_FORMAT_NONE),
- dataCb(NULL),
- dataCr(NULL),
+ dataCb(nullptr),
+ dataCr(nullptr),
chromaStride(0),
chromaStep(0)
{}
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index a4102df..22de751 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -52,13 +52,14 @@
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
+ DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
};
struct Event {
struct Header {
uint32_t type;
- uint32_t id;
+ PhysicalDisplayId displayId;
nsecs_t timestamp __attribute__((aligned(8)));
};
@@ -70,10 +71,15 @@
bool connected;
};
+ struct Config {
+ int32_t configId;
+ };
+
Header header;
union {
VSync vsync;
Hotplug hotplug;
+ Config config;
};
};
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index e06e40f..df02494 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -30,7 +30,6 @@
struct FrameEvents;
class FrameEventHistoryDelta;
-class String8;
// Identifiers for all the events that may be recorded or reported.
@@ -72,7 +71,7 @@
bool hasDequeueReadyInfo() const;
void checkFencesForCompletion();
- void dump(String8& outString) const;
+ void dump(std::string& outString) const;
bool valid{false};
int connectId{0};
@@ -112,7 +111,7 @@
FrameEvents* getFrame(uint64_t frameNumber);
FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
void checkFencesForCompletion();
- void dump(String8& outString) const;
+ void dump(std::string& outString) const;
static constexpr size_t MAX_FRAME_HISTORY = 8;
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index 8bc7cbf..ddd868d 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -140,7 +140,8 @@
// Scale the crop down horizontally or vertically such that it has the
// same aspect ratio as the buffer does.
- static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+ static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth,
+ uint32_t bufferHeight);
// getTimestamp retrieves the timestamp associated with the texture image
// set by the most recent call to updateTexImage.
@@ -305,7 +306,6 @@
// createIfNeeded creates an EGLImage if required (we haven't created
// one yet, or the EGLDisplay or crop-rect has changed).
status_t createIfNeeded(EGLDisplay display,
- const Rect& cropRect,
bool forceCreate = false);
// This calls glEGLImageTargetTexture2DOES to bind the image to the
@@ -314,7 +314,7 @@
const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
const native_handle* graphicBufferHandle() {
- return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle;
+ return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
}
private:
@@ -324,7 +324,7 @@
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
- const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
+ const sp<GraphicBuffer>& graphicBuffer);
// Disallow copying
EglImage(const EglImage& rhs);
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h
index b020ed9..7aa5432 100644
--- a/libs/gui/include/gui/GuiConfig.h
+++ b/libs/gui/include/gui/GuiConfig.h
@@ -17,12 +17,12 @@
#ifndef ANDROID_GUI_CONFIG_H
#define ANDROID_GUI_CONFIG_H
-#include <utils/String8.h>
+#include <string>
namespace android {
// Append the libgui configuration details to configStr.
-void appendGuiConfigString(String8& configStr);
+void appendGuiConfigString(std::string& configStr);
}; // namespace android
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h
index 9800602..1e9c3e7 100644
--- a/libs/gui/include/gui/HdrMetadata.h
+++ b/libs/gui/include/gui/HdrMetadata.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <vector>
#include <system/graphics.h>
#include <utils/Flattenable.h>
@@ -26,12 +27,15 @@
struct HdrMetadata : public LightFlattenable<HdrMetadata> {
enum Type : uint32_t {
SMPTE2086 = 1 << 0,
- CTA861_3 = 1 << 1,
+ CTA861_3 = 1 << 1,
+ HDR10PLUS = 1 << 2,
};
+
uint32_t validTypes{0};
android_smpte2086_metadata smpte2086{};
android_cta861_3_metadata cta8613{};
+ std::vector<uint8_t> hdr10plus{};
// LightFlattenable
bool isFixedSize() const { return false; }
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 887654e..2f538ad 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -35,6 +35,7 @@
#include <hidl/HybridInterface.h>
#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -42,8 +43,6 @@
class IProducerListener;
class NativeHandle;
class Surface;
-typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
- HGraphicBufferProducer;
/*
* This class defines the Binder IPC interface for the producer side of
@@ -62,7 +61,16 @@
class IGraphicBufferProducer : public IInterface
{
public:
- DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer)
+ using HGraphicBufferProducerV1_0 =
+ ::android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer;
+ using HGraphicBufferProducerV2_0 =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer;
+
+ DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer,
+ HGraphicBufferProducerV1_0,
+ HGraphicBufferProducerV2_0)
enum {
// A flag returned by dequeueBuffer when the client needs to call
@@ -345,7 +353,7 @@
*outScalingMode = scalingMode;
*outTransform = transform;
*outFence = fence;
- if (outStickyTransform != NULL) {
+ if (outStickyTransform != nullptr) {
*outStickyTransform = stickyTransform;
}
if (outGetFrameTimestamps) {
@@ -366,7 +374,6 @@
const HdrMetadata& getHdrMetadata() const { return hdrMetadata; }
void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; }
- private:
int64_t timestamp{0};
int isAutoTimestamp{0};
android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index e808bd3..a13d8e4 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -17,8 +17,10 @@
#ifndef ANDROID_GUI_IPRODUCERLISTENER_H
#define ANDROID_GUI_IPRODUCERLISTENER_H
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
#include <binder/IInterface.h>
-
+#include <hidl/HybridInterface.h>
#include <utils/RefBase.h>
namespace android {
@@ -47,7 +49,14 @@
class IProducerListener : public ProducerListener, public IInterface
{
public:
- DECLARE_META_INTERFACE(ProducerListener)
+ using HProducerListener1 =
+ ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener;
+ using HProducerListener2 =
+ ::android::hardware::graphics::bufferqueue::V2_0::IProducerListener;
+ DECLARE_HYBRID_META_INTERFACE(
+ ProducerListener,
+ HProducerListener1,
+ HProducerListener2)
};
class BnProducerListener : public BnInterface<IProducerListener>
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
new file mode 100644
index 0000000..1803d9a
--- /dev/null
+++ b/libs/gui/include/gui/IRegionSamplingListener.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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 <cstdint>
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
+
+namespace android {
+
+class IRegionSamplingListener : public IInterface {
+public:
+ DECLARE_META_INTERFACE(RegionSamplingListener)
+
+ virtual void onSampleCollected(float medianLuma) = 0;
+};
+
+class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
+public:
+ BnRegionSamplingListener()
+ : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 5f79804..3dffa8f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -27,11 +27,14 @@
#include <binder/IInterface.h>
+#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
-#include <ui/PixelFormat.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+#include <optional>
#include <vector>
namespace android {
@@ -41,11 +44,13 @@
struct DisplayState;
struct DisplayInfo;
struct DisplayStatInfo;
+struct InputWindowCommands;
class LayerDebugInfo;
class HdrCapabilities;
class IDisplayEventConnection;
class IGraphicBufferProducer;
class ISurfaceComposerClient;
+class IRegionSamplingListener;
class Rect;
enum class FrameEvent;
@@ -68,11 +73,6 @@
eEarlyWakeup = 0x04
};
- enum {
- eDisplayIdMain = 0,
- eDisplayIdHdmi = 1
- };
-
enum Rotation {
eRotateNone = 0,
eRotate90 = 1,
@@ -85,22 +85,11 @@
eVsyncSourceSurfaceFlinger = 1
};
- /* create connection with surface flinger, requires
- * ACCESS_SURFACE_FLINGER permission
+ /*
+ * Create a connection with SurfaceFlinger.
*/
virtual sp<ISurfaceComposerClient> createConnection() = 0;
- /** create a scoped connection with surface flinger.
- * Surfaces produced with this connection will act
- * as children of the passed in GBP. That is to say
- * SurfaceFlinger will draw them relative and confined to
- * drawing of buffers from the layer associated with parent.
- * As this is graphically equivalent in reach to just drawing
- * pixels into the parent buffers, it requires no special permission.
- */
- virtual sp<ISurfaceComposerClient> createScopedConnection(
- const sp<IGraphicBufferProducer>& parent) = 0;
-
/* return an IDisplayEventConnection */
virtual sp<IDisplayEventConnection> createDisplayEventConnection(
VsyncSource vsyncSource = eVsyncSourceApp) = 0;
@@ -116,14 +105,33 @@
*/
virtual void destroyDisplay(const sp<IBinder>& display) = 0;
- /* get the token for the existing default displays. possible values
- * for id are eDisplayIdMain and eDisplayIdHdmi.
+ /* get stable IDs for connected physical displays.
*/
- virtual sp<IBinder> getBuiltInDisplay(int32_t id) = 0;
+ virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0;
+
+ // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
+ std::optional<PhysicalDisplayId> getInternalDisplayId() const {
+ const auto displayIds = getPhysicalDisplayIds();
+ return displayIds.empty() ? std::nullopt : std::make_optional(displayIds.front());
+ }
+
+ /* get token for a physical display given its stable ID obtained via getPhysicalDisplayIds or a
+ * DisplayEventReceiver hotplug event.
+ */
+ virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const = 0;
+
+ // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
+ sp<IBinder> getInternalDisplayToken() const {
+ const auto displayId = getInternalDisplayId();
+ return displayId ? getPhysicalDisplayToken(*displayId) : nullptr;
+ }
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags) = 0;
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@@ -157,9 +165,6 @@
virtual status_t getDisplayStats(const sp<IBinder>& display,
DisplayStatInfo* stats) = 0;
- /* returns display viewport information of the given display */
- virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) = 0;
-
/* indicates which of the configurations returned by getDisplayInfo is
* currently active */
virtual int getActiveConfig(const sp<IBinder>& display) = 0;
@@ -170,6 +175,8 @@
virtual status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes) = 0;
+ virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& primaries) = 0;
virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0;
virtual status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode) = 0;
@@ -183,6 +190,37 @@
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
*
+ * reqDataspace and reqPixelFormat specify the data space and pixel format
+ * of the buffer. The caller should pick the data space and pixel format
+ * that it can consume.
+ *
+ * At the moment, sourceCrop is ignored and is always set to the visible
+ * region (projected display viewport) of the screen.
+ *
+ * reqWidth and reqHeight specifies the size of the buffer. When either
+ * of them is 0, they are set to the size of the logical display viewport.
+ *
+ * When useIdentityTransform is true, layer transformations are disabled.
+ *
+ * rotation specifies the rotation of the source crop (and the pixels in
+ * it) around its center.
+ */
+ virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
+ const ui::Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ Rotation rotation = eRotateNone,
+ bool captureSecureLayers = false) = 0;
+ /**
+ * Capture the specified screen. This requires READ_FRAME_BUFFER
+ * permission. This function will fail if there is a secure window on
+ * screen.
+ *
+ * This function can capture a subregion (the source crop) of the screen
+ * into an sRGB buffer with RGBA_8888 pixel format.
+ * The subregion can be optionally rotated. It will also be scaled to
+ * match the size of the output buffer.
+ *
* At the moment, sourceCrop is ignored and is always set to the visible
* region (projected display viewport) of the screen.
*
@@ -196,16 +234,34 @@
*/
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
- Rotation rotation = eRotateNone) = 0;
+ bool useIdentityTransform, Rotation rotation = eRotateNone) {
+ return captureScreen(display, outBuffer, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+ sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation);
+ }
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
+ *
+ * reqDataspace and reqPixelFormat specify the data space and pixel format
+ * of the buffer. The caller should pick the data space and pixel format
+ * that it can consume.
*/
virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
- sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+ sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
float frameScale = 1.0, bool childrenOnly = false) = 0;
+ /**
+ * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format,
+ * potentially ignoring the root node.
+ */
+ status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
+ const Rect& sourceCrop, float frameScale = 1.0,
+ bool childrenOnly = false) {
+ return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, sourceCrop, frameScale, childrenOnly);
+ }
+
/* Clears the frame statistics for animations.
*
* Requires the ACCESS_SURFACE_FLINGER permission.
@@ -234,29 +290,146 @@
* Requires the ACCESS_SURFACE_FLINGER permission.
*/
virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
+
+ virtual status_t getColorManagement(bool* outGetColorManagement) const = 0;
+
+ /* Gets the composition preference of the default data space and default pixel format,
+ * as well as the wide color gamut data space and wide color gamut pixel format.
+ * If the wide color gamut data space is V0_SRGB, then it implies that the platform
+ * has no wide color gamut support.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+ ui::PixelFormat* defaultPixelFormat,
+ ui::Dataspace* wideColorGamutDataspace,
+ ui::PixelFormat* wideColorGamutPixelFormat) const = 0;
+ /*
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const = 0;
+
+ /* Turns on the color sampling engine on the display.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+ uint8_t componentMask,
+ uint64_t maxFrames) const = 0;
+
+ /* Returns statistics on the color profile of the last frame displayed for a given display
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+ uint64_t timestamp,
+ DisplayedFrameStats* outStats) const = 0;
+
+ /*
+ * Gets whether SurfaceFlinger can support protected content in GPU composition.
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getProtectedContentSupport(bool* outSupported) const = 0;
+
+ /*
+ * Queries whether the given display is a wide color display.
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t isWideColorDisplay(const sp<IBinder>& token,
+ bool* outIsWideColorDisplay) const = 0;
+
+ /* Registers a listener to stream median luma updates from SurfaceFlinger.
+ *
+ * The sampling area is bounded by both samplingArea and the given stopLayerHandle
+ * (i.e., only layers behind the stop layer will be captured and sampled).
+ *
+ * Multiple listeners may be provided so long as they have independent listeners.
+ * If multiple listeners are provided, the effective sampling region for each listener will
+ * be bounded by whichever stop layer has a lower Z value.
+ *
+ * Requires the same permissions as captureLayers and captureScreen.
+ */
+ virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) = 0;
+
+ /*
+ * Removes a listener that was streaming median luma updates from SurfaceFlinger.
+ */
+ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
+
+ /*
+ * Sets the allowed display configurations to be used.
+ * The allowedConfigs in a vector of indexes corresponding to the configurations
+ * returned from getDisplayConfigs().
+ */
+ virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) = 0;
+
+ /*
+ * Returns the allowed display configurations currently set.
+ * The allowedConfigs in a vector of indexes corresponding to the configurations
+ * returned from getDisplayConfigs().
+ */
+ virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs) = 0;
+ /*
+ * Gets whether brightness operations are supported on a display.
+ *
+ * displayToken
+ * The token of the display.
+ * outSupport
+ * An output parameter for whether brightness operations are supported.
+ *
+ * Returns NO_ERROR upon success. Otherwise,
+ * NAME_NOT_FOUND if the display is invalid, or
+ * BAD_VALUE if the output parameter is invalid.
+ */
+ virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const = 0;
+
+ /*
+ * Sets the brightness of a display.
+ *
+ * displayToken
+ * The token of the display whose brightness is set.
+ * brightness
+ * A number between 0.0f (minimum brightness) and 1.0 (maximum brightness), or -1.0f to
+ * turn the backlight off.
+ *
+ * Returns NO_ERROR upon success. Otherwise,
+ * NAME_NOT_FOUND if the display is invalid, or
+ * BAD_VALUE if the brightness is invalid, or
+ * INVALID_OPERATION if brightness operations are not supported.
+ */
+ virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+ float brightness) const = 0;
};
// ----------------------------------------------------------------------------
class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
public:
- enum {
+ enum ISurfaceComposerTag {
// Note: BOOT_FINISHED must remain this value, it is called from
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
- UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
+ CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check
CREATE_DISPLAY_EVENT_CONNECTION,
CREATE_DISPLAY,
DESTROY_DISPLAY,
- GET_BUILT_IN_DISPLAY,
+ GET_PHYSICAL_DISPLAY_TOKEN,
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
GET_SUPPORTED_FRAME_TIMESTAMPS,
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
SET_ACTIVE_CONFIG,
- CONNECT_DISPLAY,
+ CONNECT_DISPLAY_UNUSED, // unused, fails permissions check
CAPTURE_SCREEN,
CAPTURE_LAYERS,
CLEAR_ANIMATION_FRAME_STATS,
@@ -270,8 +443,22 @@
ENABLE_VSYNC_INJECTIONS,
INJECT_VSYNC,
GET_LAYER_DEBUG_INFO,
- CREATE_SCOPED_CONNECTION,
- GET_DISPLAY_VIEWPORT
+ GET_COMPOSITION_PREFERENCE,
+ GET_COLOR_MANAGEMENT,
+ GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+ SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
+ GET_DISPLAYED_CONTENT_SAMPLE,
+ GET_PROTECTED_CONTENT_SUPPORT,
+ IS_WIDE_COLOR_DISPLAY,
+ GET_DISPLAY_NATIVE_PRIMARIES,
+ GET_PHYSICAL_DISPLAY_IDS,
+ ADD_REGION_SAMPLING_LISTENER,
+ REMOVE_REGION_SAMPLING_LISTENER,
+ SET_ALLOWED_DISPLAY_CONFIGS,
+ GET_ALLOWED_DISPLAY_CONFIGS,
+ GET_DISPLAY_BRIGHTNESS_SUPPORT,
+ SET_DISPLAY_BRIGHTNESS,
+ // Always append new enum to the end.
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index b4fd956..32ac9e8 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -18,8 +18,11 @@
#include <binder/IInterface.h>
#include <binder/SafeInterface.h>
+#include <gui/LayerMetadata.h>
#include <ui/PixelFormat.h>
+#include <unordered_map>
+
namespace android {
class FrameStats;
@@ -40,8 +43,9 @@
eProtectedByDRM = 0x00001000,
eCursorWindow = 0x00002000,
- eFXSurfaceNormal = 0x00000000,
+ eFXSurfaceBufferQueue = 0x00000000,
eFXSurfaceColor = 0x00020000,
+ eFXSurfaceBufferState = 0x00040000,
eFXSurfaceContainer = 0x00080000,
eFXSurfaceMask = 0x000F0000,
};
@@ -50,14 +54,18 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
- int32_t ownerUid, sp<IBinder>* handle,
+ uint32_t flags, const sp<IBinder>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
*/
- virtual status_t destroySurface(const sp<IBinder>& handle) = 0;
+ virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags,
+ const sp<IGraphicBufferProducer>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
new file mode 100644
index 0000000..29ab026
--- /dev/null
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 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>
+#include <binder/Parcelable.h>
+#include <binder/SafeInterface.h>
+
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+
+#include <cstdint>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android {
+
+class ITransactionCompletedListener;
+
+using CallbackId = int64_t;
+
+struct CallbackIdsHash {
+ // CallbackId vectors have several properties that let us get away with this simple hash.
+ // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+ // empty we can still hash 0.
+ // 2) CallbackId vectors for the same listener either are identical or contain none of the
+ // same members. It is sufficient to just check the first CallbackId in the vectors. If
+ // they match, they are the same. If they do not match, they are not the same.
+ std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
+ return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front());
+ }
+};
+
+class SurfaceStats : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ SurfaceStats() = default;
+ SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence)
+ : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+
+ sp<IBinder> surfaceControl;
+ nsecs_t acquireTime = -1;
+ sp<Fence> previousReleaseFence;
+};
+
+class TransactionStats : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ nsecs_t latchTime = -1;
+ sp<Fence> presentFence = nullptr;
+ std::vector<SurfaceStats> surfaceStats;
+};
+
+class ListenerStats : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+ const std::unordered_set<CallbackId>& callbackIds);
+
+ sp<ITransactionCompletedListener> listener;
+ std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+};
+
+class ITransactionCompletedListener : public IInterface {
+public:
+ DECLARE_META_INTERFACE(TransactionCompletedListener)
+
+ virtual void onTransactionCompleted(ListenerStats stats) = 0;
+};
+
+class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
+public:
+ BnTransactionCompletedListener()
+ : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+};
+
+class ListenerCallbacks {
+public:
+ ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+ const std::unordered_set<CallbackId>& callbacks)
+ : transactionCompletedListener(listener),
+ callbackIds(callbacks.begin(), callbacks.end()) {}
+
+ ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& ids)
+ : transactionCompletedListener(listener), callbackIds(ids) {}
+
+ sp<ITransactionCompletedListener> transactionCompletedListener;
+ std::vector<CallbackId> callbackIds;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
index 92bd8c5..66a7b4d 100644
--- a/libs/gui/include/gui/LayerDebugInfo.h
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -52,7 +52,6 @@
int32_t mWidth = -1;
int32_t mHeight = -1;
Rect mCrop = Rect::INVALID_RECT;
- Rect mFinalCrop = Rect::INVALID_RECT;
half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf);
uint32_t mFlags = 0;
PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
new file mode 100644
index 0000000..47f0ced
--- /dev/null
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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/Parcelable.h>
+
+#include <unordered_map>
+
+namespace android {
+
+enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 };
+
+struct LayerMetadata : public Parcelable {
+ std::unordered_map<uint32_t, std::vector<uint8_t>> mMap;
+
+ LayerMetadata();
+ LayerMetadata(const LayerMetadata& other);
+ LayerMetadata(LayerMetadata&& other);
+ explicit LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map);
+ LayerMetadata& operator=(const LayerMetadata& other);
+ LayerMetadata& operator=(LayerMetadata&& other);
+
+ // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in
+ // in this whose keys are paired with empty values in other will be erased.
+ bool merge(const LayerMetadata& other, bool eraseEmpty = false);
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ bool has(uint32_t key) const;
+ int32_t getInt32(uint32_t key, int32_t fallback) const;
+ void setInt32(uint32_t key, int32_t value);
+
+ std::string itemToString(uint32_t key, const char* separator) const;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 38de701..35e795c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -22,10 +22,19 @@
#include <utils/Errors.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <math/mat4.h>
+
+#ifndef NO_INPUT
+#include <input/InputWindow.h>
+#endif
+
+#include <gui/LayerMetadata.h>
#include <math/vec3.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
namespace android {
@@ -36,113 +45,183 @@
* Used to communicate layer information between SurfaceFlinger and its clients.
*/
struct layer_state_t {
-
-
enum {
- eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
- eLayerOpaque = 0x02, // SURFACE_OPAQUE
- eLayerSecure = 0x80, // SECURE
+ eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
+ eLayerOpaque = 0x02, // SURFACE_OPAQUE
+ eLayerSecure = 0x80, // SECURE
};
enum {
- ePositionChanged = 0x00000001,
- eLayerChanged = 0x00000002,
- eSizeChanged = 0x00000004,
- eAlphaChanged = 0x00000008,
- eMatrixChanged = 0x00000010,
- eTransparentRegionChanged = 0x00000020,
- eFlagsChanged = 0x00000040,
- eLayerStackChanged = 0x00000080,
- eCropChanged = 0x00000100,
- eDeferTransaction = 0x00000200,
- eFinalCropChanged = 0x00000400,
- eOverrideScalingModeChanged = 0x00000800,
- eGeometryAppliesWithResize = 0x00001000,
- eReparentChildren = 0x00002000,
- eDetachChildren = 0x00004000,
- eRelativeLayerChanged = 0x00008000,
- eReparent = 0x00010000,
- eColorChanged = 0x00020000,
- eDestroySurface = 0x00040000
+ ePositionChanged = 0x00000001,
+ eLayerChanged = 0x00000002,
+ eSizeChanged = 0x00000004,
+ eAlphaChanged = 0x00000008,
+ eMatrixChanged = 0x00000010,
+ eTransparentRegionChanged = 0x00000020,
+ eFlagsChanged = 0x00000040,
+ eLayerStackChanged = 0x00000080,
+ eCropChanged_legacy = 0x00000100,
+ eDeferTransaction_legacy = 0x00000200,
+ eOverrideScalingModeChanged = 0x00000400,
+ eGeometryAppliesWithResize = 0x00000800,
+ eReparentChildren = 0x00001000,
+ eDetachChildren = 0x00002000,
+ eRelativeLayerChanged = 0x00004000,
+ eReparent = 0x00008000,
+ eColorChanged = 0x00010000,
+ eDestroySurface = 0x00020000,
+ eTransformChanged = 0x00040000,
+ eTransformToDisplayInverseChanged = 0x00080000,
+ eCropChanged = 0x00100000,
+ eBufferChanged = 0x00200000,
+ eAcquireFenceChanged = 0x00400000,
+ eDataspaceChanged = 0x00800000,
+ eHdrMetadataChanged = 0x01000000,
+ eSurfaceDamageRegionChanged = 0x02000000,
+ eApiChanged = 0x04000000,
+ eSidebandStreamChanged = 0x08000000,
+ eColorTransformChanged = 0x10000000,
+ eListenerCallbacksChanged = 0x20000000,
+ eInputInfoChanged = 0x40000000,
+ eCornerRadiusChanged = 0x80000000,
+ eFrameChanged = 0x1'00000000,
+ eCachedBufferChanged = 0x2'00000000,
+ eBackgroundColorChanged = 0x4'00000000,
+ eMetadataChanged = 0x8'00000000,
+ eColorSpaceAgnosticChanged = 0x10'00000000,
};
layer_state_t()
- : what(0),
- x(0), y(0), z(0), w(0), h(0), layerStack(0),
- alpha(0), flags(0), mask(0),
- reserved(0), crop(Rect::INVALID_RECT),
- finalCrop(Rect::INVALID_RECT), frameNumber(0),
- overrideScalingMode(-1)
- {
+ : what(0),
+ x(0),
+ y(0),
+ z(0),
+ w(0),
+ h(0),
+ layerStack(0),
+ alpha(0),
+ flags(0),
+ mask(0),
+ reserved(0),
+ crop_legacy(Rect::INVALID_RECT),
+ cornerRadius(0.0f),
+ frameNumber_legacy(0),
+ overrideScalingMode(-1),
+ transform(0),
+ transformToDisplayInverse(false),
+ crop(Rect::INVALID_RECT),
+ frame(Rect::INVALID_RECT),
+ dataspace(ui::Dataspace::UNKNOWN),
+ surfaceDamageRegion(),
+ api(-1),
+ colorTransform(mat4()),
+ bgColorAlpha(0),
+ bgColorDataspace(ui::Dataspace::UNKNOWN),
+ colorSpaceAgnostic(false) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
+ hdrMetadata.validTypes = 0;
}
void merge(const layer_state_t& other);
- status_t write(Parcel& output) const;
- status_t read(const Parcel& input);
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
- struct matrix22_t {
- float dsdx{0};
- float dtdx{0};
- float dtdy{0};
- float dsdy{0};
- };
- sp<IBinder> surface;
- uint32_t what;
- float x;
- float y;
- int32_t z;
- uint32_t w;
- uint32_t h;
- uint32_t layerStack;
- float alpha;
- uint8_t flags;
- uint8_t mask;
- uint8_t reserved;
- matrix22_t matrix;
- Rect crop;
- Rect finalCrop;
- sp<IBinder> barrierHandle;
- sp<IBinder> reparentHandle;
- uint64_t frameNumber;
- int32_t overrideScalingMode;
+ struct matrix22_t {
+ float dsdx{0};
+ float dtdx{0};
+ float dtdy{0};
+ float dsdy{0};
+ };
+ struct cached_buffer_t {
+ sp<IBinder> token = nullptr;
+ int32_t bufferId = -1;
+ };
+ sp<IBinder> surface;
+ uint64_t what;
+ float x;
+ float y;
+ int32_t z;
+ uint32_t w;
+ uint32_t h;
+ uint32_t layerStack;
+ float alpha;
+ uint8_t flags;
+ uint8_t mask;
+ uint8_t reserved;
+ matrix22_t matrix;
+ Rect crop_legacy;
+ float cornerRadius;
+ sp<IBinder> barrierHandle_legacy;
+ sp<IBinder> reparentHandle;
+ uint64_t frameNumber_legacy;
+ int32_t overrideScalingMode;
- sp<IGraphicBufferProducer> barrierGbp;
+ sp<IGraphicBufferProducer> barrierGbp_legacy;
- sp<IBinder> relativeLayerHandle;
+ sp<IBinder> relativeLayerHandle;
- sp<IBinder> parentHandleForChild;
+ sp<IBinder> parentHandleForChild;
- half3 color;
+ half3 color;
- // non POD must be last. see write/read
- Region transparentRegion;
+ // non POD must be last. see write/read
+ Region transparentRegion;
+
+ uint32_t transform;
+ bool transformToDisplayInverse;
+ Rect crop;
+ Rect frame;
+ sp<GraphicBuffer> buffer;
+ sp<Fence> acquireFence;
+ ui::Dataspace dataspace;
+ HdrMetadata hdrMetadata;
+ Region surfaceDamageRegion;
+ int32_t api;
+ sp<NativeHandle> sidebandStream;
+ mat4 colorTransform;
+
+ std::vector<ListenerCallbacks> listenerCallbacks;
+#ifndef NO_INPUT
+ InputWindowInfo inputInfo;
+#endif
+
+ cached_buffer_t cachedBuffer;
+
+ LayerMetadata metadata;
+
+ // The following refer to the alpha, and dataspace, respectively of
+ // the background color layer
+ float bgColorAlpha;
+ ui::Dataspace bgColorDataspace;
+
+ // A color space agnostic layer means the color of this layer can be
+ // interpreted in any color space.
+ bool colorSpaceAgnostic;
};
struct ComposerState {
sp<ISurfaceComposerClient> client;
layer_state_t state;
- status_t write(Parcel& output) const;
- status_t read(const Parcel& input);
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
};
struct DisplayState {
-
enum {
- eOrientationDefault = 0,
- eOrientation90 = 1,
- eOrientation180 = 2,
- eOrientation270 = 3,
- eOrientationUnchanged = 4,
- eOrientationSwapMask = 0x01
+ eOrientationDefault = 0,
+ eOrientation90 = 1,
+ eOrientation180 = 2,
+ eOrientation270 = 3,
+ eOrientationUnchanged = 4,
+ eOrientationSwapMask = 0x01
};
enum {
- eSurfaceChanged = 0x01,
- eLayerStackChanged = 0x02,
- eDisplayProjectionChanged = 0x04,
- eDisplaySizeChanged = 0x08
+ eSurfaceChanged = 0x01,
+ eLayerStackChanged = 0x02,
+ eDisplayProjectionChanged = 0x04,
+ eDisplaySizeChanged = 0x08
};
DisplayState();
@@ -174,21 +253,33 @@
status_t read(const Parcel& input);
};
-static inline
-int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
+struct InputWindowCommands {
+ struct TransferTouchFocusCommand {
+ sp<IBinder> fromToken;
+ sp<IBinder> toToken;
+ };
+
+ std::vector<TransferTouchFocusCommand> transferTouchFocusCommands;
+ bool syncInputWindows{false};
+
+ void merge(const InputWindowCommands& other);
+ void clear();
+ void write(Parcel& output) const;
+ void read(const Parcel& input);
+};
+
+static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
if (lhs.client < rhs.client) return -1;
if (lhs.client > rhs.client) return 1;
- if (lhs.state.surface < rhs.state.surface) return -1;
- if (lhs.state.surface > rhs.state.surface) return 1;
+ if (lhs.state.surface < rhs.state.surface) return -1;
+ if (lhs.state.surface > rhs.state.surface) return 1;
return 0;
}
-static inline
-int compare_type(const DisplayState& lhs, const DisplayState& rhs) {
+static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) {
return compare_type(lhs.token, rhs.token);
}
}; // namespace android
#endif // ANDROID_SF_LAYER_STATE_H
-
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 9aeafae..248e105 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -80,7 +80,7 @@
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
static bool isValid(const sp<Surface>& surface) {
- return surface != NULL && surface->getIGraphicBufferProducer() != NULL;
+ return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr;
}
/* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer.
@@ -218,6 +218,7 @@
int dispatchSetBuffersDataSpace(va_list args);
int dispatchSetBuffersSmpte2086Metadata(va_list args);
int dispatchSetBuffersCta8613Metadata(va_list args);
+ int dispatchSetBuffersHdr10PlusMetadata(va_list args);
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
@@ -249,6 +250,7 @@
virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
+ virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
virtual int setCrop(Rect const* rect);
virtual int setUsage(uint64_t reqUsage);
virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ad8a8b0..39d6d13 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,7 +19,9 @@
#include <stdint.h>
#include <sys/types.h>
+#include <set>
#include <unordered_map>
+#include <unordered_set>
#include <binder/IBinder.h>
@@ -28,14 +30,17 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <gui/CpuConsumer.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
#include <gui/SurfaceControl.h>
#include <math/vec3.h>
-#include <gui/LayerState.h>
namespace android {
@@ -45,17 +50,37 @@
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
+class IRegionSamplingListener;
class Region;
// ---------------------------------------------------------------------------
+struct SurfaceControlStats {
+ SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
+ const sp<Fence>& prevReleaseFence)
+ : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+
+ sp<SurfaceControl> surfaceControl;
+ nsecs_t acquireTime = -1;
+ sp<Fence> previousReleaseFence;
+};
+
+using TransactionCompletedCallbackTakesContext =
+ std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
+ const sp<Fence>& /*presentFence*/,
+ const std::vector<SurfaceControlStats>& /*stats*/)>;
+using TransactionCompletedCallback =
+ std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
+ const std::vector<SurfaceControlStats>& /*stats*/)>;
+
+// ---------------------------------------------------------------------------
+
class SurfaceComposerClient : public RefBase
{
friend class Composer;
public:
SurfaceComposerClient();
SurfaceComposerClient(const sp<ISurfaceComposerClient>& client);
- SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent);
virtual ~SurfaceComposerClient();
// Always make sure we could initialize
@@ -69,7 +94,7 @@
// callback when the composer is dies
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
- void* cookie = NULL, uint32_t flags = 0);
+ void* cookie = nullptr, uint32_t flags = 0);
// Get a list of supported configurations for a given display
static status_t getDisplayConfigs(const sp<IBinder>& display,
@@ -79,9 +104,6 @@
static status_t getDisplayInfo(const sp<IBinder>& display,
DisplayInfo* info);
- // Get the display viewport for the given display
- static status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport);
-
// Get the index of the current active configuration (relative to the list
// returned by getDisplayInfo)
static int getActiveConfig(const sp<IBinder>& display);
@@ -90,10 +112,26 @@
// returned by getDisplayInfo
static status_t setActiveConfig(const sp<IBinder>& display, int id);
+ // Sets the allowed display configurations to be used.
+ // The allowedConfigs in a vector of indexes corresponding to the configurations
+ // returned from getDisplayConfigs().
+ static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs);
+
+ // Returns the allowed display configurations currently set.
+ // The allowedConfigs in a vector of indexes corresponding to the configurations
+ // returned from getDisplayConfigs().
+ static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs);
+
// Gets the list of supported color modes for the given display
static status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes);
+ // Get the coordinates of the display's native color primaries
+ static status_t getDisplayNativePrimaries(const sp<IBinder>& display,
+ ui::DisplayPrimaries& outPrimaries);
+
// Gets the active color mode for the given display
static ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
@@ -104,31 +142,91 @@
/* Triggers screen on/off or low power mode and waits for it to complete */
static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
+ /* Returns the composition preference of the default data space and default pixel format,
+ * as well as the wide color gamut data space and wide color gamut pixel format.
+ * If the wide color gamut data space is V0_SRGB, then it implies that the platform
+ * has no wide color gamut support.
+ */
+ static status_t getCompositionPreference(ui::Dataspace* defaultDataspace,
+ ui::PixelFormat* defaultPixelFormat,
+ ui::Dataspace* wideColorGamutDataspace,
+ ui::PixelFormat* wideColorGamutPixelFormat);
+
+ /*
+ * Gets whether SurfaceFlinger can support protected content in GPU composition.
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ static bool getProtectedContentSupport();
+
+ /**
+ * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it
+ * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection.
+ */
+ static void doDropReferenceTransaction(const sp<IBinder>& handle,
+ const sp<ISurfaceComposerClient>& client);
+
+ // Queries whether a given display is wide color display.
+ static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay);
+
+ /*
+ * Returns whether brightness operations are supported on a display.
+ *
+ * displayToken
+ * The token of the display.
+ *
+ * Returns whether brightness operations are supported on a display or not.
+ */
+ static bool getDisplayBrightnessSupport(const sp<IBinder>& displayToken);
+
+ /*
+ * Sets the brightness of a display.
+ *
+ * displayToken
+ * The token of the display whose brightness is set.
+ * brightness
+ * A number between 0.0 (minimum brightness) and 1.0 (maximum brightness), or -1.0f to
+ * turn the backlight off.
+ *
+ * Returns NO_ERROR upon success. Otherwise,
+ * NAME_NOT_FOUND if the display handle is invalid, or
+ * BAD_VALUE if the brightness value is invalid, or
+ * INVALID_OPERATION if brightness operaetions are not supported.
+ */
+ static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
+
// ------------------------------------------------------------------------
// surface creation / destruction
+ static sp<SurfaceComposerClient> getDefault();
+
//! Create a surface
- sp<SurfaceControl> createSurface(
- const String8& name,// name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
- int32_t ownerUid = -1 // UID of the task
+ sp<SurfaceControl> createSurface(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata() // metadata
);
- status_t createSurfaceChecked(
- const String8& name,// name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- sp<SurfaceControl>* outSurface,
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
- int32_t ownerUid = -1 // UID of the task
+ status_t createSurfaceChecked(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ sp<SurfaceControl>* outSurface,
+ uint32_t flags = 0, // usage flags
+ SurfaceControl* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata() // metadata
+ );
+
+ //! Create a surface
+ sp<SurfaceControl> createWithSurfaceParent(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0, // usage flags
+ Surface* parent = nullptr, // parent
+ LayerMetadata metadata = LayerMetadata() // metadata
);
//! Create a virtual display
@@ -137,9 +235,13 @@
//! Destroy a virtual display
static void destroyDisplay(const sp<IBinder>& display);
- //! Get the token for the existing default displays.
- //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi.
- static sp<IBinder> getBuiltInDisplay(int32_t id);
+ //! Get stable IDs for connected physical displays
+ static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
+ static std::optional<PhysicalDisplayId> getInternalDisplayId();
+
+ //! Get token for a physical display given its stable ID
+ static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
+ static sp<IBinder> getInternalDisplayToken();
static status_t enableVSyncInjections(bool enable);
@@ -151,19 +253,52 @@
}
};
+ struct TCLHash {
+ std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
+ return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
+ }
+ };
+
+ struct CallbackInfo {
+ // All the callbacks that have been requested for a TransactionCompletedListener in the
+ // Transaction
+ std::unordered_set<CallbackId> callbackIds;
+ // All the SurfaceControls that have been modified in this TransactionCompletedListener's
+ // process that require a callback if there is one or more callbackIds set.
+ std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
+ };
+
class Transaction {
std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
SortedVector<DisplayState > mDisplayStates;
+ std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+ mListenerCallbacks;
+
uint32_t mForceSynchronous = 0;
uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
bool mEarlyWakeup = false;
+ // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+ // to be presented. When it is not possible to present at exactly that time, it will be
+ // presented after the time has passed.
+ //
+ // Desired present times that are more than 1 second in the future may be ignored.
+ // When a desired present time has already passed, the transaction will be presented as soon
+ // as possible.
+ //
+ // Transactions from the same process are presented in the same order that they are applied.
+ // The desired present time does not affect this ordering.
+ int64_t mDesiredPresentTime = -1;
+
+ InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
DisplayState& getDisplayState(const sp<IBinder>& token);
+ void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+
public:
Transaction() = default;
virtual ~Transaction() = default;
@@ -203,22 +338,23 @@
float alpha);
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
- Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
- Transaction& setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+ Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key,
+ std::vector<uint8_t> data);
// Defers applying any changes made in this transaction until the Layer
// identified by handle reaches the given frameNumber. If the Layer identified
// by handle is removed, then we will apply this transaction regardless of
// what frame number has been reached.
- Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc,
- const sp<IBinder>& handle,
- uint64_t frameNumber);
- // A variant of deferTransactionUntil which identifies the Layer we wait for by
+ Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+ const sp<IBinder>& handle, uint64_t frameNumber);
+ // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by
// Surface instead of Handle. Useful for clients which may not have the
// SurfaceControl for some of their Surfaces. Otherwise behaves identically.
- Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc,
- const sp<Surface>& barrierSurface,
- uint64_t frameNumber);
+ Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
+ const sp<Surface>& barrierSurface,
+ uint64_t frameNumber);
// Reparents all children of this layer to the new parent handle.
Transaction& reparentChildren(const sp<SurfaceControl>& sc,
const sp<IBinder>& newParentHandle);
@@ -231,6 +367,31 @@
Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
+ // Sets the background color of a layer with the specified color, alpha, and dataspace
+ Transaction& setBackgroundColor(const sp<SurfaceControl>& sc, const half3& color,
+ float alpha, ui::Dataspace dataspace);
+
+ Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
+ Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
+ bool transformToDisplayInverse);
+ Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
+ Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+ Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
+ Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
+ Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
+ Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
+ Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
+ const Region& surfaceDamageRegion);
+ Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api);
+ Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
+ const sp<NativeHandle>& sidebandStream);
+ Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
+ Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic);
+
+ Transaction& addTransactionCompletedCallback(
+ TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
// The child SurfaceControls will not throw exceptions or return errors,
@@ -254,7 +415,18 @@
// freezing the total geometry of a surface until a resize is completed.
Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc);
- Transaction& destroySurface(const sp<SurfaceControl>& sc);
+#ifndef NO_INPUT
+ Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
+ Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
+ Transaction& syncInputWindows();
+#endif
+
+ // Set a color transform matrix on the given layer on the built-in display.
+ Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix,
+ const vec3& translation);
+
+ Transaction& setGeometry(const sp<SurfaceControl>& sc,
+ const Rect& source, const Rect& dst, int transform);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -280,8 +452,6 @@
void setEarlyWakeup();
};
- status_t destroySurface(const sp<IBinder>& id);
-
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
static status_t clearAnimationFrameStats();
@@ -297,13 +467,26 @@
inline sp<ISurfaceComposerClient> getClient() { return mClient; }
+ static status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask);
+ static status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+ uint8_t componentMask, uint64_t maxFrames);
+
+ static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+ uint64_t timestamp, DisplayedFrameStats* outStats);
+ static status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener);
+ static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+
private:
virtual void onFirstRef();
mutable Mutex mLock;
status_t mStatus;
sp<ISurfaceComposerClient> mClient;
- wp<IGraphicBufferProducer> mParent;
};
// ---------------------------------------------------------------------------
@@ -312,17 +495,69 @@
public:
// if cropping isn't required, callers may pass in a default Rect, e.g.:
// capture(display, producer, Rect(), reqWidth, ...);
- static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ,
- bool useIdentityTransform, uint32_t rotation,
- sp<GraphicBuffer>* outBuffer);
- static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale,
- sp<GraphicBuffer>* outBuffer);
- static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+ static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ uint32_t rotation, bool captureSecureLayers, sp<GraphicBuffer>* outBuffer);
+ static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ uint32_t rotation, sp<GraphicBuffer>* outBuffer);
+ static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ float frameScale, sp<GraphicBuffer>* outBuffer);
+ static status_t captureChildLayers(const sp<IBinder>& layerHandle,
+ const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
float frameScale, sp<GraphicBuffer>* outBuffer);
};
// ---------------------------------------------------------------------------
+
+class TransactionCompletedListener : public BnTransactionCompletedListener {
+ TransactionCompletedListener();
+
+ CallbackId getNextIdLocked() REQUIRES(mMutex);
+
+ std::mutex mMutex;
+
+ bool mListening GUARDED_BY(mMutex) = false;
+
+ CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
+
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& iBinder) const {
+ return std::hash<IBinder*>{}(iBinder.get());
+ }
+ };
+
+ struct CallbackTranslation {
+ TransactionCompletedCallback callbackFunction;
+ std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+ };
+
+ std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+
+public:
+ static sp<TransactionCompletedListener> getInstance();
+ static sp<ITransactionCompletedListener> getIInstance();
+
+ void startListeningLocked() REQUIRES(mMutex);
+
+ CallbackId addCallbackFunction(
+ const TransactionCompletedCallback& callbackFunction,
+ const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+ surfaceControls);
+
+ void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
+ const std::unordered_set<CallbackId>& callbackIds);
+
+ // Overrides BnTransactionCompletedListener's onTransactionCompleted
+ void onTransactionCompleted(ListenerStats stats) override;
+};
+
+// ---------------------------------------------------------------------------
+
}; // namespace android
#endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index bd987dd..55efcbf 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -48,18 +48,22 @@
void writeToParcel(Parcel* parcel);
static bool isValid(const sp<SurfaceControl>& surface) {
- return (surface != 0) && surface->isValid();
+ return (surface != nullptr) && surface->isValid();
}
bool isValid() {
- return mHandle!=0 && mClient!=0;
+ return mHandle!=nullptr && mClient!=nullptr;
}
static bool isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
- // release surface data from java
- void clear();
+ // Release the handles assosciated with the SurfaceControl, without reparenting
+ // them off-screen. At the moment if this isn't executed before ~SurfaceControl
+ // is called then the destructor will reparent the layer off-screen for you.
+ void release();
+ // Reparent off-screen and release. This is invoked by the destructor.
+ void destroy();
// disconnect any api that's connected
void disconnect();
@@ -71,10 +75,14 @@
sp<Surface> createSurface() const;
sp<IBinder> getHandle() const;
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+
status_t clearLayerFrameStats() const;
status_t getLayerFrameStats(FrameStats* outStats) const;
sp<SurfaceComposerClient> getClient() const;
+
+ explicit SurfaceControl(const sp<SurfaceControl>& other);
private:
// can't be copied
@@ -94,7 +102,6 @@
sp<Surface> generateSurfaceLocked() const;
status_t validate() const;
- void destroy();
sp<SurfaceComposerClient> mClient;
sp<IBinder> mHandle;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
index c92fa9d..c4d0245 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
@@ -57,7 +57,6 @@
struct H2BGraphicBufferProducer : public ::android::H2BConverter<
HGraphicBufferProducer,
- BGraphicBufferProducer,
BnGraphicBufferProducer> {
explicit H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {}
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
new file mode 100644
index 0000000..211fdd5
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H
+
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+#include <gui/IProducerListener.h>
+#include <hidl/HybridInterface.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using HProducerListener = ::android::hardware::graphics::bufferqueue::V1_0::
+ IProducerListener;
+
+using BProducerListener = ::android::IProducerListener;
+
+class H2BProducerListener
+ : public H2BConverter<HProducerListener, BnProducerListener> {
+public:
+ H2BProducerListener(sp<HProducerListener> const& base);
+ virtual void onBufferReleased() override;
+ virtual bool needsReleaseNotify() override;
+};
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H
+
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
new file mode 100644
index 0000000..1c58167
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H
+
+#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/bufferqueue/2.0/types.h>
+#include <hidl/HidlSupport.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+using HGraphicBufferProducer =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer;
+using BGraphicBufferProducer =
+ ::android::
+ IGraphicBufferProducer;
+using HProducerListener =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IProducerListener;
+
+using ::android::hardware::Return;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+using ::android::hardware::graphics::common::V1_2::HardwareBuffer;
+
+class B2HGraphicBufferProducer : public HGraphicBufferProducer {
+public:
+ B2HGraphicBufferProducer(sp<BGraphicBufferProducer> const& base);
+
+ virtual Return<HStatus> setMaxDequeuedBufferCount(
+ int32_t maxDequeuedBuffers) override;
+
+ virtual Return<void> requestBuffer(
+ int32_t slot,
+ requestBuffer_cb _hidl_cb) override;
+
+ virtual Return<HStatus> setAsyncMode(bool async) override;
+
+ virtual Return<void> dequeueBuffer(
+ DequeueBufferInput const& input,
+ dequeueBuffer_cb _hidl_cb) override;
+
+ virtual Return<HStatus> detachBuffer(int32_t slot) override;
+
+ virtual Return<void> detachNextBuffer(
+ detachNextBuffer_cb _hidl_cb) override;
+
+ virtual Return<void> attachBuffer(
+ HardwareBuffer const& buffer,
+ uint32_t generationNumber,
+ attachBuffer_cb _hidl_cb) override;
+
+ virtual Return<void> queueBuffer(
+ int32_t slot,
+ QueueBufferInput const& input,
+ queueBuffer_cb _hidl_cb) override;
+
+ virtual Return<HStatus> cancelBuffer(
+ int32_t slot,
+ hidl_handle const& fence) override;
+
+ virtual Return<void> query(int32_t what, query_cb _hidl_cb) override;
+
+ virtual Return<void> connect(
+ sp<HProducerListener> const& listener,
+ HConnectionType api,
+ bool producerControlledByApp,
+ connect_cb _hidl_cb) override;
+
+ virtual Return<HStatus> disconnect(HConnectionType api) override;
+
+ virtual Return<HStatus> allocateBuffers(
+ uint32_t width, uint32_t height,
+ uint32_t format, uint64_t usage) override;
+
+ virtual Return<HStatus> allowAllocation(bool allow) override;
+
+ virtual Return<HStatus> setGenerationNumber(uint32_t generationNumber) override;
+
+ virtual Return<HStatus> setDequeueTimeout(int64_t timeoutNs) override;
+
+ virtual Return<uint64_t> getUniqueId() override;
+
+ virtual Return<void> getConsumerName(getConsumerName_cb _hidl_cb) override;
+
+protected:
+ sp<BGraphicBufferProducer> mBase;
+};
+
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h
new file mode 100644
index 0000000..b48a473
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+#include <hidl/Status.h>
+
+#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+using ::android::hardware::Return;
+
+using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0::
+ IProducerListener;
+
+using BProducerListener = ::android::IProducerListener;
+
+struct B2HProducerListener : public HProducerListener {
+ explicit B2HProducerListener(sp<BProducerListener> const& base);
+ Return<void> onBuffersReleased(uint32_t count) override;
+protected:
+ sp<BProducerListener> mBase;
+ bool mNeedsReleaseNotify;
+};
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H
+
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h
new file mode 100644
index 0000000..7dd1617
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <hidl/HybridInterface.h>
+#include <ui/Fence.h>
+
+#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+using ::android::BnGraphicBufferProducer;
+using ::android::IProducerListener;
+using Fence = ::android::Fence;
+
+using HGraphicBufferProducer =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer;
+using HProducerListener =
+ ::android::hardware::graphics::bufferqueue::V2_0::
+ IProducerListener;
+using BGraphicBufferProducer =
+ ::android::IGraphicBufferProducer;
+
+struct H2BGraphicBufferProducer
+ : public ::android::H2BConverter<HGraphicBufferProducer,
+ BnGraphicBufferProducer> {
+ explicit H2BGraphicBufferProducer(
+ sp<HGraphicBufferProducer> const& base) : CBase(base) {}
+
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+ virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override;
+ virtual status_t setAsyncMode(bool async) override;
+ virtual status_t dequeueBuffer(
+ int* slot, sp<Fence>* fence,
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint64_t usage,
+ uint64_t* outBufferAge,
+ FrameEventHistoryDelta* outTimestamps) override;
+ virtual status_t detachBuffer(int slot) override;
+ virtual status_t detachNextBuffer(
+ sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) override;
+ virtual status_t attachBuffer(
+ int* outSlot,
+ sp<GraphicBuffer> const& buffer) override;
+ virtual status_t queueBuffer(
+ int slot,
+ QueueBufferInput const& input,
+ QueueBufferOutput* output) override;
+ virtual status_t cancelBuffer(int slot, sp<Fence> const& fence) override;
+ virtual int query(int what, int* value) override;
+ virtual status_t connect(
+ sp<IProducerListener> const& listener,
+ int api,
+ bool producerControlledByApp,
+ QueueBufferOutput* output) override;
+ virtual status_t disconnect(
+ int api,
+ DisconnectMode mode = DisconnectMode::Api) override;
+ virtual status_t setSidebandStream(sp<NativeHandle> const& stream) override;
+ virtual void allocateBuffers(
+ uint32_t width, uint32_t height,
+ PixelFormat format, uint64_t usage) override;
+ virtual status_t allowAllocation(bool allow) override;
+ virtual status_t setGenerationNumber(uint32_t generationNumber) override;
+ virtual String8 getConsumerName() const override;
+ virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
+ virtual status_t setAutoRefresh(bool autoRefresh) override;
+ virtual status_t setDequeueTimeout(nsecs_t timeout) override;
+ virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence, float outTransformMatrix[16]) override;
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
+ virtual status_t getUniqueId(uint64_t* outId) const override;
+ virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
+};
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
new file mode 100644
index 0000000..898920b
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H
+
+#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
+#include <gui/IProducerListener.h>
+#include <hidl/HybridInterface.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0::
+ IProducerListener;
+
+using BProducerListener = ::android::IProducerListener;
+
+class H2BProducerListener
+ : public H2BConverter<HProducerListener, BnProducerListener> {
+public:
+ H2BProducerListener(sp<HProducerListener> const& base);
+ virtual void onBufferReleased() override;
+ virtual bool needsReleaseNotify() override;
+};
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H
+
diff --git a/libs/gui/include/gui/bufferqueue/2.0/types.h b/libs/gui/include/gui/bufferqueue/2.0/types.h
new file mode 100644
index 0000000..62176ce
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/2.0/types.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H
+
+#include <android/hardware/graphics/bufferqueue/2.0/types.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <hidl/HidlSupport.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V2_0 {
+namespace utils {
+
+// Status
+// ======
+
+using HStatus = ::android::hardware::graphics::bufferqueue::V2_0::
+ Status;
+
+// A status_t value may have flags encoded. These flags are decoded into boolean
+// values if their corresponding output pointers are not null.
+bool b2h(status_t from, HStatus* to,
+ bool* bufferNeedsReallocation = nullptr,
+ bool* releaseAllBuffers = nullptr);
+// Simple 1-to-1 mapping. If BUFFER_NEEDS_REALLOCATION or RELEASE_ALL_BUFFERS
+// needs to be added, it must be done manually afterwards.
+bool h2b(HStatus from, status_t* to);
+
+// Fence
+// =====
+
+using BFence = ::android::Fence;
+// This class manages the lifetime of a copied handle. Its destructor calls
+// native_handle_delete() but not native_handle_close().
+struct HFenceWrapper {
+ HFenceWrapper() = default;
+ // Sets mHandle to a new value.
+ HFenceWrapper(native_handle_t* h);
+ // Deletes mHandle without closing.
+ ~HFenceWrapper();
+ // Deletes mHandle without closing, then sets mHandle to a new value.
+ HFenceWrapper& set(native_handle_t* h);
+ HFenceWrapper& operator=(native_handle_t* h);
+ // Returns a non-owning hidl_handle pointing to mHandle.
+ hidl_handle getHandle() const;
+ operator hidl_handle() const;
+protected:
+ native_handle_t* mHandle{nullptr};
+};
+
+// Does not clone the fd---only copy the fd. The returned HFenceWrapper should
+// not outlive the input Fence object.
+bool b2h(sp<BFence> const& from, HFenceWrapper* to);
+// Clones the fd and puts it in a new Fence object.
+bool h2b(native_handle_t const* from, sp<BFence>* to);
+
+// ConnectionType
+// ==============
+
+using HConnectionType = ::android::hardware::graphics::bufferqueue::V2_0::
+ ConnectionType;
+
+bool b2h(int from, HConnectionType* to);
+bool h2b(HConnectionType from, int* to);
+
+// Rect
+// ====
+
+using BRect = ::android::Rect;
+using HRect = ::android::hardware::graphics::common::V1_2::Rect;
+
+bool b2h(BRect const& from, HRect* to);
+bool h2b(HRect const& from, BRect* to);
+
+// Region
+// ======
+
+using BRegion = ::android::Region;
+using HRegion = ::android::hardware::hidl_vec<HRect>;
+
+bool b2h(BRegion const& from, HRegion* to);
+bool h2b(HRegion const& from, BRegion* to);
+
+// GraphicBuffer
+// =============
+
+using HardwareBuffer = ::android::hardware::graphics::common::V1_2::
+ HardwareBuffer;
+using HardwareBufferDescription = ::android::hardware::graphics::common::V1_2::
+ HardwareBufferDescription;
+
+// Does not clone the handle. The returned HardwareBuffer should not outlive the
+// input GraphicBuffer. Note that HardwareBuffer does not carry the generation
+// number, so this function needs another output argument.
+bool b2h(sp<GraphicBuffer> const& from, HardwareBuffer* to,
+ uint32_t* toGenerationNumber = nullptr);
+// Clones the handle and creates a new GraphicBuffer from the cloned handle.
+// Note that the generation number of the GraphicBuffer has to be set manually
+// afterwards because HardwareBuffer does not have such information.
+bool h2b(HardwareBuffer const& from, sp<GraphicBuffer>* to);
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H
+
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/gui/include/private/gui/BufferQueueThreadState.h
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/gui/include/private/gui/BufferQueueThreadState.h
index e6ac6bf..67dcf62 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/gui/include/private/gui/BufferQueueThreadState.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
namespace android {
-namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// TODO: Replace this with b/127962003
+class BufferQueueThreadState {
+public:
+ static pid_t getCallingPid();
+ static uid_t getCallingUid();
+};
-} // namespace mock
} // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 01e90e0..ab6dcaa 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -16,11 +16,14 @@
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
+ "EndToEndNativeInputTest.cpp",
+ "DisplayedContentSampling_test.cpp",
"FillBuffer.cpp",
"GLTest.cpp",
"IGraphicBufferProducer_test.cpp",
"Malicious.cpp",
"MultiTextureConsumer_test.cpp",
+ "RegionSampling_test.cpp",
"StreamSplitter_test.cpp",
"SurfaceTextureClient_test.cpp",
"SurfaceTextureFBO_test.cpp",
@@ -35,6 +38,7 @@
shared_libs: [
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
+ "libbase",
"liblog",
"libEGL",
"libGLESv1_CM",
@@ -44,15 +48,19 @@
"libgui",
"libhidlbase",
"libhidltransport",
+ "libinput",
"libui",
"libutils",
"libnativewindow"
],
}
-// Build a separate binary for each source file to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+// This test has a main method, and requires a separate binary to be built.
+// To add move tests like this, just add additional cc_test statements,
+// as opposed to adding more source files to this one.
cc_test {
- name: "libgui_separate_binary_test",
+ name: "SurfaceParcelable_test",
test_suites: ["device-tests"],
clang: true,
@@ -61,7 +69,6 @@
"-Werror",
],
- test_per_src: true,
srcs: [
"SurfaceParcelable_test.cpp",
],
@@ -81,3 +88,26 @@
"libdvr_headers",
],
}
+
+cc_test {
+ name: "SamplingDemo",
+
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "SamplingDemo.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libgui",
+ "liblog",
+ "libui",
+ "libutils",
+ ]
+}
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 9a20859..119e888 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -61,7 +61,7 @@
}
void GetMinUndequeuedBufferCount(int* bufferCount) {
- ASSERT_TRUE(bufferCount != NULL);
+ ASSERT_TRUE(bufferCount != nullptr);
ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
bufferCount));
ASSERT_GE(*bufferCount, 0);
@@ -82,7 +82,7 @@
sp<Fence> fence;
input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop,
- &scalingMode, &transform, &fence, NULL);
+ &scalingMode, &transform, &fence, nullptr);
ASSERT_EQ(timestamp, item.mTimestamp);
ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp);
ASSERT_EQ(dataSpace, item.mDataSpace);
@@ -128,17 +128,17 @@
sp<IBinder> binderProducer =
serviceManager->getService(PRODUCER_NAME);
mProducer = interface_cast<IGraphicBufferProducer>(binderProducer);
- EXPECT_TRUE(mProducer != NULL);
+ EXPECT_TRUE(mProducer != nullptr);
sp<IBinder> binderConsumer =
serviceManager->getService(CONSUMER_NAME);
mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);
- EXPECT_TRUE(mConsumer != NULL);
+ EXPECT_TRUE(mConsumer != nullptr);
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
IGraphicBufferProducer::QueueBufferOutput output;
ASSERT_EQ(OK,
- mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output));
+ mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
int slot;
sp<Fence> fence;
@@ -353,8 +353,8 @@
ASSERT_EQ(OK, buffer->unlock());
int newSlot;
- ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer));
- ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL));
+ ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(nullptr, safeToClobberBuffer));
+ ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, nullptr));
ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer));
IGraphicBufferProducer::QueueBufferInput input(0, false,
@@ -412,8 +412,8 @@
int newSlot;
sp<GraphicBuffer> safeToClobberBuffer;
- ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer));
- ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL));
+ ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(nullptr, safeToClobberBuffer));
+ ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr));
ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer));
ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY,
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 36be7d9..00e32d9 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -484,12 +484,12 @@
err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
ASSERT_NO_ERROR(err, "dequeueBuffer error: ");
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
*stride = buf->getStride();
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
ALOGVV("Lock buffer from %p for write", anw.get());
err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
@@ -554,7 +554,7 @@
err = mCC->lockNextBuffer(&b);
ASSERT_NO_ERROR(err, "getNextBuffer error: ");
- ASSERT_TRUE(b.data != NULL);
+ ASSERT_TRUE(b.data != nullptr);
EXPECT_EQ(params.width, b.width);
EXPECT_EQ(params.height, b.height);
EXPECT_EQ(params.format, b.format);
@@ -595,7 +595,7 @@
err = mCC->lockNextBuffer(&b);
ASSERT_NO_ERROR(err, "getNextBuffer error: ");
- ASSERT_TRUE(b.data != NULL);
+ ASSERT_TRUE(b.data != nullptr);
EXPECT_EQ(params.width, b.width);
EXPECT_EQ(params.height, b.height);
EXPECT_EQ(params.format, b.format);
@@ -637,7 +637,7 @@
err = mCC->lockNextBuffer(&b[i]);
ASSERT_NO_ERROR(err, "getNextBuffer error: ");
- ASSERT_TRUE(b[i].data != NULL);
+ ASSERT_TRUE(b[i].data != nullptr);
EXPECT_EQ(params.width, b[i].width);
EXPECT_EQ(params.height, b[i].height);
EXPECT_EQ(params.format, b[i].format);
@@ -660,7 +660,7 @@
err = mCC->lockNextBuffer(&bTooMuch);
ASSERT_NO_ERROR(err, "Did not allow new lock after unlock");
- ASSERT_TRUE(bTooMuch.data != NULL);
+ ASSERT_TRUE(bTooMuch.data != nullptr);
EXPECT_EQ(params.width, bTooMuch.width);
EXPECT_EQ(params.height, bTooMuch.height);
EXPECT_EQ(params.format, bTooMuch.format);
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
new file mode 100644
index 0000000..b647aab
--- /dev/null
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2018 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 <binder/ProcessState.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <inttypes.h>
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+static constexpr uint32_t INVALID_MASK = 0x10;
+class DisplayedContentSamplingTest : public ::testing::Test {
+protected:
+ void SetUp() {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(OK, mComposerClient->initCheck());
+ mDisplayToken = mComposerClient->getInternalDisplayToken();
+ ASSERT_TRUE(mDisplayToken);
+ }
+
+ bool shouldSkipTest() {
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ status_t status =
+ mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format,
+ &dataspace, &componentMask);
+ if (status == PERMISSION_DENIED) {
+ SUCCEED() << "permissions denial, skipping test";
+ return true;
+ }
+ if (status == INVALID_OPERATION) {
+ SUCCEED() << "optional function not supported, skipping test";
+ return true;
+ }
+ return false;
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<IBinder> mDisplayToken;
+ uint8_t componentMask = 0;
+};
+
+TEST_F(DisplayedContentSamplingTest, GetDisplayedContentSamplingAttributesAreSane) {
+ // tradefed infrastructure does not support use of GTEST_SKIP
+ if (shouldSkipTest()) return;
+
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ status_t status =
+ mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format,
+ &dataspace, &componentMask);
+ EXPECT_EQ(OK, status);
+ EXPECT_LE(componentMask, INVALID_MASK);
+}
+
+TEST_F(DisplayedContentSamplingTest, EnableWithInvalidMaskReturnsBadValue) {
+ if (shouldSkipTest()) return;
+
+ status_t status =
+ mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, INVALID_MASK, 0);
+ EXPECT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(DisplayedContentSamplingTest, EnableAndDisableSucceed) {
+ if (shouldSkipTest()) return;
+
+ status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true,
+ componentMask, 10);
+ EXPECT_EQ(OK, status);
+
+ status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask,
+ 0);
+ EXPECT_EQ(OK, status);
+}
+
+TEST_F(DisplayedContentSamplingTest, SelectivelyDisableComponentOk) {
+ if (shouldSkipTest()) return;
+
+ status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true,
+ componentMask, 0);
+ EXPECT_EQ(OK, status);
+
+ // Clear the lowest bit.
+ componentMask &= (componentMask - 1);
+ status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask,
+ 0);
+ EXPECT_EQ(OK, status);
+}
+
+TEST_F(DisplayedContentSamplingTest, SampleCollectionCoherentWithSupportMask) {
+ if (shouldSkipTest()) return;
+
+ DisplayedFrameStats stats;
+ status_t status = mComposerClient->getDisplayedContentSample(mDisplayToken, 0, 0, &stats);
+ EXPECT_EQ(OK, status);
+ if (stats.numFrames <= 0) return;
+
+ if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size());
+ if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size());
+ if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size());
+ if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
new file mode 100644
index 0000000..4a78480
--- /dev/null
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -0,0 +1,503 @@
+/*
+ * Copyright 2018 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 <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <poll.h>
+
+#include <memory>
+
+#include <android/native_window.h>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <input/InputWindow.h>
+#include <input/IInputFlinger.h>
+#include <input/InputTransport.h>
+#include <input/Input.h>
+
+#include <ui/DisplayInfo.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+
+namespace android {
+namespace test {
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+sp<IInputFlinger> getInputFlinger() {
+ sp<IBinder> input(defaultServiceManager()->getService(
+ String16("inputflinger")));
+ if (input == nullptr) {
+ ALOGE("Failed to link to input service");
+ } else { ALOGE("Linked to input"); }
+ return interface_cast<IInputFlinger>(input);
+}
+
+// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
+static const int LAYER_BASE = INT32_MAX - 10;
+
+class InputSurface {
+public:
+ InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
+ mSurfaceControl = sc;
+
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+ mServerChannel->setToken(new BBinder());
+
+ mInputFlinger = getInputFlinger();
+ mInputFlinger->registerInputChannel(mServerChannel);
+
+ populateInputInfo(width, height);
+
+ mInputConsumer = new InputConsumer(mClientChannel);
+ }
+
+ static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
+ int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
+ static std::unique_ptr<InputSurface> makeBufferInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Buffer Surface"), width, height,
+ PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
+ static std::unique_ptr<InputSurface> makeContainerInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
+ InputEvent* consumeEvent() {
+ waitForEventAvailable();
+
+ InputEvent *ev;
+ uint32_t seqId;
+ status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
+ if (consumed != OK) {
+ return nullptr;
+ }
+ mInputConsumer->sendFinishedSignal(seqId, true);
+ return ev;
+ }
+
+ void expectTap(int x, int y) {
+ InputEvent* ev = consumeEvent();
+ EXPECT_TRUE(ev != nullptr);
+ EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ MotionEvent* mev = static_cast<MotionEvent*>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+ EXPECT_EQ(x, mev->getX(0));
+ EXPECT_EQ(y, mev->getY(0));
+
+ ev = consumeEvent();
+ EXPECT_TRUE(ev != nullptr);
+ EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ mev = static_cast<MotionEvent*>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ }
+
+ void expectMotionEvent(int motionEventType, int x, int y) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION);
+ MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(motionEventType, mev->getAction());
+ EXPECT_EQ(x, mev->getX(0));
+ EXPECT_EQ(y, mev->getY(0));
+ }
+
+ void expectNoMotionEvent(int motionEventType) {
+ InputEvent *ev = consumeEvent();
+ if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) {
+ // Didn't find an event or a motion event so assume action didn't occur.
+ return;
+ }
+
+ MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ EXPECT_NE(motionEventType, mev->getAction());
+ }
+
+ ~InputSurface() {
+ mInputFlinger->unregisterInputChannel(mServerChannel);
+ }
+
+ void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
+ const sp<SurfaceControl>&)> transactionBody) {
+ SurfaceComposerClient::Transaction t;
+ transactionBody(t, mSurfaceControl);
+ t.apply(true);
+ }
+
+ void showAt(int x, int y) {
+ SurfaceComposerClient::Transaction t;
+ t.show(mSurfaceControl);
+ t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+ t.setLayer(mSurfaceControl, LAYER_BASE);
+ t.setPosition(mSurfaceControl, x, y);
+ t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
+ t.setAlpha(mSurfaceControl, 1);
+ t.apply(true);
+ }
+
+private:
+ void waitForEventAvailable() {
+ struct pollfd fd;
+
+ fd.fd = mClientChannel->getFd();
+ fd.events = POLLIN;
+ poll(&fd, 1, 3000);
+ }
+
+ void populateInputInfo(int width, int height) {
+ mInputInfo.token = mServerChannel->getToken();
+ mInputInfo.name = "Test info";
+ mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+ mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
+ mInputInfo.dispatchingTimeout = 100000;
+ mInputInfo.globalScaleFactor = 1.0;
+ mInputInfo.canReceiveKeys = true;
+ mInputInfo.hasFocus = true;
+ mInputInfo.hasWallpaper = false;
+ mInputInfo.paused = false;
+
+ mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+
+ // 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;
+
+ mInputInfo.applicationInfo = aInfo;
+ }
+public:
+ sp<SurfaceControl> mSurfaceControl;
+ sp<InputChannel> mServerChannel, mClientChannel;
+ sp<IInputFlinger> mInputFlinger;
+
+ InputWindowInfo mInputInfo;
+
+ PreallocatedInputEventFactory mInputEventFactory;
+ InputConsumer* mInputConsumer;
+};
+
+class InputSurfacesTest : public ::testing::Test {
+public:
+ InputSurfacesTest() {
+ ProcessState::self()->startThreadPool();
+ }
+
+ void SetUp() {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ const auto display = mComposerClient->getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
+
+ // After a new buffer is queued, SurfaceFlinger is notified and will
+ // latch the new buffer on next vsync. Let's heuristically wait for 3
+ // vsyncs.
+ mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+ }
+
+ void TearDown() {
+ mComposerClient->dispose();
+ }
+
+ std::unique_ptr<InputSurface> makeSurface(int width, int height) {
+ return InputSurface::makeColorInputSurface(mComposerClient, width, height);
+ }
+
+ void postBuffer(const sp<SurfaceControl> &layer) {
+ // wait for previous transactions (such as setSize) to complete
+ Transaction().apply(true);
+ ANativeWindow_Buffer buffer = {};
+ EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+ // Request an empty transaction to get applied synchronously to ensure the buffer is
+ // latched.
+ Transaction().apply(true);
+ usleep(mBufferPostDelay);
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+ int32_t mBufferPostDelay;
+};
+
+void injectTap(int x, int y) {
+ char *buf1, *buf2;
+ asprintf(&buf1, "%d", x);
+ asprintf(&buf2, "%d", y);
+ if (fork() == 0) {
+ execlp("input", "input", "tap", buf1, buf2, NULL);
+ }
+}
+
+void injectMotionEvent(std::string event, int x, int y) {
+ char *buf1, *buf2;
+ asprintf(&buf1, "%d", x);
+ asprintf(&buf2, "%d", y);
+ if (fork() == 0) {
+ execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL);
+ }
+}
+
+TEST_F(InputSurfacesTest, can_receive_input) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_TRUE(surface->consumeEvent() != nullptr);
+}
+
+TEST_F(InputSurfacesTest, input_respects_positioning) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+ surface2->showAt(200, 200);
+
+ injectTap(201, 201);
+ surface2->expectTap(1, 1);
+
+ injectTap(101, 101);
+ surface->expectTap(1, 1);
+
+ surface2->doTransaction([](auto &t, auto &sc) {
+ t.setPosition(sc, 100, 100);
+ });
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setPosition(sc, 200, 200);
+ });
+
+ injectTap(101, 101);
+ surface2->expectTap(1, 1);
+
+ injectTap(201, 201);
+ surface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_layering) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+
+ surface->showAt(10, 10);
+ surface2->showAt(10, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setLayer(sc, LAYER_BASE + 1);
+ });
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+
+ surface2->doTransaction([](auto &t, auto &sc) {
+ t.setLayer(sc, LAYER_BASE + 1);
+ });
+
+ injectTap(11, 11);
+ surface2->expectTap(1, 1);
+
+ surface2->doTransaction([](auto &t, auto &sc) {
+ t.hide(sc);
+ });
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
+
+// Surface Insets are set to offset the client content and draw a border around the client surface
+// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
+// of the client content.
+TEST_F(InputSurfacesTest, input_respects_surface_insets) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ bgSurface->showAt(100, 100);
+
+ fgSurface->mInputInfo.surfaceInset = 5;
+ fgSurface->showAt(100, 100);
+
+ injectTap(106, 106);
+ fgSurface->expectTap(1, 1);
+
+ injectTap(101, 101);
+ bgSurface->expectTap(1, 1);
+}
+
+// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
+TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
+ parentSurface->showAt(100, 100);
+
+ childSurface->mInputInfo.surfaceInset = 10;
+ childSurface->showAt(100, 100);
+
+ childSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setPosition(sc, -5, -5);
+ t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+ });
+
+ injectTap(106, 106);
+ childSurface->expectTap(1, 1);
+
+ injectTap(101, 101);
+ parentSurface->expectTap(1, 1);
+}
+
+// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
+TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([](auto &t, auto &sc) {
+ Region transparentRegion(Rect(0, 0, 10, 10));
+ t.setTransparentRegionHint(sc, transparentRegion);
+ });
+ surface->showAt(100, 100);
+ injectTap(101, 101);
+ surface->expectTap(1, 1);
+}
+
+// Ensure we send the input to the right surface when the surface visibility changes due to the
+// first buffer being submitted. ref: b/120839715
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+
+ bgSurface->showAt(10, 10);
+ bufferSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+
+ postBuffer(bufferSurface->mSurfaceControl);
+ injectTap(11, 11);
+ bufferSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+ postBuffer(bufferSurface->mSurfaceControl);
+
+ bgSurface->showAt(10, 10);
+ bufferSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ bufferSurface->expectTap(1, 1);
+
+ bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+
+ bgSurface->showAt(10, 10);
+ fgSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ fgSurface->expectTap(1, 1);
+
+ fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> containerSurface =
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+
+ bgSurface->showAt(10, 10);
+ containerSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ containerSurface->expectTap(1, 1);
+
+ containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+
+ injectTap(11, 11);
+ bgSurface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, transfer_touch_focus) {
+ std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100);
+
+ fromSurface->showAt(10, 10);
+ injectMotionEvent("DOWN", 11, 11);
+ fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1);
+
+ std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100);
+ toSurface->showAt(10, 10);
+
+ sp<IBinder> fromToken = fromSurface->mServerChannel->getToken();
+ sp<IBinder> toToken = toSurface->mServerChannel->getToken();
+ SurfaceComposerClient::Transaction t;
+ t.transferTouchFocus(fromToken, toToken).apply(true);
+
+ injectMotionEvent("UP", 11, 11);
+ toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1);
+ fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP);
+}
+
+TEST_F(InputSurfacesTest, input_respects_outscreen) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(-1, -1);
+
+ injectTap(0, 0);
+ surface->expectTap(1, 1);
+}
+}
+}
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index ccd674f..b60995a 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -93,11 +93,11 @@
android_native_buffer_t* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
&anb));
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
(void**)(&img)));
fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride());
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index a91552f..a1405fc 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -50,7 +50,7 @@
ASSERT_EQ(EGL_SUCCESS, eglGetError());
char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
- if (displaySecsEnv != NULL) {
+ if (displaySecsEnv != nullptr) {
mDisplaySecs = atoi(displaySecsEnv);
if (mDisplaySecs < 0) {
mDisplaySecs = 0;
@@ -67,7 +67,7 @@
String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(),
PIXEL_FORMAT_RGB_888, 0);
- ASSERT_TRUE(mSurfaceControl != NULL);
+ ASSERT_TRUE(mSurfaceControl != nullptr);
ASSERT_TRUE(mSurfaceControl->isValid());
Transaction t;
@@ -117,7 +117,7 @@
sleep(mDisplaySecs);
}
- if (mComposerClient != NULL) {
+ if (mComposerClient != nullptr) {
mComposerClient->dispose();
}
if (mEglContext != EGL_NO_CONTEXT) {
@@ -171,7 +171,7 @@
EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config,
sp<ANativeWindow>& window) const {
- return eglCreateWindowSurface(display, config, window.get(), NULL);
+ return eglCreateWindowSurface(display, config, window.get(), nullptr);
}
::testing::AssertionResult GLTest::checkPixel(int x, int y,
@@ -256,7 +256,7 @@
GLuint shader = glCreateShader(shaderType);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
if (shader) {
- glShaderSource(shader, 1, &pSource, NULL);
+ glShaderSource(shader, 1, &pSource, nullptr);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
glCompileShader(shader);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
@@ -270,7 +270,7 @@
if (infoLen) {
char* buf = (char*) malloc(infoLen);
if (buf) {
- glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ glGetShaderInfoLog(shader, infoLen, nullptr, buf);
printf("Shader compile log:\n%s\n", buf);
free(buf);
FAIL();
@@ -278,7 +278,7 @@
} else {
char* buf = (char*) malloc(0x1000);
if (buf) {
- glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+ glGetShaderInfoLog(shader, 0x1000, nullptr, buf);
printf("Shader compile log:\n%s\n", buf);
free(buf);
FAIL();
@@ -322,7 +322,7 @@
if (bufLength) {
char* buf = (char*) malloc(bufLength);
if (buf) {
- glGetProgramInfoLog(program, bufLength, NULL, buf);
+ glGetProgramInfoLog(program, bufLength, nullptr, buf);
printf("Program link log:\n%s\n", buf);
free(buf);
FAIL();
diff --git a/libs/gui/tests/GLTest.h b/libs/gui/tests/GLTest.h
index f0d27a8..f290b3c 100644
--- a/libs/gui/tests/GLTest.h
+++ b/libs/gui/tests/GLTest.h
@@ -39,7 +39,7 @@
mEglDisplay(EGL_NO_DISPLAY),
mEglSurface(EGL_NO_SURFACE),
mEglContext(EGL_NO_CONTEXT),
- mGlConfig(NULL) {
+ mGlConfig(nullptr) {
}
virtual void SetUp();
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index a35cf11..aef7aed 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -228,9 +228,9 @@
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
sp<GraphicBuffer> *buffer)
{
- ASSERT_TRUE(slot != NULL);
- ASSERT_TRUE(fence != NULL);
- ASSERT_TRUE(buffer != NULL);
+ ASSERT_TRUE(slot != nullptr);
+ ASSERT_TRUE(fence != nullptr);
+ ASSERT_TRUE(buffer != nullptr);
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
@@ -263,7 +263,7 @@
EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN,
TEST_API,
TEST_CONTROLLED_BY_APP,
- /*output*/NULL));
+ /*output*/nullptr));
// Invalid API returns bad value
EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN,
@@ -359,7 +359,7 @@
// TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
// Value was NULL
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL));
+ EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr));
ASSERT_OK(mConsumer->consumerDisconnect());
@@ -465,7 +465,7 @@
// Fence was NULL
{
- sp<Fence> nullFence = NULL;
+ sp<Fence> nullFence = nullptr;
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder().setFence(nullFence).build();
@@ -695,10 +695,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
- if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
- // TODO(b/38137191): Implement BufferHubProducer::detachBuffer
- ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence));
- }
+ ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence));
}
TEST_P(IGraphicBufferProducerTest,
@@ -735,10 +732,7 @@
ASSERT_OK(mProducer->disconnect(TEST_API));
- if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
- // TODO(b/38137191): Implement BufferHubProducer::detachBuffer
- ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot));
- }
+ ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot));
}
TEST_P(IGraphicBufferProducerTest,
@@ -778,18 +772,29 @@
sp<GraphicBuffer> buffer;
setupDequeueRequestBuffer(&slot, &fence, &buffer);
+ ASSERT_TRUE(buffer != nullptr);
- if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
- // TODO(b/38137191): Implement BufferHubProducer::detachBuffer
- ASSERT_OK(mProducer->detachBuffer(slot));
- }
+ ASSERT_OK(mProducer->detachBuffer(slot));
+ EXPECT_OK(buffer->initCheck());
ASSERT_OK(mProducer->disconnect(TEST_API));
- if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) {
- // TODO(b/69981968): Implement BufferHubProducer::attachBuffer
- ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
- }
+ ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer));
+}
+
+TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) {
+ int slot = -1;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+
+ setupDequeueRequestBuffer(&slot, &fence, &buffer);
+ ASSERT_TRUE(buffer != nullptr);
+
+ ASSERT_OK(mProducer->detachBuffer(slot));
+ EXPECT_OK(buffer->initCheck());
+
+ EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
+ EXPECT_OK(buffer->initCheck());
}
#if USE_BUFFER_HUB_AS_BUFFER_QUEUE
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 3a25ac5..7d3d4aa 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -47,7 +47,7 @@
GLTest::TearDown();
}
virtual EGLint const* getContextAttribs() {
- return NULL;
+ return nullptr;
}
virtual EGLint const* getConfigAttribs() {
static EGLint sDefaultConfigAttribs[] = {
@@ -105,7 +105,7 @@
glClear(GL_COLOR_BUFFER_BIT);
for (int i=0 ; i<8 ; i++) {
- mSurface->lock(&buffer, NULL);
+ mSurface->lock(&buffer, nullptr);
memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4);
mSurface->unlockAndPost();
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
new file mode 100644
index 0000000..d33ecfb
--- /dev/null
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright 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 <gtest/gtest.h>
+#include <thread>
+
+#include <binder/ProcessState.h>
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IRegionSamplingListener.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <utils/Looper.h>
+
+using namespace std::chrono_literals;
+
+namespace android::test {
+
+struct ChoreographerSync {
+ ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {}
+ ~ChoreographerSync() = default;
+
+ void notify() const {
+ std::unique_lock<decltype(mutex_)> lk(mutex_);
+
+ auto check_event = [](auto const& ev) -> bool {
+ return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ };
+ DisplayEventReceiver::Event ev_;
+ int evs = receiver_.getEvents(&ev_, 1);
+ auto vsync_event_found = check_event(ev_);
+ while (evs) {
+ evs = receiver_.getEvents(&ev_, 1);
+ vsync_event_found |= check_event(ev_);
+ }
+
+ if (vsync_event_found) {
+ notification_arrived_ = true;
+ cv_.notify_all();
+ }
+ }
+
+ void wait_vsync_notify() const {
+ std::unique_lock<decltype(mutex_)> lk(mutex_);
+ cv_.wait(lk, [this] { return notification_arrived_; });
+ notification_arrived_ = false;
+ }
+
+private:
+ ChoreographerSync(ChoreographerSync const&) = delete;
+ ChoreographerSync& operator=(ChoreographerSync const&) = delete;
+
+ std::mutex mutable mutex_;
+ std::condition_variable mutable cv_;
+ bool mutable notification_arrived_ = false;
+ DisplayEventReceiver& receiver_;
+};
+
+struct ChoreographerSim {
+ static std::unique_ptr<ChoreographerSim> make() {
+ auto receiver = std::make_unique<DisplayEventReceiver>();
+ if (!receiver || receiver->initCheck() == NO_INIT) {
+ ALOGE("No display reciever");
+ return nullptr;
+ }
+ return std::unique_ptr<ChoreographerSim>(new ChoreographerSim(std::move(receiver)));
+ }
+
+ ~ChoreographerSim() {
+ poll_ = false;
+ looper->wake();
+ choreographer_thread_.join();
+ }
+
+ void request_render_wait(std::function<void()> const& render_fn) {
+ display_event_receiver_->requestNextVsync();
+ choreographer_.wait_vsync_notify();
+ render_fn();
+
+ // Purpose is to make sure that the content is latched by the time we sample.
+ // Waiting one vsync after queueing could still race with vsync, so wait for two, after
+ // which the content is pretty reliably on screen.
+ display_event_receiver_->requestNextVsync();
+ choreographer_.wait_vsync_notify();
+ display_event_receiver_->requestNextVsync();
+ choreographer_.wait_vsync_notify();
+ }
+
+private:
+ ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver)
+ : display_event_receiver_{std::move(receiver)},
+ choreographer_{*display_event_receiver_},
+ looper{new Looper(false)} {
+ choreographer_thread_ = std::thread([this] {
+ auto vsync_notify_fd = display_event_receiver_->getFd();
+ looper->addFd(vsync_notify_fd, 0, Looper::EVENT_INPUT,
+ [](int /*fd*/, int /*events*/, void* data) -> int {
+ if (!data) return 0;
+ reinterpret_cast<ChoreographerSync*>(data)->notify();
+ return 1;
+ },
+ const_cast<void*>(reinterpret_cast<void const*>(&choreographer_)));
+
+ while (poll_) {
+ auto const poll_interval =
+ std::chrono::duration_cast<std::chrono::milliseconds>(1s).count();
+ auto rc = looper->pollOnce(poll_interval);
+ if ((rc != Looper::POLL_CALLBACK) && (rc != Looper::POLL_WAKE))
+ ALOGW("Vsync Looper returned: %i\n", rc);
+ }
+ });
+ }
+
+ ChoreographerSim(ChoreographerSim const&) = delete;
+ ChoreographerSim& operator=(ChoreographerSim const&) = delete;
+
+ std::unique_ptr<DisplayEventReceiver> const display_event_receiver_;
+ ChoreographerSync const choreographer_;
+ sp<Looper> looper;
+ std::thread choreographer_thread_;
+ std::atomic<bool> poll_{true};
+};
+
+struct Listener : BnRegionSamplingListener {
+ void onSampleCollected(float medianLuma) override {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ received = true;
+ mLuma = medianLuma;
+ cv.notify_all();
+ };
+ bool wait_event(std::chrono::milliseconds timeout) {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ return cv.wait_for(lk, timeout, [this] { return received; });
+ }
+
+ float luma() {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ return mLuma;
+ }
+
+ void reset() {
+ std::unique_lock<decltype(mutex)> lk(mutex);
+ received = false;
+ }
+
+private:
+ std::condition_variable cv;
+ std::mutex mutex;
+ bool received = false;
+ float mLuma = -0.0f;
+};
+
+// Hoisted to TestSuite setup to avoid flake in test (b/124675919)
+std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr;
+
+struct RegionSamplingTest : ::testing::Test {
+protected:
+ RegionSamplingTest() { ProcessState::self()->startThreadPool(); }
+
+ static void SetUpTestSuite() {
+ gChoreographerSim = ChoreographerSim::make();
+ ASSERT_NE(gChoreographerSim, nullptr);
+ }
+
+ void SetUp() override {
+ mSurfaceComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
+
+ mBackgroundLayer =
+ mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
+ 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+ uint32_t layerPositionBottom = 0x7E000000;
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mBackgroundLayer, layerPositionBottom)
+ .setPosition(mBackgroundLayer, 100, 100)
+ .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5})
+ .show(mBackgroundLayer)
+ .apply();
+
+ mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"),
+ 300, 300, PIXEL_FORMAT_RGBA_8888, 0);
+
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mContentLayer, layerPositionBottom + 1)
+ .setPosition(mContentLayer, 100, 100)
+ .setColor(mContentLayer, half3{0.5, 0.5, 0.5})
+ .show(mContentLayer)
+ .apply();
+
+ mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0,
+ 0, PIXEL_FORMAT_RGBA_8888, 0);
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mTopLayer, layerPositionBottom + 2)
+ .setPosition(mTopLayer, 0, 0)
+ .show(mBackgroundLayer)
+ .apply();
+ }
+
+ void fill_render(uint32_t rgba_value) {
+ auto surface = mContentLayer->getSurface();
+ ANativeWindow_Buffer outBuffer;
+ status_t status = surface->lock(&outBuffer, NULL);
+ ASSERT_EQ(status, android::OK);
+ auto b = reinterpret_cast<uint32_t*>(outBuffer.bits);
+ for (auto i = 0; i < outBuffer.height; i++) {
+ for (auto j = 0; j < outBuffer.width; j++) {
+ b[j] = rgba_value;
+ }
+ b += outBuffer.stride;
+ }
+
+ gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); });
+ }
+
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+ sp<SurfaceControl> mBackgroundLayer;
+ sp<SurfaceControl> mContentLayer;
+ sp<SurfaceControl> mTopLayer;
+
+ uint32_t const rgba_green = 0xFF00FF00;
+ float const luma_green = 0.7152;
+ uint32_t const rgba_blue = 0xFFFF0000;
+ float const luma_blue = 0.0722;
+ float const error_margin = 0.01;
+ float const luma_gray = 0.50;
+};
+
+TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
+ fill_render(rgba_green);
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> listener = new Listener();
+ const Rect sampleArea{100, 100, 200, 200};
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+
+ composer->removeRegionSamplingListener(listener);
+}
+
+TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
+ fill_render(rgba_green);
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> listener = new Listener();
+ const Rect sampleArea{100, 100, 200, 200};
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+
+ EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+
+ listener->reset();
+
+ fill_render(rgba_blue);
+ EXPECT_TRUE(listener->wait_event(300ms))
+ << "timed out waiting for 2nd luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
+
+ composer->removeRegionSamplingListener(listener);
+}
+
+TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
+ fill_render(rgba_green);
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<Listener> greenListener = new Listener();
+ const Rect greenSampleArea{100, 100, 200, 200};
+ composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener);
+
+ sp<Listener> grayListener = new Listener();
+ const Rect graySampleArea{500, 100, 600, 200};
+ composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
+
+ EXPECT_TRUE(grayListener->wait_event(300ms))
+ << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
+ EXPECT_TRUE(greenListener->wait_event(300ms))
+ << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
+
+ composer->removeRegionSamplingListener(greenListener);
+ composer->removeRegionSamplingListener(grayListener);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
new file mode 100644
index 0000000..9891587
--- /dev/null
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "SamplingTest"
+
+#include <chrono>
+#include <thread>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <gui/IRegionSamplingListener.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+#include <private/gui/ComposerService.h>
+#include <utils/Trace.h>
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+class Button : public BnRegionSamplingListener {
+public:
+ Button(const char* name, const Rect& samplingArea) {
+ sp<SurfaceComposerClient> client = new SurfaceComposerClient;
+
+ mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ const int32_t width = samplingArea.getWidth();
+ const int32_t height = samplingArea.getHeight();
+
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mButton, 0x7fffffff)
+ .setCrop_legacy(mButton,
+ {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+ .setPosition(mButton, samplingArea.left + BUTTON_PADDING,
+ samplingArea.top + BUTTON_PADDING)
+ .setColor(mButton, half3{1, 1, 1})
+ .show(mButton)
+ .apply();
+
+ mButtonBlend = client->createSurface(String8(name) + "Blend", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mButtonBlend, 0x7ffffffe)
+ .setCrop_legacy(mButtonBlend,
+ {0, 0, width - 2 * SAMPLE_AREA_PADDING,
+ height - 2 * SAMPLE_AREA_PADDING})
+ .setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
+ samplingArea.top + SAMPLE_AREA_PADDING)
+ .setColor(mButtonBlend, half3{1, 1, 1})
+ .setAlpha(mButtonBlend, 0.2)
+ .show(mButtonBlend)
+ .apply(true);
+
+ const bool HIGHLIGHT_SAMPLING_AREA = false;
+ if (HIGHLIGHT_SAMPLING_AREA) {
+ mSamplingArea =
+ client->createSurface(String8("SamplingArea"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mSamplingArea, 0x7ffffffd)
+ .setCrop_legacy(mSamplingArea, {0, 0, 100, 32})
+ .setPosition(mSamplingArea, 490, 1606)
+ .setColor(mSamplingArea, half3{0, 1, 0})
+ .setAlpha(mSamplingArea, 0.1)
+ .show(mSamplingArea)
+ .apply();
+ }
+ }
+
+ sp<IBinder> getStopLayerHandle() { return mButtonBlend->getHandle(); }
+
+private:
+ static const int32_t BLEND_WIDTH = 2;
+ static const int32_t SAMPLE_AREA_PADDING = 8;
+ static const int32_t BUTTON_PADDING = BLEND_WIDTH + SAMPLE_AREA_PADDING;
+
+ void setColor(float color) {
+ const float complement = std::fmod(color + 0.5f, 1.0f);
+ SurfaceComposerClient::Transaction{}
+ .setColor(mButton, half3{complement, complement, complement})
+ .setColor(mButtonBlend, half3{color, color, color})
+ .apply();
+ }
+
+ void onSampleCollected(float medianLuma) override {
+ ATRACE_CALL();
+ setColor(medianLuma);
+ }
+
+ sp<SurfaceComposerClient> mClient;
+ sp<SurfaceControl> mButton;
+ sp<SurfaceControl> mButtonBlend;
+ sp<SurfaceControl> mSamplingArea;
+};
+
+} // namespace android
+
+using namespace android;
+
+int main(int, const char**) {
+ const Rect homeButtonArea{490, 1606, 590, 1654};
+ sp<android::Button> homeButton = new android::Button("HomeButton", homeButtonArea);
+ const Rect backButtonArea{200, 1606, 248, 1654};
+ sp<android::Button> backButton = new android::Button("BackButton", backButtonArea);
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ composer->addRegionSamplingListener(homeButtonArea, homeButton->getStopLayerHandle(),
+ homeButton);
+ composer->addRegionSamplingListener(backButtonArea, backButton->getStopLayerHandle(),
+ backButton);
+
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+
+ return 0;
+}
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index d5b2f00..65e09f2 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -29,7 +29,6 @@
#include <utils/Thread.h>
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-#define CROP_EXT_STR "EGL_ANDROID_image_crop"
namespace android {
@@ -39,7 +38,7 @@
mEglDisplay(EGL_NO_DISPLAY),
mEglSurface(EGL_NO_SURFACE),
mEglContext(EGL_NO_CONTEXT),
- mEglConfig(NULL) {
+ mEglConfig(nullptr) {
}
virtual void SetUp() {
@@ -82,7 +81,7 @@
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
- mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0);
+ mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
@@ -127,7 +126,7 @@
TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
sp<IGraphicBufferProducer> ist(mSTC->getIGraphicBufferProducer());
- ASSERT_TRUE(ist != NULL);
+ ASSERT_TRUE(ist != nullptr);
}
TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) {
@@ -155,7 +154,7 @@
EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion));
ASSERT_EQ(EGL_SUCCESS, eglGetError());
- EGLConfig myConfig = {0};
+ EGLConfig myConfig = {nullptr};
EGLint numConfigs = 0;
EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
@@ -172,7 +171,7 @@
ASSERT_EQ(EGL_SUCCESS, eglGetError());
EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(),
- NULL);
+ nullptr);
EXPECT_NE(EGL_NO_SURFACE, eglSurface);
EXPECT_EQ(EGL_SUCCESS, eglGetError());
@@ -185,7 +184,7 @@
TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) {
- EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL);
+ EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), nullptr);
EXPECT_NE(EGL_NO_SURFACE, eglSurface);
EXPECT_EQ(EGL_SUCCESS, eglGetError());
@@ -638,18 +637,6 @@
}
TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) {
- // Query to see if the image crop extension exists
- EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
- size_t cropExtLen = strlen(CROP_EXT_STR);
- size_t extsLen = strlen(exts);
- bool equal = !strcmp(CROP_EXT_STR, exts);
- bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1);
- bool atEnd = (cropExtLen+1) < extsLen &&
- !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1));
- bool inMiddle = strstr(exts, " " CROP_EXT_STR " ");
- bool hasEglAndroidImageCrop = equal || atStart || atEnd || inMiddle;
-
android_native_buffer_t* buf[3];
float mtx[16] = {};
android_native_rect_t crop;
@@ -669,17 +656,15 @@
ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers
mST->getTransformMatrix(mtx);
- // If the egl image crop extension is not present, this accounts for the
- // .5 texel shrink for each edge that's included in the transform matrix
- // to avoid texturing outside the crop region. Otherwise the crop is not
- // included in the transform matrix.
- EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5, mtx[0]);
+ // This accounts for the .5 texel shrink for each edge that's included in
+ // the transform matrix to avoid texturing outside the crop region.
+ EXPECT_EQ(0.5f, mtx[0]);
EXPECT_EQ(0.f, mtx[1]);
EXPECT_EQ(0.f, mtx[2]);
EXPECT_EQ(0.f, mtx[3]);
EXPECT_EQ(0.f, mtx[4]);
- EXPECT_EQ(hasEglAndroidImageCrop ? -1 : -0.5, mtx[5]);
+ EXPECT_EQ(-0.5f, mtx[5]);
EXPECT_EQ(0.f, mtx[6]);
EXPECT_EQ(0.f, mtx[7]);
@@ -688,8 +673,8 @@
EXPECT_EQ(1.f, mtx[10]);
EXPECT_EQ(0.f, mtx[11]);
- EXPECT_EQ(hasEglAndroidImageCrop ? 0 : 0.0625f, mtx[12]);
- EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5625f, mtx[13]);
+ EXPECT_EQ(0.0625f, mtx[12]);
+ EXPECT_EQ(0.5625f, mtx[13]);
EXPECT_EQ(0.f, mtx[14]);
EXPECT_EQ(1.f, mtx[15]);
}
@@ -753,7 +738,7 @@
ASSERT_EQ(EGL_SUCCESS, eglGetError());
mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT,
- 0);
+ nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
@@ -765,7 +750,7 @@
GLConsumer::TEXTURE_EXTERNAL, true, false));
sp<Surface> stc(new Surface(producer));
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
- static_cast<ANativeWindow*>(stc.get()), NULL);
+ static_cast<ANativeWindow*>(stc.get()), nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]);
}
diff --git a/libs/gui/tests/SurfaceTextureFBO.h b/libs/gui/tests/SurfaceTextureFBO.h
index 7f1ae84..70f988d 100644
--- a/libs/gui/tests/SurfaceTextureFBO.h
+++ b/libs/gui/tests/SurfaceTextureFBO.h
@@ -34,7 +34,7 @@
glGenTextures(1, &mFboTex);
glBindTexture(GL_TEXTURE_2D, mFboTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(),
- getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindTexture(GL_TEXTURE_2D, 0);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index 0134273..f34561f 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -39,12 +39,12 @@
android_native_buffer_t* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with green
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255,
0, 255);
@@ -63,7 +63,7 @@
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
buf = GraphicBuffer::from(anb);
diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
index 2ce20eb..03975b1 100644
--- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h
+++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
@@ -158,7 +158,7 @@
}
virtual void TearDown() {
- if (mProducerThread != NULL) {
+ if (mProducerThread != nullptr) {
mProducerThread->requestExitAndWait();
}
mProducerThread.clear();
@@ -167,7 +167,7 @@
}
void runProducerThread(const sp<ProducerThread> producerThread) {
- ASSERT_TRUE(mProducerThread == NULL);
+ ASSERT_TRUE(mProducerThread == nullptr);
mProducerThread = producerThread;
producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
mProducerEglContext);
diff --git a/libs/gui/tests/SurfaceTextureGLToGL.h b/libs/gui/tests/SurfaceTextureGLToGL.h
index 5d43a48..3a87c12 100644
--- a/libs/gui/tests/SurfaceTextureGLToGL.h
+++ b/libs/gui/tests/SurfaceTextureGLToGL.h
@@ -38,7 +38,7 @@
void SetUpWindowAndContext() {
mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
+ mANW.get(), nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 5639286..e2b4f3d 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -40,12 +40,12 @@
ANativeWindowBuffer* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with the a checkerboard pattern
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
fillYV12Buffer(img, texWidth, texHeight, buf->getStride());
buf->unlock();
@@ -90,12 +90,12 @@
ANativeWindowBuffer* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with the a checkerboard pattern
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
fillYV12Buffer(img, texWidth, texHeight, buf->getStride());
buf->unlock();
@@ -155,11 +155,11 @@
ANativeWindowBuffer* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
- ASSERT_TRUE(anb != NULL);
+ ASSERT_TRUE(anb != nullptr);
sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop);
buf->unlock();
@@ -234,7 +234,7 @@
&anb) != NO_ERROR) {
return false;
}
- if (anb == NULL) {
+ if (anb == nullptr) {
return false;
}
@@ -248,7 +248,7 @@
int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2;
int yuvTexStrideU = yuvTexStrideV;
- uint8_t* img = NULL;
+ uint8_t* img = nullptr;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
// Gray out all the test pixels first, so we're more likely to
@@ -457,7 +457,7 @@
&anb) != NO_ERROR) {
return false;
}
- if (anb == NULL) {
+ if (anb == nullptr) {
return false;
}
if (mANW->queueBuffer(mANW.get(), anb, -1)
@@ -641,7 +641,7 @@
&anb) != NO_ERROR) {
return false;
}
- if (anb == NULL) {
+ if (anb == nullptr) {
return false;
}
if (mANW->queueBuffer(mANW.get(), anb, -1)
@@ -654,7 +654,7 @@
&anb) != NO_ERROR) {
return false;
}
- if (anb == NULL) {
+ if (anb == nullptr) {
return false;
}
if (mANW->queueBuffer(mANW.get(), anb, -1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 959aafc..06fe86c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -72,7 +72,7 @@
mSurfaceControl = mComposerClient->createSurface(
String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mSurfaceControl != NULL);
+ ASSERT_TRUE(mSurfaceControl != nullptr);
ASSERT_TRUE(mSurfaceControl->isValid());
Transaction t;
@@ -81,7 +81,7 @@
.apply());
mSurface = mSurfaceControl->getSurface();
- ASSERT_TRUE(mSurface != NULL);
+ ASSERT_TRUE(mSurface != nullptr);
}
virtual void TearDown() {
@@ -131,11 +131,14 @@
// Verify the screenshot works with no protected buffers.
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
+
+ const sp<IBinder> display = sf->getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
- 64, 64, 0, 0x7fffffff, false));
+ ASSERT_EQ(NO_ERROR,
+ sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
NATIVE_WINDOW_API_CPU));
@@ -145,7 +148,7 @@
ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
GRALLOC_USAGE_PROTECTED));
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
- ANativeWindowBuffer* buf = 0;
+ ANativeWindowBuffer* buf = nullptr;
status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
if (err) {
@@ -165,8 +168,9 @@
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
- 64, 64, 0, 0x7fffffff, false));
+ ASSERT_EQ(NO_ERROR,
+ sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
}
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -205,7 +209,7 @@
}
TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
- const android_dataspace TEST_DATASPACE = HAL_DATASPACE_SRGB;
+ const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -364,10 +368,17 @@
78.0,
62.0,
};
+
+ std::vector<uint8_t> hdr10plus;
+ hdr10plus.push_back(0xff);
+
int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086);
ASSERT_EQ(error, NO_ERROR);
error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3);
ASSERT_EQ(error, NO_ERROR);
+ error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(),
+ hdr10plus.data());
+ ASSERT_EQ(error, NO_ERROR);
}
TEST_F(SurfaceTest, DynamicSetBufferCount) {
@@ -536,10 +547,6 @@
}
sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
- sp<ISurfaceComposerClient> createScopedConnection(
- const sp<IGraphicBufferProducer>& /* parent */) override {
- return nullptr;
- }
sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
override {
return nullptr;
@@ -547,10 +554,14 @@
sp<IBinder> createDisplay(const String8& /*displayName*/,
bool /*secure*/) override { return nullptr; }
void destroyDisplay(const sp<IBinder>& /*display */) override {}
- sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
+ std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
+ sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
void setTransactionState(const Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
- override {}
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ const sp<IBinder>& /*applyToken*/,
+ const InputWindowCommands& /*inputWindowCommands*/,
+ int64_t /*desiredPresentTime*/) override {}
+
void bootFinished() override {}
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& /*surface*/) const override {
@@ -581,9 +592,6 @@
Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
status_t getDisplayStats(const sp<IBinder>& /*display*/,
DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
- status_t getDisplayViewport(const sp<IBinder>& /*display*/, Rect* /*outViewport*/) override {
- return NO_ERROR;
- }
int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
override {
@@ -593,21 +601,30 @@
Vector<ColorMode>* /*outColorModes*/) override {
return NO_ERROR;
}
+ status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/,
+ ui::DisplayPrimaries& /*primaries*/) override {
+ return NO_ERROR;
+ }
ColorMode getActiveColorMode(const sp<IBinder>& /*display*/)
override {
return ColorMode::NATIVE;
}
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
- status_t captureScreen(const sp<IBinder>& /*display*/,
- sp<GraphicBuffer>* /*outBuffer*/,
- Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
- int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/,
- bool /*useIdentityTransform*/,
- Rotation /*rotation*/) override { return NO_ERROR; }
+ status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
+ const ui::Dataspace /*reqDataspace*/,
+ const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/,
+ uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+ bool /*useIdentityTransform*/, Rotation /*rotation*/,
+ bool /*captureSecureLayers*/) override {
+ return NO_ERROR;
+ }
virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/,
- sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/,
- float /*frameScale*/, bool /*childrenOnly*/) override {
+ sp<GraphicBuffer>* /*outBuffer*/,
+ const ui::Dataspace /*reqDataspace*/,
+ const ui::PixelFormat /*reqPixelFormat*/,
+ const Rect& /*sourceCrop*/, float /*frameScale*/,
+ bool /*childrenOnly*/) override {
return NO_ERROR;
}
status_t clearAnimationFrameStats() override { return NO_ERROR; }
@@ -625,6 +642,59 @@
status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
return NO_ERROR;
}
+ status_t getCompositionPreference(
+ ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/,
+ ui::Dataspace* /*outWideColorGamutDataspace*/,
+ ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override {
+ return NO_ERROR;
+ }
+ status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/,
+ ui::PixelFormat* /*outFormat*/,
+ ui::Dataspace* /*outDataspace*/,
+ uint8_t* /*outComponentMask*/) const override {
+ return NO_ERROR;
+ }
+ status_t setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/,
+ uint8_t /*componentMask*/,
+ uint64_t /*maxFrames*/) const override {
+ return NO_ERROR;
+ }
+ status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/,
+ uint64_t /*timestamp*/,
+ DisplayedFrameStats* /*outStats*/) const override {
+ return NO_ERROR;
+ }
+
+ status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; }
+ status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; }
+
+ status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
+ status_t getDisplayBrightnessSupport(const sp<IBinder>& /*displayToken*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/,
+ float /*brightness*/) const override {
+ return NO_ERROR;
+ }
+
+ status_t addRegionSamplingListener(const Rect& /*samplingArea*/,
+ const sp<IBinder>& /*stopLayerHandle*/,
+ const sp<IRegionSamplingListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
+ status_t removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
+ status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
+ const std::vector<int32_t>& /*allowedConfigs*/) override {
+ return NO_ERROR;
+ }
+ status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
+ std::vector<int32_t>* /*outAllowedConfigs*/) override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 2f39976..2d78811 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -28,6 +28,7 @@
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "TouchVideoFrame.cpp",
"VirtualKeyMap.cpp",
],
@@ -43,7 +44,10 @@
android: {
srcs: [
"IInputFlinger.cpp",
+ "InputApplication.cpp",
"InputTransport.cpp",
+ "InputWindow.cpp",
+ "ISetInputWindowsListener.cpp",
"VelocityControl.cpp",
"VelocityTracker.cpp",
],
@@ -51,6 +55,7 @@
shared_libs: [
"libutils",
"libbinder",
+ "libui"
],
sanitize: {
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index 003e73d..d6a73bf 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -23,7 +23,6 @@
#include <input/IInputFlinger.h>
-
namespace android {
class BpInputFlinger : public BpInterface<IInputFlinger> {
@@ -31,23 +30,85 @@
explicit BpInputFlinger(const sp<IBinder>& impl) :
BpInterface<IInputFlinger>(impl) { }
- virtual status_t doSomething() {
+ virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) {
Parcel data, reply;
data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
- remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply);
- return reply.readInt32();
+
+ 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 transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+
+ data.writeStrongBinder(fromToken);
+ data.writeStrongBinder(toToken);
+ remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, 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 DO_SOMETHING_TRANSACTION: {
+ case SET_INPUT_WINDOWS_TRANSACTION: {
CHECK_INTERFACE(IInputFlinger, data, reply);
- reply->writeInt32(0);
+ 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 = new InputChannel();
+ channel->read(data);
+ registerInputChannel(channel);
+ break;
+ }
+ case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
+ CHECK_INTERFACE(IInputFlinger, data, reply);
+ sp<InputChannel> channel = new InputChannel();
+ channel->read(data);
+ unregisterInputChannel(channel);
+ break;
+ }
+ case TRANSFER_TOUCH_FOCUS: {
+ CHECK_INTERFACE(IInputFlinger, data, reply);
+ sp<IBinder> fromToken = data.readStrongBinder();
+ sp<IBinder> toToken = data.readStrongBinder();
+ transferTouchFocus(fromToken, toToken);
break;
}
default:
diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp
new file mode 100644
index 0000000..a0330da
--- /dev/null
+++ b/libs/input/ISetInputWindowsListener.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 a624663..9fd25f9 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -29,16 +29,29 @@
namespace android {
+const char* motionClassificationToString(MotionClassification classification) {
+ switch (classification) {
+ case MotionClassification::NONE:
+ return "NONE";
+ case MotionClassification::AMBIGUOUS_GESTURE:
+ return "AMBIGUOUS_GESTURE";
+ case MotionClassification::DEEP_PRESS:
+ return "DEEP_PRESS";
+ }
+}
+
// --- InputEvent ---
-void InputEvent::initialize(int32_t deviceId, int32_t source) {
+void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
mDeviceId = deviceId;
mSource = source;
+ mDisplayId = displayId;
}
void InputEvent::initialize(const InputEvent& from) {
mDeviceId = from.mDeviceId;
mSource = from.mSource;
+ mDisplayId = from.mDisplayId;
}
// --- KeyEvent ---
@@ -54,6 +67,7 @@
void KeyEvent::initialize(
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t flags,
int32_t keyCode,
@@ -62,7 +76,7 @@
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
- InputEvent::initialize(deviceId, source);
+ InputEvent::initialize(deviceId, source, displayId);
mAction = action;
mFlags = flags;
mKeyCode = keyCode;
@@ -128,15 +142,24 @@
}
}
-void PointerCoords::scale(float scaleFactor) {
+void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) {
// No need to scale pressure or size since they are normalized.
// No need to scale orientation since it is meaningless to do so.
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+
+ // If there is a global scale factor, it is included in the windowX/YScale
+ // so we don't need to apply it twice to the X/Y axes.
+ // However we don't want to apply any windowXYScale not included in the global scale
+ // to the TOUCH_MAJOR/MINOR coordinates.
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
+}
+
+void PointerCoords::scale(float globalScaleFactor) {
+ scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
}
void PointerCoords::applyOffset(float xOffset, float yOffset) {
@@ -215,12 +238,14 @@
void MotionEvent::initialize(
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t actionButton,
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
+ MotionClassification classification,
float xOffset,
float yOffset,
float xPrecision,
@@ -230,13 +255,14 @@
size_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
- InputEvent::initialize(deviceId, source);
+ InputEvent::initialize(deviceId, source, displayId);
mAction = action;
mActionButton = actionButton;
mFlags = flags;
mEdgeFlags = edgeFlags;
mMetaState = metaState;
mButtonState = buttonState;
+ mClassification = classification;
mXOffset = xOffset;
mYOffset = yOffset;
mXPrecision = xPrecision;
@@ -250,13 +276,14 @@
}
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
- InputEvent::initialize(other->mDeviceId, other->mSource);
+ InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
mAction = other->mAction;
mActionButton = other->mActionButton;
mFlags = other->mFlags;
mEdgeFlags = other->mEdgeFlags;
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
+ mClassification = other->mClassification;
mXOffset = other->mXOffset;
mYOffset = other->mYOffset;
mXPrecision = other->mXPrecision;
@@ -341,15 +368,15 @@
mYOffset += yOffset;
}
-void MotionEvent::scale(float scaleFactor) {
- mXOffset *= scaleFactor;
- mYOffset *= scaleFactor;
- mXPrecision *= scaleFactor;
- mYPrecision *= scaleFactor;
+void MotionEvent::scale(float globalScaleFactor) {
+ mXOffset *= globalScaleFactor;
+ mYOffset *= globalScaleFactor;
+ mXPrecision *= globalScaleFactor;
+ mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
+ mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
}
}
@@ -431,12 +458,14 @@
mDeviceId = parcel->readInt32();
mSource = parcel->readInt32();
+ mDisplayId = parcel->readInt32();
mAction = parcel->readInt32();
mActionButton = parcel->readInt32();
mFlags = parcel->readInt32();
mEdgeFlags = parcel->readInt32();
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
+ mClassification = static_cast<MotionClassification>(parcel->readByte());
mXOffset = parcel->readFloat();
mYOffset = parcel->readFloat();
mXPrecision = parcel->readFloat();
@@ -480,12 +509,14 @@
parcel->writeInt32(mDeviceId);
parcel->writeInt32(mSource);
+ parcel->writeInt32(mDisplayId);
parcel->writeInt32(mAction);
parcel->writeInt32(mActionButton);
parcel->writeInt32(mFlags);
parcel->writeInt32(mEdgeFlags);
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
+ parcel->writeByte(static_cast<int8_t>(mClassification));
parcel->writeFloat(mXOffset);
parcel->writeFloat(mYOffset);
parcel->writeFloat(mXPrecision);
diff --git a/services/inputflinger/InputApplication.cpp b/libs/input/InputApplication.cpp
similarity index 64%
rename from services/inputflinger/InputApplication.cpp
rename to libs/input/InputApplication.cpp
index 9e90631..7936f50 100644
--- a/services/inputflinger/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "InputApplication"
-#include "InputApplication.h"
+#include <input/InputApplication.h>
#include <android/log.h>
@@ -25,7 +25,7 @@
// --- InputApplicationHandle ---
InputApplicationHandle::InputApplicationHandle() :
- mInfo(NULL) {
+ mInfo(nullptr) {
}
InputApplicationHandle::~InputApplicationHandle() {
@@ -35,8 +35,25 @@
void InputApplicationHandle::releaseInfo() {
if (mInfo) {
delete mInfo;
- mInfo = NULL;
+ mInfo = nullptr;
}
}
+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;
+}
+
} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4287abe..4db9e06 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -20,9 +20,12 @@
#include <unistd.h>
#include <ctype.h>
+#include <android-base/stringprintf.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
+using android::base::StringPrintf;
+
namespace android {
static const char* CONFIGURATION_FILE_DIR[] = {
@@ -41,65 +44,62 @@
return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_');
}
-static void appendInputDeviceConfigurationFileRelativePath(String8& path,
- const String8& name, InputDeviceConfigurationFileType type) {
- path.append(CONFIGURATION_FILE_DIR[type]);
- for (size_t i = 0; i < name.length(); i++) {
- char ch = name[i];
- if (!isValidNameChar(ch)) {
- ch = '_';
- }
- path.append(&ch, 1);
- }
- path.append(CONFIGURATION_FILE_EXTENSION[type]);
+static void appendInputDeviceConfigurationFileRelativePath(std::string& path,
+ const std::string& name, InputDeviceConfigurationFileType type) {
+ path += CONFIGURATION_FILE_DIR[type];
+ path += name;
+ path += CONFIGURATION_FILE_EXTENSION[type];
}
-String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
+std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
- String8 versionPath(getInputDeviceConfigurationFilePathByName(
- String8::format("Vendor_%04x_Product_%04x_Version_%04x",
+ std::string versionPath = getInputDeviceConfigurationFilePathByName(
+ StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
- type));
- if (!versionPath.isEmpty()) {
+ type);
+ if (!versionPath.empty()) {
return versionPath;
}
}
// Try vendor product.
- String8 productPath(getInputDeviceConfigurationFilePathByName(
- String8::format("Vendor_%04x_Product_%04x",
+ std::string productPath = getInputDeviceConfigurationFilePathByName(
+ StringPrintf("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
- type));
- if (!productPath.isEmpty()) {
+ type);
+ if (!productPath.empty()) {
return productPath;
}
}
// Try device name.
- return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
+ return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
}
-String8 getInputDeviceConfigurationFilePathByName(
- const String8& name, InputDeviceConfigurationFileType type) {
+std::string getInputDeviceConfigurationFilePathByName(
+ const std::string& name, InputDeviceConfigurationFileType type) {
// Search system repository.
- String8 path;
+ std::string path;
// Treblized input device config files will be located /odm/usr or /vendor/usr.
const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
for (size_t i = 0; i < size(rootsForPartition); i++) {
- path.setTo(rootsForPartition[i]);
- path.append("/usr/");
+ if (rootsForPartition[i] == nullptr) {
+ continue;
+ }
+ path = rootsForPartition[i];
+ path += "/usr/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
ALOGD("Probing for system provided input device configuration file: path='%s'",
- path.string());
+ path.c_str());
#endif
- if (!access(path.string(), R_OK)) {
+ if (!access(path.c_str(), R_OK)) {
#if DEBUG_PROBE
ALOGD("Found");
#endif
@@ -109,13 +109,17 @@
// Search user repository.
// TODO Should only look here if not in safe mode.
- path.setTo(getenv("ANDROID_DATA"));
- path.append("/system/devices/");
+ path = "";
+ char *androidData = getenv("ANDROID_DATA");
+ if (androidData != nullptr) {
+ path += androidData;
+ }
+ path += "/system/devices/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
- ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
+ ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str());
#endif
- if (!access(path.string(), R_OK)) {
+ if (!access(path.c_str(), R_OK)) {
#if DEBUG_PROBE
ALOGD("Found");
#endif
@@ -125,16 +129,28 @@
// Not found.
#if DEBUG_PROBE
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
- name.string(), type);
+ name.c_str(), type);
#endif
- return String8();
+ return "";
+}
+
+// --- InputDeviceIdentifier
+
+std::string InputDeviceIdentifier::getCanonicalName() const {
+ std::string replacedName = name;
+ for (char& ch : replacedName) {
+ if (!isValidNameChar(ch)) {
+ ch = '_';
+ }
+ }
+ return replacedName;
}
// --- InputDeviceInfo ---
InputDeviceInfo::InputDeviceInfo() {
- initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), false, false);
+ initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false);
}
InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
@@ -150,7 +166,7 @@
}
void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber,
- const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal,
+ const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
bool hasMic) {
mId = id;
mGeneration = generation;
@@ -170,12 +186,12 @@
int32_t axis, uint32_t source) const {
size_t numRanges = mMotionRanges.size();
for (size_t i = 0; i < numRanges; i++) {
- const MotionRange& range = mMotionRanges.itemAt(i);
+ const MotionRange& range = mMotionRanges[i];
if (range.axis == axis && range.source == source) {
return ⦥
}
}
- return NULL;
+ return nullptr;
}
void InputDeviceInfo::addSource(uint32_t source) {
@@ -185,11 +201,11 @@
void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
float flat, float fuzz, float resolution) {
MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
- mMotionRanges.add(range);
+ mMotionRanges.push_back(range);
}
void InputDeviceInfo::addMotionRange(const MotionRange& range) {
- mMotionRanges.add(range);
+ mMotionRanges.push_back(range);
}
} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 03f593f..e13b40e 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -30,6 +30,7 @@
#include <cutils/properties.h>
#include <log/log.h>
+#include <binder/Parcel.h>
#include <input/InputTransport.h>
namespace android {
@@ -58,6 +59,18 @@
// far into the future. This time is further bounded by 50% of the last time delta.
static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS;
+/**
+ * System property for enabling / disabling touch resampling.
+ * Resampling extrapolates / interpolates the reported touch event coordinates to better
+ * align them to the VSYNC signal, thus resulting in smoother scrolling performance.
+ * Resampling is not needed (and should be disabled) on hardware that already
+ * has touch events triggered by VSYNC.
+ * Set to "1" to enable resampling (default).
+ * Set to "0" to disable resampling.
+ * Resampling is enabled by default.
+ */
+static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling";
+
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
@@ -156,6 +169,8 @@
msg->body.motion.metaState = body.motion.metaState;
// int32_t buttonState
msg->body.motion.buttonState = body.motion.buttonState;
+ // MotionClassification classification
+ msg->body.motion.classification = body.motion.classification;
// int32_t edgeFlags
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
@@ -200,15 +215,13 @@
// --- InputChannel ---
InputChannel::InputChannel(const std::string& name, int fd) :
- mName(name), mFd(fd) {
+ mName(name) {
#if DEBUG_CHANNEL_LIFECYCLE
ALOGD("Input channel constructed: name='%s', fd=%d",
mName.c_str(), fd);
#endif
- int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
- LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
- "non-blocking. errno=%d", mName.c_str(), errno);
+ setFd(fd);
}
InputChannel::~InputChannel() {
@@ -220,6 +233,18 @@
::close(mFd);
}
+void InputChannel::setFd(int fd) {
+ if (mFd > 0) {
+ ::close(mFd);
+ }
+ mFd = fd;
+ if (mFd > 0) {
+ int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
+ "non-blocking. errno=%d", mName.c_str(), errno);
+ }
+}
+
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
@@ -328,10 +353,51 @@
sp<InputChannel> InputChannel::dup() const {
int fd = ::dup(getFd());
- return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
+ return fd >= 0 ? new InputChannel(getName(), fd) : nullptr;
}
+status_t InputChannel::write(Parcel& out) const {
+ status_t s = out.writeString8(String8(getName().c_str()));
+
+ if (s != OK) {
+ return s;
+ }
+ s = out.writeStrongBinder(mToken);
+ if (s != OK) {
+ return s;
+ }
+
+ s = out.writeDupFileDescriptor(getFd());
+
+ return s;
+}
+
+status_t InputChannel::read(const Parcel& from) {
+ mName = from.readString8();
+ mToken = from.readStrongBinder();
+
+ int rawFd = from.readFileDescriptor();
+ setFd(::dup(rawFd));
+
+ if (mFd < 0) {
+ return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+sp<IBinder> InputChannel::getToken() const {
+ return mToken;
+}
+
+void InputChannel::setToken(const sp<IBinder>& token) {
+ if (mToken != nullptr) {
+ ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str());
+ }
+ mToken = token;
+}
+
// --- InputPublisher ---
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
@@ -345,6 +411,7 @@
uint32_t seq,
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t flags,
int32_t keyCode,
@@ -372,6 +439,7 @@
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
+ msg.body.key.displayId = displayId;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
@@ -394,6 +462,7 @@
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
+ MotionClassification classification,
float xOffset,
float yOffset,
float xPrecision,
@@ -405,12 +474,14 @@
const PointerCoords* pointerCoords) {
#if DEBUG_TRANSPORT_ACTIONS
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, xOffset=%f, yOffset=%f, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32,
mChannel->getName().c_str(), seq,
- deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState,
+ deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
+ buttonState, motionClassificationToString(classification),
xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
#endif
@@ -437,6 +508,7 @@
msg.body.motion.edgeFlags = edgeFlags;
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
+ msg.body.motion.classification = classification;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
@@ -485,31 +557,18 @@
}
bool InputConsumer::isTouchResamplingEnabled() {
- char value[PROPERTY_VALUE_MAX];
- int length = property_get("ro.input.noresample", value, NULL);
- if (length > 0) {
- if (!strcmp("1", value)) {
- return false;
- }
- if (strcmp("0", value)) {
- ALOGD("Unrecognized property value for 'ro.input.noresample'. "
- "Use '1' or '0'.");
- }
- }
- return true;
+ return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
}
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
- bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
- int32_t* displayId) {
+ bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime);
#endif
*outSeq = 0;
- *outEvent = NULL;
- *displayId = -1; // Invalid display.
+ *outEvent = nullptr;
// Fetch the next input message.
// Loop until an event can be returned or no additional events are received.
@@ -524,7 +583,7 @@
if (result) {
// Consume the next batched event unless batches are being held for later.
if (consumeBatches || result != WOULD_BLOCK) {
- result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId);
+ result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
@@ -568,7 +627,7 @@
// 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, displayId);
+ batch, batch.samples.size(), outSeq, outEvent);
mBatches.removeAt(batchIndex);
if (result) {
return result;
@@ -602,7 +661,7 @@
initializeMotionEvent(motionEvent, &mMsg);
*outSeq = mMsg.body.motion.seq;
*outEvent = motionEvent;
- *displayId = mMsg.body.motion.displayId;
+
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
mChannel->getName().c_str(), *outSeq);
@@ -620,14 +679,13 @@
}
status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) {
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
status_t result;
for (size_t i = mBatches.size(); i > 0; ) {
i--;
Batch& batch = mBatches.editItemAt(i);
if (frameTime < 0) {
- result = consumeSamples(factory, batch, batch.samples.size(),
- outSeq, outEvent, displayId);
+ result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
mBatches.removeAt(i);
return result;
}
@@ -641,11 +699,11 @@
continue;
}
- result = consumeSamples(factory, batch, split + 1, outSeq, outEvent, displayId);
+ result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
const InputMessage* next;
if (batch.samples.isEmpty()) {
mBatches.removeAt(i);
- next = NULL;
+ next = nullptr;
} else {
next = &batch.samples.itemAt(0);
}
@@ -659,7 +717,7 @@
}
status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) {
+ Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) {
MotionEvent* motionEvent = factory->createMotionEvent();
if (! motionEvent) return NO_MEMORY;
@@ -674,7 +732,6 @@
mSeqChains.push(seqChain);
addSample(motionEvent, &msg);
} else {
- *displayId = msg.body.motion.displayId;
initializeMotionEvent(motionEvent, &msg);
}
chain = msg.body.motion.seq;
@@ -1030,6 +1087,7 @@
event->initialize(
msg->body.key.deviceId,
msg->body.key.source,
+ msg->body.key.displayId,
msg->body.key.action,
msg->body.key.flags,
msg->body.key.keyCode,
@@ -1052,12 +1110,14 @@
event->initialize(
msg->body.motion.deviceId,
msg->body.motion.source,
+ msg->body.motion.displayId,
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.xOffset,
msg->body.motion.yOffset,
msg->body.motion.xPrecision,
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
new file mode 100644
index 0000000..5a60347
--- /dev/null
+++ b/libs/input/InputWindow.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 "InputWindow"
+#define LOG_NDEBUG 0
+
+#include <binder/Parcel.h>
+#include <input/InputWindow.h>
+#include <input/InputTransport.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);
+}
+
+bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
+ return touchableRegion.contains(x,y);
+}
+
+bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
+ return x >= frameLeft && x < frameRight
+ && 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;
+}
+
+bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
+ return frameLeft < other->frameRight && frameRight > other->frameLeft
+ && frameTop < other->frameBottom && frameBottom > other->frameTop;
+}
+
+status_t InputWindowInfo::write(Parcel& output) const {
+ if (token == nullptr) {
+ output.writeInt32(0);
+ return OK;
+ }
+ output.writeInt32(1);
+ status_t s = output.writeStrongBinder(token);
+ if (s != OK) return s;
+
+ 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(layer);
+ 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.writeWeakBinder(touchableRegionCropHandle);
+ return OK;
+}
+
+InputWindowInfo InputWindowInfo::read(const Parcel& from) {
+ InputWindowInfo ret;
+
+ if (from.readInt32() == 0) {
+ return ret;
+ }
+
+ sp<IBinder> token = from.readStrongBinder();
+ if (token == nullptr) {
+ return ret;
+ }
+
+ ret.token = token;
+ 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.layer = from.readInt32();
+ 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.readWeakBinder();
+
+ return ret;
+}
+
+InputWindowInfo::InputWindowInfo(const Parcel& from) {
+ *this = read(from);
+}
+
+// --- InputWindowHandle ---
+
+InputWindowHandle::InputWindowHandle() {
+}
+
+InputWindowHandle::~InputWindowHandle() {
+}
+
+void InputWindowHandle::releaseChannel() {
+ mInfo.token.clear();
+}
+
+sp<IBinder> InputWindowHandle::getToken() const {
+ return mInfo.token;
+}
+
+void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
+ mInfo = handle->mInfo;
+}
+
+} // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index cba1111..e189d20 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -106,14 +106,14 @@
}
}
-status_t KeyCharacterMap::load(const String8& filename,
+status_t KeyCharacterMap::load(const std::string& filename,
Format format, sp<KeyCharacterMap>* outMap) {
outMap->clear();
Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
+ status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
- ALOGE("Error %d opening key character map file %s.", status, filename.string());
+ ALOGE("Error %d opening key character map file %s.", status, filename.c_str());
} else {
status = load(tokenizer, format, outMap);
delete tokenizer;
@@ -121,12 +121,12 @@
return status;
}
-status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
+status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents,
Format format, sp<KeyCharacterMap>* outMap) {
outMap->clear();
Tokenizer* tokenizer;
- status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
+ status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
if (status) {
ALOGE("Error %d opening key character map.", status);
} else {
@@ -164,10 +164,10 @@
sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
const sp<KeyCharacterMap>& overlay) {
- if (overlay == NULL) {
+ if (overlay == nullptr) {
return base;
}
- if (base == NULL) {
+ if (base == nullptr) {
return overlay;
}
@@ -468,7 +468,7 @@
// Try to find the most general behavior that maps to this character.
// For example, the base key behavior will usually be last in the list.
- const Behavior* found = NULL;
+ const Behavior* found = nullptr;
for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) {
if (behavior->character == ch) {
found = behavior;
@@ -487,7 +487,7 @@
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
outEvents.push();
KeyEvent& event = outEvents.editTop();
- event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD,
+ event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
0, keyCode, 0, metaState, 0, time, time);
}
@@ -605,11 +605,11 @@
map->mType = parcel->readInt32();
size_t numKeys = parcel->readInt32();
if (parcel->errorCheck()) {
- return NULL;
+ return nullptr;
}
if (numKeys > MAX_KEYS) {
ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS);
- return NULL;
+ return nullptr;
}
for (size_t i = 0; i < numKeys; i++) {
@@ -617,7 +617,7 @@
char16_t label = parcel->readInt32();
char16_t number = parcel->readInt32();
if (parcel->errorCheck()) {
- return NULL;
+ return nullptr;
}
Key* key = new Key();
@@ -625,14 +625,14 @@
key->number = number;
map->mKeys.add(keyCode, key);
- Behavior* lastBehavior = NULL;
+ Behavior* lastBehavior = nullptr;
while (parcel->readInt32()) {
int32_t metaState = parcel->readInt32();
char16_t character = parcel->readInt32();
int32_t fallbackKeyCode = parcel->readInt32();
int32_t replacementKeyCode = parcel->readInt32();
if (parcel->errorCheck()) {
- return NULL;
+ return nullptr;
}
Behavior* behavior = new Behavior();
@@ -649,7 +649,7 @@
}
if (parcel->errorCheck()) {
- return NULL;
+ return nullptr;
}
}
return map;
@@ -666,7 +666,7 @@
parcel->writeInt32(keyCode);
parcel->writeInt32(key->label);
parcel->writeInt32(key->number);
- for (const Behavior* behavior = key->firstBehavior; behavior != NULL;
+ for (const Behavior* behavior = key->firstBehavior; behavior != nullptr;
behavior = behavior->next) {
parcel->writeInt32(1);
parcel->writeInt32(behavior->metaState);
@@ -683,12 +683,12 @@
// --- KeyCharacterMap::Key ---
KeyCharacterMap::Key::Key() :
- label(0), number(0), firstBehavior(NULL) {
+ label(0), number(0), firstBehavior(nullptr) {
}
KeyCharacterMap::Key::Key(const Key& other) :
label(other.label), number(other.number),
- firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
+ firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) {
}
KeyCharacterMap::Key::~Key() {
@@ -704,11 +704,11 @@
// --- KeyCharacterMap::Behavior ---
KeyCharacterMap::Behavior::Behavior() :
- next(NULL), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) {
+ next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) {
}
KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
- next(other.next ? new Behavior(*other.next) : NULL),
+ next(other.next ? new Behavior(*other.next) : nullptr),
metaState(other.metaState), character(other.character),
fallbackKeyCode(other.fallbackKeyCode),
replacementKeyCode(other.replacementKeyCode) {
@@ -944,7 +944,7 @@
properties.add(Property(PROPERTY_NUMBER));
} else {
int32_t metaState;
- status_t status = parseModifier(token, &metaState);
+ status_t status = parseModifier(token.string(), &metaState);
if (status) {
ALOGE("%s: Expected a property name or modifier, got '%s'.",
mTokenizer->getLocation().string(), token.string());
@@ -1137,7 +1137,7 @@
return NO_ERROR;
}
-status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) {
+status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) {
if (token == "base") {
*outMetaState = 0;
return NO_ERROR;
@@ -1145,7 +1145,7 @@
int32_t combinedMeta = 0;
- const char* str = token.string();
+ const char* str = token.c_str();
const char* start = str;
for (const char* cur = str; ; cur++) {
char ch = *cur;
@@ -1164,7 +1164,7 @@
}
if (combinedMeta & metaState) {
ALOGE("%s: Duplicate modifier combination '%s'.",
- mTokenizer->getLocation().string(), token.string());
+ mTokenizer->getLocation().string(), token.c_str());
return BAD_VALUE;
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 2b2f13e..efca68d 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -49,13 +49,13 @@
KeyLayoutMap::~KeyLayoutMap() {
}
-status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
+status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
outMap->clear();
Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
+ status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
- ALOGE("Error %d opening key layout map file %s.", status, filename.string());
+ ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
} else {
sp<KeyLayoutMap> map = new KeyLayoutMap();
if (!map.get()) {
@@ -117,14 +117,15 @@
return &mKeysByScanCode.valueAt(index);
}
}
- return NULL;
+ return nullptr;
}
-status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
+status_t KeyLayoutMap::findScanCodesForKey(
+ int32_t keyCode, std::vector<int32_t>* outScanCodes) const {
const size_t N = mKeysByScanCode.size();
for (size_t i=0; i<N; i++) {
if (mKeysByScanCode.valueAt(i).keyCode == keyCode) {
- outScanCodes->add(mKeysByScanCode.keyAt(i));
+ outScanCodes->push_back(mKeysByScanCode.keyAt(i));
}
}
return NO_ERROR;
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 11842ee..0c22bfe 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -45,22 +45,22 @@
String8 keyLayoutName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
- status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
+ status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
- deviceIdenfifier.name.string(), keyLayoutName.string());
+ deviceIdenfifier.name.c_str(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
- status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
+ status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
- deviceIdenfifier.name.string(), keyLayoutName.string());
+ deviceIdenfifier.name.c_str(), keyLayoutName.string());
}
}
@@ -70,30 +70,30 @@
}
// Try searching by device identifier.
- if (probeKeyMap(deviceIdenfifier, String8::empty())) {
+ if (probeKeyMap(deviceIdenfifier, "")) {
return OK;
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
- if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
+ if (probeKeyMap(deviceIdenfifier, "Generic")) {
return OK;
}
// Try the Virtual key map as a last resort.
- if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
+ if (probeKeyMap(deviceIdenfifier, "Virtual")) {
return OK;
}
// Give up!
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
- deviceIdenfifier.name.string());
+ deviceIdenfifier.name.c_str());
return NAME_NOT_FOUND;
}
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
- const String8& keyMapName) {
+ const std::string& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
@@ -104,10 +104,10 @@
}
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name) {
- String8 path(getPath(deviceIdentifier, name,
+ const std::string& name) {
+ std::string path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
- if (path.isEmpty()) {
+ if (path.empty()) {
return NAME_NOT_FOUND;
}
@@ -116,15 +116,15 @@
return status;
}
- keyLayoutFile.setTo(path);
+ keyLayoutFile = path;
return OK;
}
status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name) {
- String8 path(getPath(deviceIdentifier, name,
- INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP));
- if (path.isEmpty()) {
+ const std::string& name) {
+ std::string path = getPath(deviceIdentifier, name,
+ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP);
+ if (path.empty()) {
return NAME_NOT_FOUND;
}
@@ -134,13 +134,13 @@
return status;
}
- keyCharacterMapFile.setTo(path);
+ keyCharacterMapFile = path;
return OK;
}
-String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
- const String8& name, InputDeviceConfigurationFileType type) {
- return name.isEmpty()
+std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
+ const std::string& name, InputDeviceConfigurationFileType type) {
+ return name.empty()
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
: getInputDeviceConfigurationFilePathByName(name, type);
}
@@ -174,7 +174,7 @@
}
}
- return strstr(deviceIdentifier.name.string(), "-keypad");
+ return strstr(deviceIdentifier.name.c_str(), "-keypad");
}
static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
new file mode 100644
index 0000000..8a4298a
--- /dev/null
+++ b/libs/input/TouchVideoFrame.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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/TouchVideoFrame.h>
+
+namespace android {
+
+TouchVideoFrame::TouchVideoFrame(uint32_t height, uint32_t width, std::vector<int16_t> data,
+ const struct timeval& timestamp) :
+ mHeight(height), mWidth(width),mData(std::move(data)), mTimestamp(timestamp) {
+}
+
+bool TouchVideoFrame::operator==(const TouchVideoFrame& rhs) const {
+ return mHeight == rhs.mHeight
+ && mWidth == rhs.mWidth
+ && mData == rhs.mData
+ && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec
+ && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec;
+}
+
+uint32_t TouchVideoFrame::getHeight() const { return mHeight; }
+
+uint32_t TouchVideoFrame::getWidth() const { return mWidth; }
+
+const std::vector<int16_t>& TouchVideoFrame::getData() const { return mData; }
+
+const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }
+
+void TouchVideoFrame::rotate(int32_t orientation) {
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ rotateQuarterTurn(true /*clockwise*/);
+ break;
+ case DISPLAY_ORIENTATION_180:
+ rotate180();
+ break;
+ case DISPLAY_ORIENTATION_270:
+ rotateQuarterTurn(false /*clockwise*/);
+ break;
+ }
+}
+
+/**
+ * Rotate once clockwise by a quarter turn === rotate 90 degrees
+ * Rotate once counterclockwise by a quarter turn === rotate 270 degrees
+ * For a clockwise rotation:
+ * An element at position (i, j) is rotated to (j, height - i - 1)
+ * For a counterclockwise rotation:
+ * An element at position (i, j) is rotated to (width - j - 1, i)
+ */
+void TouchVideoFrame::rotateQuarterTurn(bool clockwise) {
+ std::vector<int16_t> rotated(mData.size());
+ for (size_t i = 0; i < mHeight; i++) {
+ for (size_t j = 0; j < mWidth; j++) {
+ size_t iRotated, jRotated;
+ if (clockwise) {
+ iRotated = j;
+ jRotated = mHeight - i - 1;
+ } else {
+ iRotated = mWidth - j - 1;
+ jRotated = i;
+ }
+ size_t indexRotated = iRotated * mHeight + jRotated;
+ rotated[indexRotated] = mData[i * mWidth + j];
+ }
+ }
+ mData = std::move(rotated);
+ std::swap(mHeight, mWidth);
+}
+
+/**
+ * An element at position (i, j) is rotated to (height - i - 1, width - j - 1)
+ * This is equivalent to moving element [i] to position [height * width - i - 1]
+ * Since element at [height * width - i - 1] would move to position [i],
+ * we can just swap elements [i] and [height * width - i - 1].
+ */
+void TouchVideoFrame::rotate180() {
+ if (mData.size() == 0) {
+ return;
+ }
+ // Just need to swap elements i and (height * width - 1 - i)
+ for (size_t i = 0; i < mData.size() / 2; i++) {
+ std::swap(mData[i], mData[mHeight * mWidth - 1 - i]);
+ }
+}
+
+} // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index f834c55..42d774e 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -117,7 +117,7 @@
// Allow the default strategy to be overridden using a system property for debugging.
if (!strategy) {
- int length = property_get("debug.velocitytracker.strategy", value, NULL);
+ int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
if (length > 0) {
strategy = value;
} else {
@@ -141,7 +141,7 @@
bool VelocityTracker::configureStrategy(const char* strategy) {
mStrategy = createStrategy(strategy);
- return mStrategy != NULL;
+ return mStrategy != nullptr;
}
VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
@@ -206,7 +206,7 @@
// time to adjust to changes in direction.
return new LegacyVelocityTrackerStrategy();
}
- return NULL;
+ return nullptr;
}
void VelocityTracker::clear() {
diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp
index 28ea717..865366b 100644
--- a/libs/input/VirtualKeyMap.cpp
+++ b/libs/input/VirtualKeyMap.cpp
@@ -28,10 +28,6 @@
// Enables debug output for the parser.
#define DEBUG_PARSER 0
-// Enables debug output for parser performance.
-#define DEBUG_PARSER_PERFORMANCE 0
-
-
namespace android {
static const char* WHITESPACE = " \t\r";
@@ -46,39 +42,28 @@
VirtualKeyMap::~VirtualKeyMap() {
}
-status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {
- *outMap = NULL;
-
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
- if (status) {
- ALOGE("Error %d opening virtual key map file %s.", status, filename.string());
- } else {
- VirtualKeyMap* map = new VirtualKeyMap();
- if (!map) {
- ALOGE("Error allocating virtual key map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map, tokenizer);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (status) {
- delete map;
- } else {
- *outMap = map;
- }
- }
- delete tokenizer;
+std::unique_ptr<VirtualKeyMap> VirtualKeyMap::load(const std::string& filename) {
+ Tokenizer* t;
+ status_t status = Tokenizer::open(String8(filename.c_str()), &t);
+ if (status != OK) {
+ ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str());
+ return nullptr;
}
- return status;
+ std::unique_ptr<Tokenizer> tokenizer(t);
+ // Using 'new' to access a non-public constructor
+ std::unique_ptr<VirtualKeyMap> map(new VirtualKeyMap());
+ if (!map) {
+ ALOGE("Error allocating virtual key map.");
+ return nullptr;
+ }
+
+ Parser parser(map.get(), tokenizer.get());
+ status = parser.parse();
+ if (status != OK) {
+ return nullptr;
+ }
+
+ return map;
}
@@ -127,7 +112,7 @@
"width=%d, height=%d",
defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);
#endif
- mMap->mVirtualKeys.push(defn);
+ mMap->mVirtualKeys.push_back(defn);
} while (consumeFieldDelimiterAndSkipWhitespace());
if (!mTokenizer->isEol()) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index f06119f..ade931e 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -3,8 +3,11 @@
name: "libinput_tests",
srcs: [
"InputChannel_test.cpp",
+ "InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
+ "InputWindow_test.cpp",
+ "TouchVideoFrame_test.cpp",
"VelocityTracker_test.cpp",
],
cflags: [
@@ -33,5 +36,14 @@
"-O0",
"-Wall",
"-Werror",
+ "-Wextra",
],
+ shared_libs: [
+ "libinput",
+ "libcutils",
+ "libutils",
+ "libbinder",
+ "libui",
+ "libbase",
+ ]
}
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 96c165c..f1675c0 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <array>
+
#include "TestHelpers.h"
#include <unistd.h>
@@ -155,5 +157,37 @@
<< "sendMessage should have returned DEAD_OBJECT";
}
+TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
+ sp<InputChannel> serverChannel, clientChannel;
+ status_t result = InputChannel::openInputChannelPair("channel name",
+ serverChannel, clientChannel);
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ std::array<MotionClassification, 3> classifications = {
+ MotionClassification::NONE,
+ MotionClassification::AMBIGUOUS_GESTURE,
+ MotionClassification::DEEP_PRESS,
+ };
+
+ InputMessage serverMsg = {}, clientMsg;
+ serverMsg.header.type = InputMessage::TYPE_MOTION;
+ serverMsg.body.motion.seq = 1;
+ serverMsg.body.motion.pointerCount = 1;
+
+ for (MotionClassification classification : classifications) {
+ // Send and receive a message with classification
+ serverMsg.body.motion.classification = classification;
+ EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
+ << "server channel should be able to send message to client channel";
+
+ EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
+ << "client channel should be able to receive message from server channel";
+ EXPECT_EQ(serverMsg.header.type, clientMsg.header.type);
+ EXPECT_EQ(classification, clientMsg.body.motion.classification) <<
+ "Expected to receive " << motionClassificationToString(classification);
+ }
+}
+
} // namespace android
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
new file mode 100644
index 0000000..c174ae9
--- /dev/null
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <input/InputDevice.h>
+
+namespace android {
+
+// --- InputDeviceIdentifierTest ---
+
+TEST(InputDeviceIdentifierTest, getCanonicalName) {
+ InputDeviceIdentifier identifier;
+ identifier.name = "test device";
+ ASSERT_EQ(std::string("test_device"), identifier.getCanonicalName());
+
+ identifier.name = "deviceName-123 version_C!";
+ ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName());
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index fd3b7c8..2b75c82 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <array>
#include <math.h>
#include <binder/Parcel.h>
@@ -22,11 +23,10 @@
namespace android {
-class BaseTest : public testing::Test {
-protected:
- virtual void SetUp() { }
- virtual void TearDown() { }
-};
+// Default display id.
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+
+class BaseTest : public testing::Test { };
// --- PointerCoordsTest ---
@@ -178,13 +178,14 @@
// Initialize and get properties.
const nsecs_t ARBITRARY_DOWN_TIME = 1;
const nsecs_t ARBITRARY_EVENT_TIME = 2;
- event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN,
+ event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN,
AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
ASSERT_EQ(2, event.getDeviceId());
ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource());
+ ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
@@ -197,6 +198,11 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+
+ // Set display id.
+ constexpr int32_t newDisplayId = 2;
+ event.setDisplayId(newDisplayId);
+ ASSERT_EQ(newDisplayId, event.getDisplayId());
}
@@ -248,10 +254,10 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
- event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, 0,
+ event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0,
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME,
2, pointerProperties, pointerCoords);
@@ -301,11 +307,13 @@
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
ASSERT_EQ(2, event->getDeviceId());
ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource());
+ ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
+ ASSERT_EQ(MotionClassification::NONE, event->getClassification());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -434,6 +442,11 @@
event.setSource(AINPUT_SOURCE_JOYSTICK);
ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ // Set displayId.
+ constexpr int32_t newDisplayId = 2;
+ event.setDisplayId(newDisplayId);
+ ASSERT_EQ(newDisplayId, event.getDisplayId());
+
// Set action.
event.setAction(AMOTION_EVENT_ACTION_CANCEL);
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
@@ -557,8 +570,11 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
- event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE,
+ 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
+ AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
+ 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -591,4 +607,30 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
+TEST_F(MotionEventTest, Initialize_SetsClassification) {
+ std::array<MotionClassification, 3> classifications = {
+ MotionClassification::NONE,
+ MotionClassification::AMBIGUOUS_GESTURE,
+ MotionClassification::DEEP_PRESS,
+ };
+
+ MotionEvent event;
+ constexpr size_t pointerCount = 1;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerCoords[i].clear();
+ }
+
+ for (MotionClassification classification : classifications) {
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+ classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/,
+ pointerCount, pointerProperties, pointerCoords);
+ ASSERT_EQ(classification, event.getClassification());
+ }
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index c532241..f2cd1be 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -46,12 +46,12 @@
virtual void TearDown() {
if (mPublisher) {
delete mPublisher;
- mPublisher = NULL;
+ mPublisher = nullptr;
}
if (mConsumer) {
delete mConsumer;
- mConsumer = NULL;
+ mConsumer = nullptr;
}
serverChannel.clear();
@@ -70,32 +70,31 @@
void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
status_t status;
- const uint32_t seq = 15;
- const int32_t deviceId = 1;
- const int32_t source = AINPUT_SOURCE_KEYBOARD;
- const int32_t action = AKEY_EVENT_ACTION_DOWN;
- const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
- const int32_t keyCode = AKEYCODE_ENTER;
- const int32_t scanCode = 13;
- const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
- const int32_t repeatCount = 1;
- const nsecs_t downTime = 3;
- const nsecs_t eventTime = 4;
+ constexpr uint32_t seq = 15;
+ constexpr int32_t deviceId = 1;
+ constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+ constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
+ constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+ constexpr int32_t keyCode = AKEYCODE_ENTER;
+ constexpr int32_t scanCode = 13;
+ constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ constexpr int32_t repeatCount = 1;
+ constexpr nsecs_t downTime = 3;
+ constexpr nsecs_t eventTime = 4;
- status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags,
+ status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
ASSERT_EQ(OK, status)
<< "publisher publishKeyEvent should return OK";
uint32_t consumeSeq;
InputEvent* event;
- int32_t displayId;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event,
- &displayId);
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status)
<< "consumer consume should return OK";
- ASSERT_TRUE(event != NULL)
+ ASSERT_TRUE(event != nullptr)
<< "consumer should have returned non-NULL event";
ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
<< "consumer should have returned a key event";
@@ -104,6 +103,7 @@
EXPECT_EQ(seq, consumeSeq);
EXPECT_EQ(deviceId, keyEvent->getDeviceId());
EXPECT_EQ(source, keyEvent->getSource());
+ EXPECT_EQ(displayId, keyEvent->getDisplayId());
EXPECT_EQ(action, keyEvent->getAction());
EXPECT_EQ(flags, keyEvent->getFlags());
EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -131,23 +131,24 @@
void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
status_t status;
- const uint32_t seq = 15;
- const int32_t deviceId = 1;
- const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
- int32_t displayId = 0;
- const int32_t action = AMOTION_EVENT_ACTION_MOVE;
- const int32_t actionButton = 0;
- const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
- const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
- const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
- const float xOffset = -10;
- const float yOffset = -20;
- const float xPrecision = 0.25;
- const float yPrecision = 0.5;
- const nsecs_t downTime = 3;
- const size_t pointerCount = 3;
- const nsecs_t eventTime = 4;
+ constexpr uint32_t seq = 15;
+ constexpr int32_t deviceId = 1;
+ constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
+ constexpr int32_t actionButton = 0;
+ constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+ constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
+ constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+ constexpr float xOffset = -10;
+ constexpr float yOffset = -20;
+ constexpr float xPrecision = 0.25;
+ constexpr float yPrecision = 0.5;
+ constexpr nsecs_t downTime = 3;
+ constexpr size_t pointerCount = 3;
+ constexpr nsecs_t eventTime = 4;
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
for (size_t i = 0; i < pointerCount; i++) {
@@ -168,20 +169,19 @@
}
status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
- flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
- downTime, eventTime, pointerCount,
+ flags, edgeFlags, metaState, buttonState, classification,
+ xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount,
pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
uint32_t consumeSeq;
InputEvent* event;
- status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event,
- &displayId);
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
ASSERT_EQ(OK, status)
<< "consumer consume should return OK";
- ASSERT_TRUE(event != NULL)
+ ASSERT_TRUE(event != nullptr)
<< "consumer should have returned non-NULL event";
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
<< "consumer should have returned a motion event";
@@ -190,11 +190,13 @@
EXPECT_EQ(seq, consumeSeq);
EXPECT_EQ(deviceId, motionEvent->getDeviceId());
EXPECT_EQ(source, motionEvent->getSource());
+ EXPECT_EQ(displayId, motionEvent->getDisplayId());
EXPECT_EQ(action, motionEvent->getAction());
EXPECT_EQ(flags, motionEvent->getFlags());
EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
+ EXPECT_EQ(classification, motionEvent->getClassification());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
EXPECT_EQ(downTime, motionEvent->getDownTime());
@@ -264,7 +266,8 @@
pointerCoords[i].clear();
}
- status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
@@ -276,7 +279,8 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
- status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
@@ -293,7 +297,8 @@
pointerCoords[i].clear();
}
- status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
new file mode 100644
index 0000000..6db18ab
--- /dev/null
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2018 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 <binder/Binder.h>
+#include <binder/Parcel.h>
+
+#include <input/InputWindow.h>
+#include <input/InputTransport.h>
+
+namespace android {
+namespace test {
+
+TEST(InputWindowInfo, ParcellingWithoutToken) {
+ InputWindowInfo i;
+ i.token = nullptr;
+
+ Parcel p;
+ ASSERT_EQ(OK, i.write(p));
+ p.setDataPosition(0);
+ InputWindowInfo i2 = InputWindowInfo::read(p);
+ ASSERT_TRUE(i2.token == nullptr);
+}
+
+TEST(InputWindowInfo, Parcelling) {
+ sp<IBinder> touchableRegionCropHandle = new BBinder();
+ InputWindowInfo i;
+ i.token = new BBinder();
+ i.name = "Foobar";
+ i.layoutParamsFlags = 7;
+ i.layoutParamsType = 39;
+ i.dispatchingTimeout = 12;
+ 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.visible = false;
+ i.canReceiveKeys = false;
+ i.hasFocus = false;
+ i.hasWallpaper = false;
+ i.paused = false;
+ i.layer = 7;
+ i.ownerPid = 19;
+ i.ownerUid = 24;
+ i.inputFeatures = 29;
+ i.displayId = 34;
+ i.portalToDisplayId = 2;
+ i.replaceTouchableRegionWithCrop = true;
+ i.touchableRegionCropHandle = touchableRegionCropHandle;
+
+ Parcel p;
+ i.write(p);
+
+ p.setDataPosition(0);
+ InputWindowInfo i2 = InputWindowInfo::read(p);
+ ASSERT_EQ(i.token, i2.token);
+ ASSERT_EQ(i.name, i2.name);
+ ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
+ ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
+ ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
+ ASSERT_EQ(i.frameLeft, i2.frameLeft);
+ ASSERT_EQ(i.frameTop, i2.frameTop);
+ ASSERT_EQ(i.frameRight, i2.frameRight);
+ 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.visible, i2.visible);
+ ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
+ ASSERT_EQ(i.hasFocus, i2.hasFocus);
+ ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
+ ASSERT_EQ(i.paused, i2.paused);
+ ASSERT_EQ(i.layer, i2.layer);
+ ASSERT_EQ(i.ownerPid, i2.ownerPid);
+ ASSERT_EQ(i.ownerUid, i2.ownerUid);
+ ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
+ ASSERT_EQ(i.displayId, i2.displayId);
+ ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
+ ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
+ ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 12a6782..62023fb 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -57,7 +57,8 @@
CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
- CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 48);
+ CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
+ CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
new file mode 100644
index 0000000..815424e
--- /dev/null
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 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 <gtest/gtest.h>
+
+#include <input/TouchVideoFrame.h>
+
+namespace android {
+namespace test {
+
+static const struct timeval TIMESTAMP = {1, 2};
+
+TEST(TouchVideoFrame, Constructor) {
+ const std::vector<int16_t> data = {1, 2, 3, 4, 5, 6};
+ constexpr uint32_t height = 3;
+ constexpr uint32_t width = 2;
+
+ TouchVideoFrame frame(height, width, data, TIMESTAMP);
+
+ ASSERT_EQ(data, frame.getData());
+ ASSERT_EQ(height, frame.getHeight());
+ ASSERT_EQ(width, frame.getWidth());
+ ASSERT_EQ(TIMESTAMP.tv_sec, frame.getTimestamp().tv_sec);
+ ASSERT_EQ(TIMESTAMP.tv_usec, frame.getTimestamp().tv_usec);
+}
+
+TEST(TouchVideoFrame, Equality) {
+ const std::vector<int16_t> data = {1, 2, 3, 4, 5, 6};
+ constexpr uint32_t height = 3;
+ constexpr uint32_t width = 2;
+ TouchVideoFrame frame(height, width, data, TIMESTAMP);
+
+ TouchVideoFrame identicalFrame(height, width, data, TIMESTAMP);
+ ASSERT_EQ(frame, identicalFrame);
+
+ // The two cases below create an invalid frame, but it is OK for comparison purposes.
+ // There aren't any checks currently enforced on the frame dimensions and data
+ // Change height
+ TouchVideoFrame changedHeightFrame(height + 1, width, data, TIMESTAMP);
+ ASSERT_FALSE(frame == changedHeightFrame);
+
+ // Change width
+ TouchVideoFrame changedWidthFrame(height, width + 1, data, TIMESTAMP);
+ ASSERT_FALSE(frame == changedWidthFrame);
+
+ // Change data
+ const std::vector<int16_t> differentData = {1, 2, 3, 3, 5, 6};
+ TouchVideoFrame changedDataFrame(height, width, differentData, TIMESTAMP);
+ ASSERT_FALSE(frame == changedDataFrame);
+
+ // Change timestamp
+ const struct timeval differentTimestamp = {TIMESTAMP.tv_sec + 1, TIMESTAMP.tv_usec + 1};
+ TouchVideoFrame changedTimestampFrame(height, width, data, differentTimestamp);
+ ASSERT_FALSE(frame == changedTimestampFrame);
+}
+
+// --- Rotate 90 degrees ---
+
+TEST(TouchVideoFrame, Rotate90_0x0) {
+ TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
+ TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_1x1) {
+ TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
+ TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_2x2) {
+ TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_3x2) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate90_3x2_4times) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(DISPLAY_ORIENTATION_90);
+ ASSERT_EQ(frame, frameOriginal);
+}
+
+// --- Rotate 180 degrees ---
+
+TEST(TouchVideoFrame, Rotate180_0x0) {
+ TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
+ TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_1x1) {
+ TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
+ TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_2x2) {
+ TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_3x2) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate180_3x2_2times) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameOriginal);
+}
+
+TEST(TouchVideoFrame, Rotate180_3x3) {
+ TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
+ TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_180);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+// --- Rotate 270 degrees ---
+
+TEST(TouchVideoFrame, Rotate270_0x0) {
+ TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
+ TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_1x1) {
+ TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
+ TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_2x2) {
+ TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_3x2) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameRotated);
+}
+
+TEST(TouchVideoFrame, Rotate270_3x2_4times) {
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(DISPLAY_ORIENTATION_270);
+ ASSERT_EQ(frame, frameOriginal);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 9da2e2a..a106451 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -27,6 +27,8 @@
namespace android {
+constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id
+
constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests
// velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV
@@ -64,56 +66,60 @@
EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE);
}
-void failWithMessage(std::string message) {
+static void failWithMessage(std::string message) {
FAIL() << message; // cannot do this directly from a non-void function
}
struct Position {
- nsecs_t time;
- float x;
- float y;
+ nsecs_t time;
+ float x;
+ float y;
};
-
-MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSamples) {
+static std::unique_ptr<MotionEvent> createSimpleMotionEvent(
+ const std::vector<Position>& positions) {
/**
* Only populate the basic fields of a MotionEvent, such as time and a single axis
* Designed for use with manually-defined tests.
- * Create a new MotionEvent on the heap, caller responsible for destroying the object.
*/
- if (numSamples < 1) {
- failWithMessage(StringPrintf("Need at least 1 sample to create a MotionEvent."
- " Received numSamples=%zu", numSamples));
+ if (positions.empty()) {
+ failWithMessage("Need at least 1 sample to create a MotionEvent. Received empty vector.");
}
- MotionEvent* event = new MotionEvent();
- PointerCoords coords;
- PointerProperties properties[1];
+ std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>();
+ constexpr size_t pointerCount = 1;
+ PointerCoords coords[pointerCount];
+ coords[0].clear();
+
+ PointerProperties properties[pointerCount];
properties[0].id = DEFAULT_POINTER_ID;
properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
// First sample added separately with initialize
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
- event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
+ event->initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
+ 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ 0 /*downTime*/, positions[0].time, pointerCount, properties, coords);
- for (size_t i = 1; i < numSamples; i++) {
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
- event->addSample(positions[i].time, &coords);
+ for (size_t i = 1; i < positions.size(); i++) {
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
+ event->addSample(positions[i].time, coords);
}
return event;
}
-static void computeAndCheckVelocity(const Position* positions, size_t numSamples,
+static void computeAndCheckVelocity(const std::vector<Position>& positions,
int32_t axis, float targetVelocity) {
VelocityTracker vt(nullptr);
float Vx, Vy;
- MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
- vt.addMovement(event);
+ std::unique_ptr<MotionEvent> event = createSimpleMotionEvent(positions);
+ vt.addMovement(event.get());
vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy);
@@ -127,14 +133,13 @@
default:
FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
}
- delete event;
}
-static void computeAndCheckQuadraticEstimate(const Position* positions, size_t numSamples,
+static void computeAndCheckQuadraticEstimate(const std::vector<Position>& positions,
const std::array<float, 3>& coefficients) {
VelocityTracker vt("lsq2");
- MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
- vt.addMovement(event);
+ std::unique_ptr<MotionEvent> event = createSimpleMotionEvent(positions);
+ vt.addMovement(event.get());
VelocityTracker::Estimator estimator;
EXPECT_TRUE(vt.getEstimator(0, &estimator));
for (size_t i = 0; i< coefficients.size(); i++) {
@@ -151,35 +156,32 @@
// Same coordinate is reported 2 times in a row
// It is difficult to determine the correct answer here, but at least the direction
// of the reported velocity should be positive.
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 273, NAN },
{ 12585000, 293, NAN },
{ 14730000, 293, NAN },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1600);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1600);
}
TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
// Same coordinate is reported 3 times in a row
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 293, NAN },
{ 6132000, 293, NAN },
{ 11283000, 293, NAN },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 0);
}
TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
// Fixed velocity at 5 points per 10 milliseconds
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 0, NAN },
{ 10000000, 5, NAN },
{ 20000000, 10, NAN },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 500);
}
@@ -197,7 +199,7 @@
// @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy
TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) {
// Recording of a fling on Swordfish that could cause a fling in the wrong direction
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 271, 96 },
{ 16071042, 269.786346, 106.922775 },
{ 35648403, 267.983063, 156.660034 },
@@ -206,9 +208,8 @@
{ 85639375, 274.79245, 428.113159 },
{ 96948871, 274.79245, 428.113159 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 623.577637);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 8523.348633);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 623.577637);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 8523.348633);
}
// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -230,7 +231,7 @@
TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
// Sailfish - fling up - slow - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235089067457000, 528.00, 983.00 },
{ 235089084684000, 527.00, 981.00 },
{ 235089093349000, 527.00, 977.00 },
@@ -248,17 +249,16 @@
{ 235089160784000, 559.00, 851.00 },
{ 235089162955851, 560.66, 843.82 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) {
// Sailfish - fling up - slow - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235110560704000, 522.00, 1107.00 },
{ 235110575764000, 522.00, 1107.00 },
{ 235110584385000, 522.00, 1107.00 },
@@ -277,15 +277,14 @@
{ 235110655089581, 525.54, 1000.19 },
{ 235110660368000, 530.00, 980.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) {
// Sailfish - fling up - slow - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 792536237000, 580.00, 1317.00 },
{ 792541538987, 580.63, 1311.94 },
{ 792544613000, 581.00, 1309.00 },
@@ -305,17 +304,16 @@
{ 792625653873, 617.32, 1121.73 },
{ 792629200000, 619.00, 1115.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) {
// Sailfish - fling up - faster - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235160420675000, 610.00, 1042.00 },
{ 235160428220000, 609.00, 1026.00 },
{ 235160436544000, 609.00, 1024.00 },
@@ -335,17 +333,16 @@
{ 235160512603000, 670.00, 837.00 },
{ 235160520366000, 679.00, 814.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) {
// Sailfish - fling up - faster - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 847153808000, 576.00, 1264.00 },
{ 847171174000, 576.00, 1262.00 },
{ 847179640000, 576.00, 1257.00 },
@@ -361,15 +358,14 @@
{ 847235701400, 607.56, 1103.83 },
{ 847237986000, 610.00, 1095.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) {
// Sailfish - fling up - faster - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 235200532789000, 507.00, 1084.00 },
{ 235200549221000, 507.00, 1083.00 },
{ 235200557841000, 507.00, 1081.00 },
@@ -385,15 +381,14 @@
{ 235200608527086, 563.02, 910.94 },
{ 235200616933000, 590.00, 844.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) {
// Sailfish - fling up - fast - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 920922149000, 561.00, 1412.00 },
{ 920930185000, 559.00, 1377.00 },
{ 920930262463, 558.98, 1376.66 },
@@ -408,17 +403,16 @@
{ 920980906000, 672.00, 993.00 },
{ 920989261000, 715.00, 903.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) {
// Sailfish - fling up - fast - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235247153233000, 518.00, 1168.00 },
{ 235247170452000, 517.00, 1167.00 },
{ 235247178908000, 515.00, 1159.00 },
@@ -431,15 +425,14 @@
{ 235247213222491, 574.72, 778.45 },
{ 235247220736000, 620.00, 641.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) {
// Sailfish - fling up - fast - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 235302568736000, 529.00, 1167.00 },
{ 235302576644000, 523.00, 1140.00 },
{ 235302579395063, 520.91, 1130.61 },
@@ -450,15 +443,14 @@
{ 235302610545000, 652.00, 605.00 },
{ 235302613019881, 679.26, 526.73 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) {
// Sailfish - fling down - slow - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235655749552755, 582.00, 432.49 },
{ 235655750638000, 582.00, 433.00 },
{ 235655758865000, 582.00, 440.00 },
@@ -478,17 +470,16 @@
{ 235655834541000, 566.00, 623.00 },
{ 235655842893000, 563.00, 649.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) {
// Sailfish - fling down - slow - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235671152083370, 485.24, 558.28 },
{ 235671154126000, 485.00, 559.00 },
{ 235671162497000, 484.00, 566.00 },
@@ -508,17 +499,16 @@
{ 235671238098000, 472.00, 765.00 },
{ 235671246532000, 470.00, 799.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) {
// Sailfish - fling down - slow - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 170983201000, 557.00, 533.00 },
{ 171000668000, 556.00, 534.00 },
{ 171007359750, 554.73, 535.27 },
@@ -531,17 +521,16 @@
{ 171043147000, 541.00, 571.00 },
{ 171051052000, 536.00, 586.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) {
// Sailfish - fling down - faster - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235695280333000, 558.00, 451.00 },
{ 235695283971237, 558.43, 454.45 },
{ 235695289038000, 559.00, 462.00 },
@@ -561,15 +550,14 @@
{ 235695368118682, 562.24, 722.52 },
{ 235695373403000, 564.00, 744.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) {
// Sailfish - fling down - faster - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235709624766000, 535.00, 579.00 },
{ 235709642256000, 534.00, 580.00 },
{ 235709643350278, 533.94, 580.06 },
@@ -586,17 +574,16 @@
{ 235709709830000, 512.00, 739.00 },
{ 235709710626776, 511.72, 741.85 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) {
// Sailfish - fling down - faster - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 235727628927000, 540.00, 440.00 },
{ 235727636810000, 537.00, 454.00 },
{ 235727646176000, 536.00, 454.00 },
@@ -615,15 +602,14 @@
{ 235727720880776, 516.33, 655.36 },
{ 235727721580000, 516.00, 658.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) {
// Sailfish - fling down - fast - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235762352849000, 467.00, 286.00 },
{ 235762360250000, 443.00, 344.00 },
{ 235762362787412, 434.77, 363.89 },
@@ -634,15 +620,14 @@
{ 235762394133000, 406.00, 648.00 },
{ 235762396429369, 404.37, 680.67 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) {
// Sailfish - fling down - fast - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235772487188000, 576.00, 204.00 },
{ 235772495159000, 553.00, 236.00 },
{ 235772503568000, 551.00, 240.00 },
@@ -653,15 +638,14 @@
{ 235772529174000, 498.00, 451.00 },
{ 235772537635000, 484.00, 589.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) {
// Sailfish - fling down - fast - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 507650295000, 628.00, 233.00 },
{ 507658234000, 605.00, 269.00 },
{ 507666784000, 601.00, 274.00 },
@@ -673,11 +657,10 @@
{ 507700707000, 454.00, 792.00 },
{ 507703352649, 443.71, 857.77 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
}
/*
@@ -704,7 +687,7 @@
* In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 1, 1 }, // 0 s
{ 1000000, 1, 1 }, // 0.001 s
{ 2000000, 1, 1 }, // 0.002 s
@@ -714,15 +697,14 @@
// -0.002, 1
// -0.001, 1
// -0.000, 1
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({1, 0, 0}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({1, 0, 0}));
}
/*
* Straight line y = x :: the constant and quadratic coefficients are zero.
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, -2, -2 },
{ 1000000, -1, -1 },
{ 2000000, -0, -0 },
@@ -732,15 +714,14 @@
// -0.002, -2
// -0.001, -1
// -0.000, 0
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({0, 1E3, 0}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({0, 1E3, 0}));
}
/*
* Parabola
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 1, 1 },
{ 1000000, 4, 4 },
{ 2000000, 8, 8 },
@@ -750,15 +731,14 @@
// -0.002, 1
// -0.001, 4
// -0.000, 8
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({8, 4.5E3, 0.5E6}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({8, 4.5E3, 0.5E6}));
}
/*
* Parabola
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 1, 1 },
{ 1000000, 4, 4 },
{ 2000000, 9, 9 },
@@ -768,15 +748,14 @@
// -0.002, 1
// -0.001, 4
// -0.000, 9
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({9, 6E3, 1E6}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({9, 6E3, 1E6}));
}
/*
* Parabola :: y = x^2 :: the constant and linear coefficients are zero.
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 4, 4 },
{ 1000000, 1, 1 },
{ 2000000, 0, 0 },
@@ -786,8 +765,7 @@
// -0.002, 4
// -0.001, 1
// -0.000, 0
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({0, 0E3, 1E6}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({0, 0E3, 1E6}));
}
} // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 7e26b0b..bf80481 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -41,33 +41,11 @@
// ----------------------------------------------------------------------------
int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
- if (!outBuffer || !desc)
- return BAD_VALUE;
-
- if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
- ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format);
- return BAD_VALUE;
- }
+ if (!outBuffer || !desc) return BAD_VALUE;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE;
int format = AHardwareBuffer_convertToPixelFormat(desc->format);
- if (desc->rfu0 != 0 || desc->rfu1 != 0) {
- ALOGE("AHardwareBuffer_Desc::rfu fields must be 0");
- return BAD_VALUE;
- }
-
- if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
- ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
- return BAD_VALUE;
- }
-
- if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
- (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
- ALOGE("AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
- "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
- return BAD_VALUE;
- }
-
- uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+ uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
sp<GraphicBuffer> gbuffer(new GraphicBuffer(
desc->width, desc->height, format, desc->layers, usage,
std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
@@ -115,13 +93,91 @@
outDesc->rfu1 = 0;
}
-int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, void** outVirtualAddress) {
+int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, void** outVirtualAddress,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
if (!buffer) return BAD_VALUE;
if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ return BAD_VALUE;
+ }
+
+ usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+ GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+ //Mapper implementations before 3.0 will not return bytes per pixel or
+ //bytes per stride information.
+ if (gbuffer->getBufferMapperVersion() == GraphicBufferMapper::Version::GRALLOC_2) {
+ ALOGE("Mapper versions before 3.0 cannot retrieve bytes per pixel and bytes per stride info");
+ return INVALID_OPERATION;
+ }
+
+ if (gbuffer->getLayerCount() > 1) {
+ ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; "
+ "only buffers with one layer are allowed");
+ return INVALID_OPERATION;
+ }
+
+ Rect bounds;
+ if (!rect) {
+ bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight()));
+ } else {
+ bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+ }
+ int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, outBytesPerPixel, outBytesPerStride);
+
+ // if hardware returns -1 for bytes per pixel or bytes per stride, we fail
+ // and unlock the buffer
+ if (*outBytesPerPixel == -1 || *outBytesPerStride == -1) {
+ gbuffer->unlock();
+ return INVALID_OPERATION;
+ }
+
+ return result;
+}
+
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, void** outVirtualAddress) {
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+
+ if (!buffer) return BAD_VALUE;
+
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ return BAD_VALUE;
+ }
+
+ usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+ GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+ if (gbuffer->getLayerCount() > 1) {
+ ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; "
+ "only buffers with one layer are allowed");
+ return INVALID_OPERATION;
+ }
+
+ Rect bounds;
+ if (!rect) {
+ bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight()));
+ } else {
+ bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+ }
+ return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride);
+}
+
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) {
+ if (!buffer || !outPlanes) return BAD_VALUE;
+
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
" AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -134,7 +190,33 @@
} else {
bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
}
- return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
+ int format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gBuffer->getPixelFormat()));
+ memset(outPlanes->planes, 0, sizeof(outPlanes->planes));
+ if (AHardwareBuffer_formatIsYuv(format)) {
+ android_ycbcr yuvData;
+ int result = gBuffer->lockAsyncYCbCr(usage, bounds, &yuvData, fence);
+ if (result == 0) {
+ outPlanes->planeCount = 3;
+ outPlanes->planes[0].data = yuvData.y;
+ outPlanes->planes[0].pixelStride = 1;
+ outPlanes->planes[0].rowStride = yuvData.ystride;
+ outPlanes->planes[1].data = yuvData.cb;
+ outPlanes->planes[1].pixelStride = yuvData.chroma_step;
+ outPlanes->planes[1].rowStride = yuvData.cstride;
+ outPlanes->planes[2].data = yuvData.cr;
+ outPlanes->planes[2].pixelStride = yuvData.chroma_step;
+ outPlanes->planes[2].rowStride = yuvData.cstride;
+ } else {
+ outPlanes->planeCount = 0;
+ }
+ return result;
+ } else {
+ const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format);
+ outPlanes->planeCount = 1;
+ outPlanes->planes[0].pixelStride = pixelStride;
+ outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride;
+ return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence);
+ }
}
int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
@@ -274,6 +356,38 @@
return NO_ERROR;
}
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) {
+ if (!desc) return 0;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
+
+ bool supported = false;
+ GraphicBuffer* gBuffer = new GraphicBuffer();
+ status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers,
+ desc->usage, &supported);
+
+ if (err == NO_ERROR) {
+ return supported;
+ }
+
+ // function isSupported is not implemented on device or an error occurred during HAL
+ // query. Make a trial allocation.
+ AHardwareBuffer_Desc trialDesc = *desc;
+ trialDesc.width = 4;
+ trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4;
+ if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
+ trialDesc.layers = desc->layers == 6 ? 6 : 12;
+ } else {
+ trialDesc.layers = desc->layers == 1 ? 1 : 2;
+ }
+ AHardwareBuffer* trialBuffer = nullptr;
+ int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer);
+ if (result == NO_ERROR) {
+ AHardwareBuffer_release(trialBuffer);
+ return 1;
+ }
+ return 0;
+}
+
// ----------------------------------------------------------------------------
// VNDK functions
@@ -322,14 +436,84 @@
namespace android {
-// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
-struct UsageMaskMapping {
- uint64_t hardwareBufferMask;
- uint64_t grallocMask;
-};
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) {
+ if (desc->width == 0 || desc->height == 0 || desc->layers == 0) {
+ ALOGE_IF(log, "Width, height and layers must all be nonzero");
+ return false;
+ }
-static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
- return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+ if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
+ ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))",
+ desc->format, desc->format);
+ return false;
+ }
+
+ if (desc->rfu0 != 0 || desc->rfu1 != 0) {
+ ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0");
+ return false;
+ }
+
+ if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) {
+ if (desc->height != 1 || desc->layers != 1) {
+ ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB");
+ return false;
+ }
+ const uint64_t blobInvalidGpuMask =
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
+ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+ if (desc->usage & blobInvalidGpuMask) {
+ ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; "
+ "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed");
+ return false;
+ }
+ if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video");
+ return false;
+ }
+ } else if (AHardwareBuffer_formatIsYuv(desc->format)) {
+ if (desc->layers != 1) {
+ ALOGE_IF(log, "Layers must be 1 for YUV formats.");
+ return false;
+ }
+ const uint64_t yuvInvalidGpuMask =
+ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+ if (desc->usage & yuvInvalidGpuMask) {
+ ALOGE_IF(log, "Invalid usage flags specified for YUV format; "
+ "mip-mapping and cube-mapping are not allowed.");
+ return false;
+ }
+ } else {
+ if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB");
+ return false;
+ }
+ if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB");
+ return false;
+ }
+ }
+
+ if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
+ (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
+ "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
+ return false;
+ }
+
+ if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
+ if (desc->width != desc->height) {
+ ALOGE_IF(log, "Cube maps must be square");
+ return false;
+ }
+ if (desc->layers % 6 != 0) {
+ ALOGE_IF(log, "Cube map layers must be a multiple of 6");
+ return false;
+ }
+ }
+ return true;
}
bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
@@ -400,6 +584,7 @@
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
case AHARDWAREBUFFER_FORMAT_S8_UINT:
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
// VNDK formats only -- unfortunately we can't differentiate from where we're called
case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_YV12:
@@ -410,7 +595,6 @@
case AHARDWAREBUFFER_FORMAT_RAW12:
case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
- case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
@@ -421,6 +605,40 @@
}
}
+bool AHardwareBuffer_formatIsYuv(uint32_t format) {
+ switch (format) {
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
+ case AHARDWAREBUFFER_FORMAT_YV12:
+ case AHARDWAREBUFFER_FORMAT_Y8:
+ case AHARDWAREBUFFER_FORMAT_Y16:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
+ case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
+ switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+ return 2;
+ case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D24_UNORM:
+ return 3;
+ case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
return hal_format;
}
@@ -445,7 +663,7 @@
"gralloc and AHardwareBuffer flags don't match");
static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE,
"gralloc and AHardwareBuffer flags don't match");
- static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
+ static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
"gralloc and AHardwareBuffer flags don't match");
static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED,
"gralloc and AHardwareBuffer flags don't match");
@@ -467,11 +685,11 @@
}
const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) {
- return reinterpret_cast<const GraphicBuffer*>(buffer);
+ return GraphicBuffer::fromAHardwareBuffer(buffer);
}
GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) {
- return reinterpret_cast<GraphicBuffer*>(buffer);
+ return GraphicBuffer::fromAHardwareBuffer(buffer);
}
const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) {
@@ -483,7 +701,7 @@
}
AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) {
- return reinterpret_cast<AHardwareBuffer*>(buffer);
+ return buffer->toAHardwareBuffer();
}
} // namespace android
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index d74bdb3..27ab482 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -13,13 +13,20 @@
// limitations under the License.
ndk_headers {
- name: "libnativewindow_headers",
+ name: "libnativewindow_ndk_headers",
from: "include/android",
to: "android",
srcs: ["include/android/*.h"],
license: "NOTICE",
}
+// TODO(b/118715870): cleanup header files
+cc_library_headers {
+ name: "libnativewindow_headers",
+ export_include_dirs: ["include"],
+ vendor_available: true,
+}
+
ndk_library {
name: "libnativewindow",
symbol_file: "libnativewindow.map.txt",
@@ -40,6 +47,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wno-enum-compare",
"-Wno-unused-function",
],
@@ -66,6 +74,7 @@
header_libs: [
"libnativebase_headers",
+ "libnativewindow_headers",
],
// headers we include in our public headers
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 71f5634..ddfd1d1 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -28,13 +28,24 @@
#include <stdint.h>
struct AHardwareBuffer;
+struct AHardwareBuffer_Desc;
struct ANativeWindowBuffer;
namespace android {
+// Validates whether the passed description does not have conflicting
+// parameters. Note: this does not verify any platform-specific contraints.
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log);
+
// whether this AHardwareBuffer format is valid
bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
+// whether this is a YUV type format
+bool AHardwareBuffer_formatIsYuv(uint32_t format);
+
+// number of bytes per pixel or 0 if unknown or multi-planar
+uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format);
+
// convert AHardwareBuffer format to HAL format (note: this is a no-op)
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 3ac1c58..2899bcf 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -75,7 +75,7 @@
* scRGB:
*
* The red, green, and blue components are stored in extended sRGB space,
- * but are linear, not gamma-encoded.
+ * and gamma-encoded using the SRGB transfer function.
* The RGB primaries and the white point are the same as BT.709.
*
* The values are floating point.
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 52b4582..165b75a 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -150,6 +150,14 @@
* OpenGL ES: GL_STENCIL_INDEX8
*/
AHARDWAREBUFFER_FORMAT_S8_UINT = 0x35,
+
+ /**
+ * YUV 420 888 format.
+ * Must have an even width and height. Can be accessed in OpenGL
+ * shaders through an external sampler. Does not support mip-maps
+ * cube-maps or multi-layered textures.
+ */
+ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
};
/**
@@ -194,6 +202,8 @@
/// The buffer will be read from by the GPU as a texture.
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
+ /// The buffer will be written to by the GPU as a framebuffer attachment.
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9,
/**
* The buffer will be written to by the GPU as a framebuffer
* attachment.
@@ -201,9 +211,21 @@
* Note that the name of this flag is somewhat misleading: it does
* not imply that the buffer contains a color format. A buffer with
* depth or stencil format that will be used as a framebuffer
- * attachment should also have this flag.
+ * attachment should also have this flag. Use the equivalent flag
+ * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion.
*/
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9,
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
+ /**
+ * The buffer will be used as a composer HAL overlay layer.
+ *
+ * This flag is currently only needed when using ASurfaceTransaction_setBuffer
+ * to set a buffer. In all other cases, the framework adds this flag
+ * internally to buffers that could be presented in a composer overlay.
+ * ASurfaceTransaction_setBuffer is special because it uses buffers allocated
+ * directly through AHardwareBuffer_allocate instead of buffers allocated
+ * by the framework.
+ */
+ AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11,
/**
* The buffer is protected from direct CPU access or being read by
* non-secure hardware, such as video encoders.
@@ -288,6 +310,24 @@
} AHardwareBuffer_Desc;
/**
+ * Holds data for a single image plane.
+ */
+typedef struct AHardwareBuffer_Plane {
+ void* data; ///< Points to first byte in plane
+ uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
+ uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to
+ /// the first value of the next row.
+} AHardwareBuffer_Plane;
+
+/**
+ * Holds all image planes that contain the pixel data.
+ */
+typedef struct AHardwareBuffer_Planes {
+ uint32_t planeCount; ///< Number of distinct planes
+ AHardwareBuffer_Plane planes[4]; ///< Array of image planes
+} AHardwareBuffer_Planes;
+
+/**
* Opaque handle for a native hardware buffer.
*/
typedef struct AHardwareBuffer AHardwareBuffer;
@@ -382,6 +422,46 @@
int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
/**
+ * Lock an AHardwareBuffer for direct CPU access.
+ *
+ * This function is the same as the above lock function, but passes back
+ * additional information about the bytes per pixel and the bytes per stride
+ * of the locked buffer. If the bytes per pixel or bytes per stride are unknown
+ * or variable, or if the underlying mapper implementation does not support returning
+ * additional information, then this call will fail with INVALID_OPERATION
+ */
+int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, void** outVirtualAddress,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) __INTRODUCED_IN(29);
+/**
+ * Lock a potentially multi-planar AHardwareBuffer for direct CPU access.
+ *
+ * This function is similar to AHardwareBuffer_lock, but can lock multi-planar
+ * formats. The locked planes are returned in the \a outPlanes argument. Note,
+ * that multi-planar should not be confused with multi-layer images, which this
+ * locking function does not support.
+ *
+ * YUV formats are always represented by three separate planes of data, one for
+ * each color plane. The order of planes in the array is guaranteed such that
+ * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V
+ * (Cr). All other formats are represented by a single plane.
+ *
+ * Additional information always accompanies the buffers, describing the row
+ * stride and the pixel stride for each plane.
+ *
+ * In case the buffer cannot be locked, \a outPlanes will contain zero planes.
+ *
+ * See the AHardwareBuffer_lock documentation for all other locking semantics.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
+ * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
+ * has more than one layer. Error number if the lock fails for any other
+ * reason.
+ */
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29);
+
+/**
* Unlock the AHardwareBuffer from direct CPU access.
*
* Must be called after all changes to the buffer are completed by the
@@ -417,6 +497,29 @@
#endif // __ANDROID_API__ >= 26
+#if __ANDROID_API__ >= 29
+
+/**
+ * Test whether the given format and usage flag combination is
+ * allocatable.
+ *
+ * If this function returns true, it means that a buffer with the given
+ * description can be allocated on this implementation, unless resource
+ * exhaustion occurs. If this function returns false, it means that the
+ * allocation of the given description will never succeed.
+ *
+ * The return value of this function may depend on all fields in the
+ * description, except stride, which is always ignored. For example,
+ * some implementations have implementation-defined limits on texture
+ * size and layer count.
+ *
+ * \return 1 if the format and usage flag combination is allocatable,
+ * 0 otherwise.
+ */
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
__END_DECLS
#endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h
new file mode 100644
index 0000000..88772a9
--- /dev/null
+++ b/libs/nativewindow/include/android/hdr_metadata.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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.
+ */
+
+/**
+ * @file hdr_metadata.h
+ */
+
+#ifndef ANDROID_HDR_METADATA_H
+#define ANDROID_HDR_METADATA_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * These structures are used to define the display's capabilities for HDR content.
+ * They can be used to better tone map content to user's display.
+ */
+
+/**
+ * HDR metadata standards that are supported by Android.
+ */
+enum AHdrMetadataType : uint32_t {
+ HDR10_SMPTE2086 = 1,
+ HDR10_CTA861_3 = 2,
+ HDR10PLUS_SEI = 3,
+};
+
+/**
+ * Color is defined in CIE XYZ coordinates.
+ */
+struct AColor_xy {
+ float x;
+ float y;
+};
+
+/**
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ */
+struct AHdrMetadata_smpte2086 {
+ struct AColor_xy displayPrimaryRed;
+ struct AColor_xy displayPrimaryGreen;
+ struct AColor_xy displayPrimaryBlue;
+ struct AColor_xy whitePoint;
+ float maxLuminance;
+ float minLuminance;
+};
+
+/**
+ * CTA 861.3 "HDR Static Metadata Extension" static metadata
+ */
+struct AHdrMetadata_cta861_3 {
+ float maxContentLightLevel;
+ float maxFrameAverageLightLevel;
+};
+
+__END_DECLS
+
+#endif // ANDROID_HDR_METADATA_H
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 197f73f..61590e0 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -202,7 +202,7 @@
* ANativeWindow.
*/
enum {
-// clang-format off
+ // clang-format off
NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */
NATIVE_WINDOW_CONNECT = 1, /* deprecated */
NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
@@ -237,7 +237,8 @@
NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
-// clang-format on
+ NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
+ // clang-format on
};
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -748,6 +749,27 @@
}
/*
+ * native_window_set_buffers_hdr10_plus_metadata(..., metadata)
+ * All buffers queued after this call will be associated with the
+ * HDR10+ dynamic metadata specified.
+ *
+ * metadata specifies additional dynamic information about the
+ * contents of the buffer that may affect how it is displayed. When
+ * it is nullptr, it means no such information is available. No
+ * HDR10+ dynamic emtadata is associated with the buffers by default.
+ *
+ * Parameter "size" refers to the length of the metadata blob pointed to
+ * by parameter "data". The metadata blob will adhere to the HDR10+ SEI
+ * message standard.
+ */
+static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window,
+ const size_t size,
+ const uint8_t* metadata) {
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size,
+ metadata);
+}
+
+/*
* native_window_set_buffers_transform(..., int transform)
* All buffers queued after this call will be displayed transformed according
* to the transform parameter specified.
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 6c9ec34..3392d7f 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -73,8 +73,6 @@
AHARDWAREBUFFER_FORMAT_RAW_OPAQUE = 0x24,
/* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */
AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
- /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */
- AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
/* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */
AHARDWAREBUFFER_FORMAT_YCbCr_422_SP = 0x10,
/* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 753954d..23a05f3 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -5,7 +5,9 @@
AHardwareBuffer_createFromHandle; # vndk
AHardwareBuffer_describe;
AHardwareBuffer_getNativeHandle; # vndk
+ AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
+ AHardwareBuffer_lockAndGetInfo; # introduced=29
AHardwareBuffer_recvHandleFromUnixSocket;
AHardwareBuffer_release;
AHardwareBuffer_sendHandleToUnixSocket;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
new file mode 100644
index 0000000..36211ca
--- /dev/null
+++ b/libs/renderengine/Android.bp
@@ -0,0 +1,98 @@
+cc_defaults {
+ name: "renderengine_defaults",
+ cflags: [
+ "-DLOG_TAG=\"RenderEngine\"",
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
+
+cc_defaults {
+ name: "librenderengine_defaults",
+ defaults: ["renderengine_defaults"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+filegroup {
+ name: "librenderengine_sources",
+ srcs: [
+ "Description.cpp",
+ "Mesh.cpp",
+ "RenderEngine.cpp",
+ "Texture.cpp",
+ ],
+}
+
+filegroup {
+ name: "librenderengine_gl_sources",
+ srcs: [
+ "gl/GLESRenderEngine.cpp",
+ "gl/GLExtensions.cpp",
+ "gl/GLFramebuffer.cpp",
+ "gl/GLImage.cpp",
+ "gl/Program.cpp",
+ "gl/ProgramCache.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "librenderengine",
+ defaults: ["librenderengine_defaults"],
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ clang: true,
+ cflags: [
+ "-fvisibility=hidden",
+ "-Werror=format",
+ ],
+ cppflags: [
+ "-fwhole-program-vtables", // requires ThinLTO
+ ],
+ srcs: [
+ ":librenderengine_sources",
+ ":librenderengine_gl_sources",
+ ],
+ lto: {
+ thin: true,
+ },
+}
+
+cc_library_static {
+ name: "librenderengine_mocks",
+ defaults: ["librenderengine_defaults"],
+ srcs: [
+ "mock/Framebuffer.cpp",
+ "mock/Image.cpp",
+ "mock/RenderEngine.cpp",
+ ],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
new file mode 100644
index 0000000..b9cea10
--- /dev/null
+++ b/libs/renderengine/Description.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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 <renderengine/private/Description.h>
+
+#include <stdint.h>
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace renderengine {
+
+Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) {
+ ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK);
+ switch (transfer) {
+ case ui::Dataspace::TRANSFER_ST2084:
+ return Description::TransferFunction::ST2084;
+ case ui::Dataspace::TRANSFER_HLG:
+ return Description::TransferFunction::HLG;
+ case ui::Dataspace::TRANSFER_LINEAR:
+ return Description::TransferFunction::LINEAR;
+ default:
+ return Description::TransferFunction::SRGB;
+ }
+}
+
+bool Description::hasInputTransformMatrix() const {
+ const mat4 identity;
+ return inputTransformMatrix != identity;
+}
+
+bool Description::hasOutputTransformMatrix() const {
+ const mat4 identity;
+ return outputTransformMatrix != identity;
+}
+
+bool Description::hasColorMatrix() const {
+ const mat4 identity;
+ return colorMatrix != identity;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Mesh.cpp b/libs/renderengine/Mesh.cpp
similarity index 72%
rename from services/surfaceflinger/RenderEngine/Mesh.cpp
rename to libs/renderengine/Mesh.cpp
index 6a62b1d..f5387f2 100644
--- a/services/surfaceflinger/RenderEngine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "Mesh.h"
+#include <renderengine/Mesh.h>
#include <utils/Log.h>
namespace android {
+namespace renderengine {
Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
: mVertexCount(vertexCount),
@@ -26,20 +27,22 @@
mTexCoordsSize(texCoordSize),
mPrimitive(primitive) {
if (vertexCount == 0) {
- mVertices = new float[1];
+ mVertices.resize(1);
mVertices[0] = 0.0f;
mStride = 0;
return;
}
- size_t stride = vertexSize + texCoordSize;
+ const size_t CROP_COORD_SIZE = 2;
+ size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
size_t remainder = (stride * vertexCount) / vertexCount;
// Since all of the input parameters are unsigned, if stride is less than
// either vertexSize or texCoordSize, it must have overflowed. remainder
// will be equal to stride as long as stride * vertexCount doesn't overflow.
if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize);
- mVertices = new float[1];
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
+ CROP_COORD_SIZE);
+ mVertices.resize(1);
mVertices[0] = 0.0f;
mVertexCount = 0;
mVertexSize = 0;
@@ -48,30 +51,33 @@
return;
}
- mVertices = new float[stride * vertexCount];
+ mVertices.resize(stride * vertexCount);
mStride = stride;
}
-Mesh::~Mesh() {
- delete[] mVertices;
-}
-
Mesh::Primitive Mesh::getPrimitive() const {
return mPrimitive;
}
float const* Mesh::getPositions() const {
- return mVertices;
+ return mVertices.data();
}
float* Mesh::getPositions() {
- return mVertices;
+ return mVertices.data();
}
float const* Mesh::getTexCoords() const {
- return mVertices + mVertexSize;
+ return mVertices.data() + mVertexSize;
}
float* Mesh::getTexCoords() {
- return mVertices + mVertexSize;
+ return mVertices.data() + mVertexSize;
+}
+
+float const* Mesh::getCropCoords() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+float* Mesh::getCropCoords() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize;
}
size_t Mesh::getVertexCount() const {
@@ -94,4 +100,5 @@
return mStride;
}
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
new file mode 100644
index 0000000..c00fbba
--- /dev/null
+++ b/libs/renderengine/OWNERS
@@ -0,0 +1,2 @@
+lpy@google.com
+stoza@google.com
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
new file mode 100644
index 0000000..166c267
--- /dev/null
+++ b/libs/renderengine/RenderEngine.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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 <renderengine/RenderEngine.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <private/gui/SyncFeatures.h>
+#include "gl/GLESRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+
+std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags,
+ uint32_t imageCacheSize) {
+ char prop[PROPERTY_VALUE_MAX];
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+ if (strcmp(prop, "gles") == 0) {
+ ALOGD("RenderEngine GLES Backend");
+ return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);
+ }
+ ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
+ return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);
+}
+
+RenderEngine::~RenderEngine() = default;
+
+namespace impl {
+
+RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {}
+
+RenderEngine::~RenderEngine() = default;
+
+bool RenderEngine::useNativeFenceSync() const {
+ return SyncFeatures::getInstance().useNativeFenceSync();
+}
+
+bool RenderEngine::useWaitSync() const {
+ return SyncFeatures::getInstance().useWaitSync();
+}
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING
new file mode 100644
index 0000000..995dba1
--- /dev/null
+++ b/libs/renderengine/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "librenderengine_test"
+ }
+ ]
+}
diff --git a/services/surfaceflinger/RenderEngine/Texture.cpp b/libs/renderengine/Texture.cpp
similarity index 93%
rename from services/surfaceflinger/RenderEngine/Texture.cpp
rename to libs/renderengine/Texture.cpp
index 351430f..154cde8 100644
--- a/services/surfaceflinger/RenderEngine/Texture.cpp
+++ b/libs/renderengine/Texture.cpp
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#include <string.h>
-
-#include "Texture.h"
+#include <renderengine/Texture.h>
namespace android {
+namespace renderengine {
Texture::Texture()
: mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {}
@@ -74,4 +73,5 @@
return mHeight;
}
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
new file mode 100644
index 0000000..f651309
--- /dev/null
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -0,0 +1,1432 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLESRenderEngine.h"
+
+#include <math.h>
+#include <fstream>
+#include <sstream>
+#include <unordered_set>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
+#include <renderengine/private/Description.h>
+#include <sync/sync.h>
+#include <ui/ColorSpace.h>
+#include <ui/DebugUtils.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <utils/KeyedVector.h>
+#include <utils/Trace.h>
+#include "GLExtensions.h"
+#include "GLFramebuffer.h"
+#include "GLImage.h"
+#include "Program.h"
+#include "ProgramCache.h"
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
+bool checkGlError(const char* op, int lineNumber) {
+ bool errorFound = false;
+ GLint error = glGetError();
+ while (error != GL_NO_ERROR) {
+ errorFound = true;
+ error = glGetError();
+ ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+ }
+ return errorFound;
+}
+
+static constexpr bool outputDebugPPMs = false;
+
+void writePPM(const char* basename, GLuint width, GLuint height) {
+ ALOGV("writePPM #%s: %d x %d", basename, width, height);
+
+ std::vector<GLubyte> pixels(width * height * 4);
+ std::vector<GLubyte> outBuffer(width * height * 3);
+
+ // TODO(courtneygo): We can now have float formats, need
+ // to remove this code or update to support.
+ // Make returned pixels fit in uint32_t, one byte per component
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+ if (checkGlError(__FUNCTION__, __LINE__)) {
+ return;
+ }
+
+ std::string filename(basename);
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
+ file << "P6\n";
+ file << width << "\n";
+ file << height << "\n";
+ file << 255 << "\n";
+
+ auto ptr = reinterpret_cast<char*>(pixels.data());
+ auto outPtr = reinterpret_cast<char*>(outBuffer.data());
+ for (int y = height - 1; y >= 0; y--) {
+ char* data = ptr + y * width * sizeof(uint32_t);
+
+ for (GLuint x = 0; x < width; x++) {
+ // Only copy R, G and B components
+ outPtr[0] = data[0];
+ outPtr[1] = data[1];
+ outPtr[2] = data[2];
+ data += sizeof(uint32_t);
+ outPtr += 3;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+}
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+using base::StringAppendF;
+using ui::Dataspace;
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+ EGLint wanted, EGLConfig* outConfig) {
+ EGLint numConfigs = -1, n = 0;
+ eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+ configs.resize(n);
+
+ if (!configs.empty()) {
+ if (attribute != EGL_NONE) {
+ for (EGLConfig config : configs) {
+ EGLint value = 0;
+ eglGetConfigAttrib(dpy, config, attribute, &value);
+ if (wanted == value) {
+ *outConfig = config;
+ return NO_ERROR;
+ }
+ }
+ } else {
+ // just pick the first one
+ *outConfig = configs[0];
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+class EGLAttributeVector {
+ struct Attribute;
+ class Adder;
+ friend class Adder;
+ KeyedVector<Attribute, EGLint> mList;
+ struct Attribute {
+ Attribute() : v(0){};
+ explicit Attribute(EGLint v) : v(v) {}
+ EGLint v;
+ bool operator<(const Attribute& other) const {
+ // this places EGL_NONE at the end
+ EGLint lhs(v);
+ EGLint rhs(other.v);
+ if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
+ if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
+ return lhs < rhs;
+ }
+ };
+ class Adder {
+ friend class EGLAttributeVector;
+ EGLAttributeVector& v;
+ EGLint attribute;
+ Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {}
+
+ public:
+ void operator=(EGLint value) {
+ if (attribute != EGL_NONE) {
+ v.mList.add(Attribute(attribute), value);
+ }
+ }
+ operator EGLint() const { return v.mList[attribute]; }
+ };
+
+public:
+ EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); }
+ void remove(EGLint attribute) {
+ if (attribute != EGL_NONE) {
+ mList.removeItem(Attribute(attribute));
+ }
+ }
+ Adder operator[](EGLint attribute) { return Adder(*this, attribute); }
+ EGLint operator[](EGLint attribute) const { return mList[attribute]; }
+ // cast-operator to (EGLint const*)
+ operator EGLint const*() const { return &mList.keyAt(0).v; }
+};
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+ EGLConfig* config) {
+ // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+ // it is to be used with WIFI displays
+ status_t err;
+ EGLint wantedAttribute;
+ EGLint wantedAttributeValue;
+
+ EGLAttributeVector attribs;
+ if (renderableType) {
+ attribs[EGL_RENDERABLE_TYPE] = renderableType;
+ attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
+ attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
+ attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
+ attribs[EGL_RED_SIZE] = 8;
+ attribs[EGL_GREEN_SIZE] = 8;
+ attribs[EGL_BLUE_SIZE] = 8;
+ attribs[EGL_ALPHA_SIZE] = 8;
+ wantedAttribute = EGL_NONE;
+ wantedAttributeValue = EGL_NONE;
+ } else {
+ // if no renderable type specified, fallback to a simplified query
+ wantedAttribute = EGL_NATIVE_VISUAL_ID;
+ wantedAttributeValue = format;
+ }
+
+ err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config);
+ if (err == NO_ERROR) {
+ EGLint caveat;
+ if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+ ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+ }
+
+ return err;
+}
+
+std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags,
+ uint32_t imageCacheSize) {
+ // initialize EGL for the default display
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(display, nullptr, nullptr)) {
+ LOG_ALWAYS_FATAL("failed to initialize EGL");
+ }
+
+ GLExtensions& extensions = GLExtensions::getInstance();
+ extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
+ eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
+
+ // The code assumes that ES2 or later is available if this extension is
+ // supported.
+ EGLConfig config = EGL_NO_CONFIG;
+ if (!extensions.hasNoConfigContext()) {
+ config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+
+ bool useContextPriority = extensions.hasContextPriority() &&
+ (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
+ EGLContext protectedContext = EGL_NO_CONTEXT;
+ if (extensions.hasProtectedContent()) {
+ protectedContext = createEglContext(display, config, nullptr, useContextPriority,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+ }
+
+ EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
+ Protection::UNPROTECTED);
+
+ // if can't create a GL context, we can only abort.
+ LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+ EGLSurface dummy = EGL_NO_SURFACE;
+ if (!extensions.hasSurfacelessContext()) {
+ dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED);
+ LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+ }
+ EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
+ extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+ glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+ // In order to have protected contents in GPU composition, the OpenGL ES extension
+ // GL_EXT_protected_textures must be supported. If it's not supported, reset
+ // protected context to EGL_NO_CONTEXT to indicate that protected contents is not supported.
+ if (!extensions.hasProtectedTexture()) {
+ protectedContext = EGL_NO_CONTEXT;
+ }
+
+ EGLSurface protectedDummy = EGL_NO_SURFACE;
+ if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+ protectedDummy =
+ createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED);
+ ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer");
+ }
+
+ // now figure out what version of GL did we actually get
+ GlesVersion version = parseGlesVersion(extensions.getVersion());
+
+ // initialize the renderer while GL is current
+ std::unique_ptr<GLESRenderEngine> engine;
+ switch (version) {
+ case GLES_VERSION_1_0:
+ case GLES_VERSION_1_1:
+ LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
+ break;
+ case GLES_VERSION_2_0:
+ case GLES_VERSION_3_0:
+ engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy,
+ protectedContext, protectedDummy,
+ imageCacheSize);
+ break;
+ }
+
+ ALOGI("OpenGL ES informations:");
+ ALOGI("vendor : %s", extensions.getVendor());
+ ALOGI("renderer : %s", extensions.getRenderer());
+ ALOGI("version : %s", extensions.getVersion());
+ ALOGI("extensions: %s", extensions.getExtensions());
+ ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+ ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+ return engine;
+}
+
+EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+ status_t err;
+ EGLConfig config;
+
+ // First try to get an ES3 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES3 fails, try to get an ES2 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES2 still doesn't work, probably because we're on the emulator.
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ err = selectEGLConfig(display, format, 0, &config);
+ if (err != NO_ERROR) {
+ // this EGL is too lame for android
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ }
+ }
+ }
+
+ if (logConfig) {
+ // print some debugging info
+ EGLint r, g, b, a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ ALOGI("EGL information:");
+ ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
+ ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
+ ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+ ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+ ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+ }
+
+ return config;
+}
+
+GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config,
+ EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
+ EGLSurface protectedDummy, uint32_t imageCacheSize)
+ : renderengine::impl::RenderEngine(featureFlags),
+ mEGLDisplay(display),
+ mEGLConfig(config),
+ mEGLContext(ctxt),
+ mDummySurface(dummy),
+ mProtectedEGLContext(protectedContext),
+ mProtectedDummySurface(protectedDummy),
+ mVpWidth(0),
+ mVpHeight(0),
+ mFramebufferImageCacheSize(imageCacheSize),
+ mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+ // Initialize protected EGL Context.
+ if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+ EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface,
+ mProtectedEGLContext);
+ ALOGE_IF(!success, "can't make protected context current");
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+ success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
+ }
+
+ const uint16_t protTexData[] = {0};
+ glGenTextures(1, &mProtectedTexName);
+ glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
+
+ // mColorBlindnessCorrection = M;
+
+ if (mUseColorManagement) {
+ const ColorSpace srgb(ColorSpace::sRGB());
+ const ColorSpace displayP3(ColorSpace::DisplayP3());
+ const ColorSpace bt2020(ColorSpace::BT2020());
+
+ // no chromatic adaptation needed since all color spaces use D65 for their white points.
+ mSrgbToXyz = mat4(srgb.getRGBtoXYZ());
+ mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ());
+ mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ());
+ mXyzToSrgb = mat4(srgb.getXYZtoRGB());
+ mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
+ mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
+
+ // Compute sRGB to Display P3 and BT2020 transform matrix.
+ // NOTE: For now, we are limiting output wide color space support to
+ // Display-P3 and BT2020 only.
+ mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz;
+ mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz;
+
+ // Compute Display P3 to sRGB and BT2020 transform matrix.
+ mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz;
+ mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz;
+
+ // Compute BT2020 to sRGB and Display P3 transform matrix
+ mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz;
+ mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.egl.traceGpuCompletion", value, "0");
+ if (atoi(value)) {
+ mTraceGpuCompletion = true;
+ mFlushTracer = std::make_unique<FlushTracer>(this);
+ }
+ mDrawingBuffer = createFramebuffer();
+}
+
+GLESRenderEngine::~GLESRenderEngine() {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ unbindFrameBuffer(mDrawingBuffer.get());
+ mDrawingBuffer = nullptr;
+ while (!mFramebufferImageCache.empty()) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ }
+ mImageCache.clear();
+ eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEGLDisplay);
+}
+
+std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
+ return std::make_unique<GLFramebuffer>(*this);
+}
+
+std::unique_ptr<Image> GLESRenderEngine::createImage() {
+ return std::make_unique<GLImage>(*this);
+}
+
+Framebuffer* GLESRenderEngine::getFramebufferForDrawing() {
+ return mDrawingBuffer.get();
+}
+
+void GLESRenderEngine::primeCache() const {
+ ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+ mFeatureFlags & USE_COLOR_MANAGEMENT);
+}
+
+bool GLESRenderEngine::isCurrent() const {
+ return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
+}
+
+base::unique_fd GLESRenderEngine::flush() {
+ ATRACE_CALL();
+ if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
+ }
+
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
+
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+ }
+
+ // Only trace if we have a valid fence, as current usage falls back to
+ // calling finish() if the fence fd is invalid.
+ if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) {
+ mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
+ }
+
+ return fenceFd;
+}
+
+bool GLESRenderEngine::finish() {
+ ATRACE_CALL();
+ if (!GLExtensions::getInstance().hasFenceSync()) {
+ ALOGW("no synchronization support");
+ return false;
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) {
+ mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
+ }
+
+ return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR);
+}
+
+bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) {
+ EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (result != EGL_CONDITION_SATISFIED_KHR) {
+ if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGW("fence wait timed out");
+ } else {
+ ALOGW("error waiting on EGL fence: %#x", error);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+ !GLExtensions::getInstance().hasWaitSync()) {
+ return false;
+ }
+
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ // fenceFd is now owned by EGLSync
+ (void)fenceFd.release();
+
+ // XXX: The spec draft is inconsistent as to whether this should return an
+ // EGLint or void. Ignore the return value for now, as it's not strictly
+ // needed.
+ eglWaitSyncKHR(mEGLDisplay, sync, 0);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (error != EGL_SUCCESS) {
+ ALOGE("failed to wait for EGL native fence sync: %#x", error);
+ return false;
+ }
+
+ return true;
+}
+
+void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
+ ATRACE_CALL();
+ glDisable(GL_BLEND);
+ glClearColor(red, green, blue, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue,
+ float alpha) {
+ size_t c;
+ Rect const* r = region.getArray(&c);
+ Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ for (size_t i = 0; i < c; i++, r++) {
+ position[i * 6 + 0].x = r->left;
+ position[i * 6 + 0].y = r->top;
+ position[i * 6 + 1].x = r->left;
+ position[i * 6 + 1].y = r->bottom;
+ position[i * 6 + 2].x = r->right;
+ position[i * 6 + 2].y = r->bottom;
+ position[i * 6 + 3].x = r->left;
+ position[i * 6 + 3].y = r->top;
+ position[i * 6 + 4].x = r->right;
+ position[i * 6 + 4].y = r->bottom;
+ position[i * 6 + 5].x = r->right;
+ position[i * 6 + 5].y = r->top;
+ }
+ setupFillWithColor(red, green, blue, alpha);
+ drawMesh(mesh);
+}
+
+void GLESRenderEngine::setScissor(const Rect& region) {
+ // Invert y-coordinate to map to GL-space.
+ int32_t canvasHeight = mFboHeight;
+ int32_t glBottom = canvasHeight - region.bottom;
+
+ glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
+ glEnable(GL_SCISSOR_TEST);
+}
+
+void GLESRenderEngine::disableScissor() {
+ glDisable(GL_SCISSOR_TEST);
+}
+
+void GLESRenderEngine::genTextures(size_t count, uint32_t* names) {
+ glGenTextures(count, names);
+}
+
+void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+ glDeleteTextures(count, names);
+}
+
+void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ ATRACE_CALL();
+ const GLImage& glImage = static_cast<const GLImage&>(image);
+ const GLenum target = GL_TEXTURE_EXTERNAL_OES;
+
+ glBindTexture(target, texName);
+ if (supportsProtectedContent()) {
+ glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT,
+ glImage.isProtected() ? GL_TRUE : GL_FALSE);
+ }
+ if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
+ glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
+ }
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+ sp<Fence> bufferFence) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
+ sp<GraphicBuffer> buffer,
+ sp<Fence> bufferFence) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+ ATRACE_CALL();
+ auto cachedImage = mImageCache.find(buffer->getId());
+
+ if (cachedImage != mImageCache.end()) {
+ bindExternalTextureImage(texName, *cachedImage->second);
+ return NO_ERROR;
+ }
+
+ std::unique_ptr<Image> newImage = createImage();
+
+ bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
+ buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ if (!created) {
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ bindExternalTextureImage(texName, *createImage());
+ return NO_INIT;
+ }
+
+ bindExternalTextureImage(texName, *newImage);
+
+ // Wait for the new buffer to be ready.
+ if (bufferFence != nullptr && bufferFence->isValid()) {
+ if (GLExtensions::getInstance().hasWaitSync()) {
+ base::unique_fd fenceFd(bufferFence->dup());
+ if (fenceFd == -1) {
+ ALOGE("error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ if (!waitFence(std::move(fenceFd))) {
+ ALOGE("failed to wait on fence fd");
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
+ if (err != NO_ERROR) {
+ ALOGE("error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+
+ return NO_ERROR;
+}
+
+void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ const auto& cachedImage = mImageCache.find(bufferId);
+ if (cachedImage != mImageCache.end()) {
+ ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
+ mImageCache.erase(bufferId);
+ return;
+ }
+ ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
+}
+
+FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
+ // Translate win by the rounded corners rect coordinates, to have all values in
+ // layer coordinate space.
+ FloatRect cropWin = layer.geometry.boundaries;
+ const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop;
+ cropWin.left -= roundedCornersCrop.left;
+ cropWin.right -= roundedCornersCrop.left;
+ cropWin.top -= roundedCornersCrop.top;
+ cropWin.bottom -= roundedCornersCrop.top;
+ Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>());
+ cropCoords[0] = vec2(cropWin.left, cropWin.top);
+ cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight());
+ cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight());
+ cropCoords[3] = vec2(cropWin.right, cropWin.top);
+
+ setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight());
+ return cropWin;
+}
+
+status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
+ ATRACE_CALL();
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
+ EGLImageKHR eglImage = glFramebuffer->getEGLImage();
+ uint32_t textureName = glFramebuffer->getTextureName();
+ uint32_t framebufferName = glFramebuffer->getFramebufferName();
+
+ // Bind the texture and turn our EGLImage into a texture
+ glBindTexture(GL_TEXTURE_2D, textureName);
+ if (supportsProtectedContent()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PROTECTED_EXT,
+ mInProtectedContext ? GL_TRUE : GL_FALSE);
+ }
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
+
+ // Bind the Framebuffer to render into
+ glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
+
+ mFboHeight = glFramebuffer->getBufferHeight();
+
+ uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+ ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
+ glStatus);
+
+ return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
+}
+
+void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
+ ATRACE_CALL();
+ mFboHeight = 0;
+
+ // back to main framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void GLESRenderEngine::checkErrors() const {
+ do {
+ // there could be more than one error flag
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) break;
+ ALOGE("GL error 0x%04x", int(error));
+ } while (true);
+}
+
+bool GLESRenderEngine::supportsProtectedContent() const {
+ return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
+ if (useProtectedContext == mInProtectedContext) {
+ return true;
+ }
+ if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
+ return false;
+ }
+ const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface;
+ const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+ const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+ if (success) {
+ mInProtectedContext = useProtectedContext;
+ }
+ return success;
+}
+EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
+ bool isProtected) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
+ uint64_t bufferId = graphicBuffer->getId();
+ for (const auto& image : mFramebufferImageCache) {
+ if (image.first == bufferId) {
+ return image.second;
+ }
+ }
+ EGLint attributes[] = {
+ isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+ isProtected ? EGL_TRUE : EGL_NONE,
+ EGL_NONE,
+ };
+ EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ nativeBuffer, attributes);
+ if (image != EGL_NO_IMAGE_KHR) {
+ if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ }
+ mFramebufferImageCache.push_back({bufferId, image});
+ }
+ return image;
+}
+
+status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* const buffer,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+ ATRACE_CALL();
+ if (layers.empty()) {
+ ALOGV("Drawing empty layer stack");
+ return NO_ERROR;
+ }
+
+ if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) {
+ ATRACE_NAME("Waiting before draw");
+ sync_wait(bufferFence.get(), -1);
+ }
+
+ if (buffer == nullptr) {
+ ALOGE("No output buffer provided. Aborting GPU composition.");
+ return BAD_VALUE;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+
+ BindNativeBufferAsFramebuffer fbo(*this, buffer);
+
+ if (fbo.getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return fbo.getStatus();
+ }
+
+ // clear the entire buffer, sometimes when we reuse buffers we'd persist
+ // ghost images otherwise.
+ // we also require a full transparent framebuffer for overlays. This is
+ // probably not quite efficient on all GPUs, since we could filter out
+ // opaque layers.
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+
+ setOutputDataSpace(display.outputDataspace);
+ setDisplayMaxLuminance(display.maxLuminance);
+
+ mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+ mState.projectionMatrix = projectionMatrix;
+ if (!display.clearRegion.isEmpty()) {
+ glDisable(GL_BLEND);
+ fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+ }
+
+ Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+ for (auto layer : layers) {
+ mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+ const FloatRect bounds = layer.geometry.boundaries;
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ position[0] = vec2(bounds.left, bounds.top);
+ position[1] = vec2(bounds.left, bounds.bottom);
+ position[2] = vec2(bounds.right, bounds.bottom);
+ position[3] = vec2(bounds.right, bounds.top);
+
+ setupLayerCropping(layer, mesh);
+ setColorTransform(display.colorTransform * layer.colorTransform);
+
+ bool usePremultipliedAlpha = true;
+ bool disableTexture = true;
+ bool isOpaque = false;
+
+ if (layer.source.buffer.buffer != nullptr) {
+ disableTexture = false;
+ isOpaque = layer.source.buffer.isOpaque;
+
+ sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+ bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
+ layer.source.buffer.fence);
+
+ usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+ mat4 texMatrix = layer.source.buffer.textureTransform;
+
+ texture.setMatrix(texMatrix.asArray());
+ texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+ texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+ setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+ renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+ texCoords[0] = vec2(0.0, 0.0);
+ texCoords[1] = vec2(0.0, 1.0);
+ texCoords[2] = vec2(1.0, 1.0);
+ texCoords[3] = vec2(1.0, 0.0);
+ setupLayerTexturing(texture);
+ }
+
+ const half3 solidColor = layer.source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ // Buffer sources will have a black solid color ignored in the shader,
+ // so in that scenario the solid color passed here is arbitrary.
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
+ glDisable(GL_BLEND);
+ }
+ setSourceDataSpace(layer.sourceDataspace);
+
+ drawMesh(mesh);
+
+ // Cleanup if there's a buffer source
+ if (layer.source.buffer.buffer != nullptr) {
+ disableBlending();
+ setSourceY410BT2020(false);
+ disableTexturing();
+ }
+ }
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ if (drawFence == nullptr || drawFence->get() < 0) {
+ bool success = finish();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ checkErrors();
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+ }
+
+ checkErrors();
+ }
+ return NO_ERROR;
+}
+
+void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ ui::Transform::orientation_flags rotation) {
+ setViewportAndProjection(Rect(vpw, vph), sourceCrop);
+
+ if (rotation == ui::Transform::ROT_0) {
+ return;
+ }
+
+ // Apply custom rotation to the projection.
+ float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+ mat4 m = mState.projectionMatrix;
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
+ break;
+ case ui::Transform::ROT_180:
+ m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
+ break;
+ case ui::Transform::ROT_270:
+ m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
+ break;
+ default:
+ break;
+ }
+ mState.projectionMatrix = m;
+}
+
+void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
+ ATRACE_CALL();
+ mVpWidth = viewport.getWidth();
+ mVpHeight = viewport.getHeight();
+
+ // We pass the the top left corner instead of the bottom left corner,
+ // because since we're rendering off-screen first.
+ glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight);
+
+ mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1);
+}
+
+void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color, float cornerRadius) {
+ mState.isPremultipliedAlpha = premultipliedAlpha;
+ mState.isOpaque = opaque;
+ mState.color = color;
+ mState.cornerRadius = cornerRadius;
+
+ if (disableTexture) {
+ mState.textureEnabled = false;
+ }
+
+ if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
+ glEnable(GL_BLEND);
+ glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glDisable(GL_BLEND);
+ }
+}
+
+void GLESRenderEngine::setSourceY410BT2020(bool enable) {
+ mState.isY410BT2020 = enable;
+}
+
+void GLESRenderEngine::setSourceDataSpace(Dataspace source) {
+ mDataSpace = source;
+}
+
+void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) {
+ mOutputDataSpace = dataspace;
+}
+
+void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
+ mState.displayMaxLuminance = maxLuminance;
+}
+
+void GLESRenderEngine::setupLayerTexturing(const Texture& texture) {
+ GLuint target = texture.getTextureTarget();
+ glBindTexture(target, texture.getTextureName());
+ GLenum filter = GL_NEAREST;
+ if (texture.getFiltering()) {
+ filter = GL_LINEAR;
+ }
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+
+ mState.texture = texture;
+ mState.textureEnabled = true;
+}
+
+void GLESRenderEngine::setupLayerBlackedOut() {
+ glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
+ Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
+ texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
+ mState.texture = texture;
+ mState.textureEnabled = true;
+}
+
+void GLESRenderEngine::setColorTransform(const mat4& colorTransform) {
+ mState.colorMatrix = colorTransform;
+}
+
+void GLESRenderEngine::disableTexturing() {
+ mState.textureEnabled = false;
+}
+
+void GLESRenderEngine::disableBlending() {
+ glDisable(GL_BLEND);
+}
+
+void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) {
+ mState.isPremultipliedAlpha = true;
+ mState.isOpaque = false;
+ mState.color = half4(r, g, b, a);
+ mState.textureEnabled = false;
+ glDisable(GL_BLEND);
+}
+
+void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) {
+ mState.cropSize = half2(width, height);
+}
+
+void GLESRenderEngine::drawMesh(const Mesh& mesh) {
+ ATRACE_CALL();
+ if (mesh.getTexCoordsSize()) {
+ glEnableVertexAttribArray(Program::texCoords);
+ glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getTexCoords());
+ }
+
+ glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getPositions());
+
+ if (mState.cornerRadius > 0.0f) {
+ glEnableVertexAttribArray(Program::cropCoords);
+ glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getCropCoords());
+ }
+
+ // By default, DISPLAY_P3 is the only supported wide color output. However,
+ // when HDR content is present, hardware composer may be able to handle
+ // BT2020 data space, in that case, the output data space is set to be
+ // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
+ // to respect this and convert non-HDR content to HDR format.
+ if (mUseColorManagement) {
+ Description managedState = mState;
+ Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
+ Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+ Dataspace outputStandard =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK);
+ Dataspace outputTransfer =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+ bool needsXYZConversion = needsXYZTransformMatrix();
+
+ // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or
+ // STANDARD_BT2020, it will be treated as STANDARD_BT709
+ if (inputStandard != Dataspace::STANDARD_DCI_P3 &&
+ inputStandard != Dataspace::STANDARD_BT2020) {
+ inputStandard = Dataspace::STANDARD_BT709;
+ }
+
+ if (needsXYZConversion) {
+ // The supported input color spaces are standard RGB, Display P3 and BT2020.
+ switch (inputStandard) {
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.inputTransformMatrix = mDisplayP3ToXyz;
+ break;
+ case Dataspace::STANDARD_BT2020:
+ managedState.inputTransformMatrix = mBt2020ToXyz;
+ break;
+ default:
+ managedState.inputTransformMatrix = mSrgbToXyz;
+ break;
+ }
+
+ // The supported output color spaces are BT2020, Display P3 and standard RGB.
+ switch (outputStandard) {
+ case Dataspace::STANDARD_BT2020:
+ managedState.outputTransformMatrix = mXyzToBt2020;
+ break;
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.outputTransformMatrix = mXyzToDisplayP3;
+ break;
+ default:
+ managedState.outputTransformMatrix = mXyzToSrgb;
+ break;
+ }
+ } else if (inputStandard != outputStandard) {
+ // At this point, the input data space and output data space could be both
+ // HDR data spaces, but they match each other, we do nothing in this case.
+ // In addition to the case above, the input data space could be
+ // - scRGB linear
+ // - scRGB non-linear
+ // - sRGB
+ // - Display P3
+ // - BT2020
+ // The output data spaces could be
+ // - sRGB
+ // - Display P3
+ // - BT2020
+ switch (outputStandard) {
+ case Dataspace::STANDARD_BT2020:
+ if (inputStandard == Dataspace::STANDARD_BT709) {
+ managedState.outputTransformMatrix = mSrgbToBt2020;
+ } else if (inputStandard == Dataspace::STANDARD_DCI_P3) {
+ managedState.outputTransformMatrix = mDisplayP3ToBt2020;
+ }
+ break;
+ case Dataspace::STANDARD_DCI_P3:
+ if (inputStandard == Dataspace::STANDARD_BT709) {
+ managedState.outputTransformMatrix = mSrgbToDisplayP3;
+ } else if (inputStandard == Dataspace::STANDARD_BT2020) {
+ managedState.outputTransformMatrix = mBt2020ToDisplayP3;
+ }
+ break;
+ default:
+ if (inputStandard == Dataspace::STANDARD_DCI_P3) {
+ managedState.outputTransformMatrix = mDisplayP3ToSrgb;
+ } else if (inputStandard == Dataspace::STANDARD_BT2020) {
+ managedState.outputTransformMatrix = mBt2020ToSrgb;
+ }
+ break;
+ }
+ }
+
+ // we need to convert the RGB value to linear space and convert it back when:
+ // - there is a color matrix that is not an identity matrix, or
+ // - there is an output transform matrix that is not an identity matrix, or
+ // - the input transfer function doesn't match the output transfer function.
+ if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() ||
+ inputTransfer != outputTransfer) {
+ managedState.inputTransferFunction =
+ Description::dataSpaceToTransferFunction(inputTransfer);
+ managedState.outputTransferFunction =
+ Description::dataSpaceToTransferFunction(outputTransfer);
+ }
+
+ ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
+ : mEGLContext,
+ managedState);
+
+ glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+
+ if (outputDebugPPMs) {
+ static uint64_t managedColorFrameCount = 0;
+ std::ostringstream out;
+ out << "/data/texture_out" << managedColorFrameCount++;
+ writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+ }
+ } else {
+ ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
+ : mEGLContext,
+ mState);
+
+ glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+ }
+
+ if (mesh.getTexCoordsSize()) {
+ glDisableVertexAttribArray(Program::texCoords);
+ }
+
+ if (mState.cornerRadius > 0.0f) {
+ glDisableVertexAttribArray(Program::cropCoords);
+ }
+}
+
+size_t GLESRenderEngine::getMaxTextureSize() const {
+ return mMaxTextureSize;
+}
+
+size_t GLESRenderEngine::getMaxViewportDims() const {
+ return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
+}
+
+void GLESRenderEngine::dump(std::string& result) {
+ const GLExtensions& extensions = GLExtensions::getInstance();
+ ProgramCache& cache = ProgramCache::getInstance();
+
+ StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
+ StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
+ StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
+ extensions.getVersion());
+ StringAppendF(&result, "%s\n", extensions.getExtensions());
+ StringAppendF(&result, "RenderEngine is in protected context : %d\n", mInProtectedContext);
+ StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n",
+ cache.getSize(mEGLContext));
+ StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n",
+ cache.getSize(mProtectedEGLContext));
+ StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
+ dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
+}
+
+GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
+ int major, minor;
+ if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
+ if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
+ ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
+ return GLES_VERSION_1_0;
+ }
+ }
+
+ if (major == 1 && minor == 0) return GLES_VERSION_1_0;
+ if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
+ if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
+ if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
+
+ ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
+ return GLES_VERSION_1_0;
+}
+
+EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority,
+ Protection protection) {
+ EGLint renderableType = 0;
+ if (config == EGL_NO_CONFIG) {
+ renderableType = EGL_OPENGL_ES3_BIT;
+ } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+ EGLint contextClientVersion = 0;
+ if (renderableType & EGL_OPENGL_ES3_BIT) {
+ contextClientVersion = 3;
+ } else if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(7);
+ contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ contextAttributes.push_back(contextClientVersion);
+ if (useContextPriority) {
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ }
+ if (protection == Protection::PROTECTED) {
+ contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ contextAttributes.push_back(EGL_TRUE);
+ }
+ contextAttributes.push_back(EGL_NONE);
+
+ EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+
+ if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
+ // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
+ // EGL_NO_CONTEXT so that we can abort.
+ if (config != EGL_NO_CONFIG) {
+ return context;
+ }
+ // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should
+ // try to fall back to GLES 2.
+ contextAttributes[1] = 2;
+ context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+ }
+
+ return context;
+}
+
+EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection) {
+ EGLConfig dummyConfig = config;
+ if (dummyConfig == EGL_NO_CONFIG) {
+ dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+ std::vector<EGLint> attributes;
+ attributes.reserve(7);
+ attributes.push_back(EGL_WIDTH);
+ attributes.push_back(1);
+ attributes.push_back(EGL_HEIGHT);
+ attributes.push_back(1);
+ if (protection == Protection::PROTECTED) {
+ attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ attributes.push_back(EGL_TRUE);
+ }
+ attributes.push_back(EGL_NONE);
+
+ return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+}
+
+bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
+ const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
+ const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
+ return standard == Dataspace::STANDARD_BT2020 &&
+ (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
+}
+
+// For convenience, we want to convert the input color space to XYZ color space first,
+// and then convert from XYZ color space to output color space when
+// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
+// HDR content will be tone-mapped to SDR; Or,
+// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
+// HLG content to PQ content.
+// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
+// input data space or output data space is HDR data space, and the input transfer function
+// doesn't match the output transfer function, we would enable an intermediate transfrom to
+// XYZ color space.
+bool GLESRenderEngine::needsXYZTransformMatrix() const {
+ const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
+ const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
+ const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+ const Dataspace outputTransfer =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+
+ return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
+}
+
+bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ const auto& cachedImage = mImageCache.find(bufferId);
+ return cachedImage != mImageCache.end();
+}
+
+bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
+ [=](std::pair<uint64_t, EGLImageKHR> image) {
+ return image.first == bufferId;
+ });
+}
+
+// FlushTracer implementation
+GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
+ mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
+}
+
+GLESRenderEngine::FlushTracer::~FlushTracer() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mRunning = false;
+ }
+ mCondition.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ char name[64];
+ const uint64_t frameNum = mFramesQueued++;
+ snprintf(name, sizeof(name), "Queueing sync for frame: %lu",
+ static_cast<unsigned long>(frameNum));
+ ATRACE_NAME(name);
+ mQueue.push({sync, frameNum});
+ ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+ mCondition.notify_one();
+}
+
+void GLESRenderEngine::FlushTracer::loop() {
+ while (mRunning) {
+ QueueEntry entry;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mCondition.wait(mMutex,
+ [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
+
+ if (!mRunning) {
+ // if mRunning is false, then FlushTracer is being destroyed, so
+ // bail out now.
+ break;
+ }
+ entry = mQueue.front();
+ mQueue.pop();
+ }
+ {
+ char name[64];
+ snprintf(name, sizeof(name), "waiting for frame %lu",
+ static_cast<unsigned long>(entry.mFrameNum));
+ ATRACE_NAME(name);
+ mEngine->waitSync(entry.mSync, 0);
+ }
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
new file mode 100644
index 0000000..efb6ef0
--- /dev/null
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright 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 SF_GLESRENDERENGINE_H_
+#define SF_GLESRENDERENGINE_H_
+
+#include <android-base/thread_annotations.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/private/Description.h>
+
+#define EGL_NO_CONFIG ((EGLConfig)0)
+
+namespace android {
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace gl {
+
+class GLImage;
+
+class GLESRenderEngine : public impl::RenderEngine {
+public:
+ static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags,
+ uint32_t imageCacheSize);
+ static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+
+ GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag
+ EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
+ EGLContext protectedContext, EGLSurface protectedDummy,
+ uint32_t imageCacheSize);
+ ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
+
+ std::unique_ptr<Framebuffer> createFramebuffer() override;
+ std::unique_ptr<Image> createImage() override;
+
+ void primeCache() const override;
+ bool isCurrent() const override;
+ base::unique_fd flush() override;
+ bool finish() override;
+ bool waitFence(base::unique_fd fenceFd) override;
+ void clearWithColor(float red, float green, float blue, float alpha) override;
+ void fillRegionWithColor(const Region& region, float red, float green, float blue,
+ float alpha) override;
+ void setScissor(const Rect& region) override;
+ void disableScissor() 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, sp<GraphicBuffer> buffer, sp<Fence> fence)
+ EXCLUDES(mRenderingMutex);
+ void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+ void unbindFrameBuffer(Framebuffer* framebuffer) override;
+ void checkErrors() const override;
+
+ bool isProtected() const override { return mInProtectedContext; }
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) EXCLUDES(mRenderingMutex) override;
+
+ // internal to RenderEngine
+ EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
+ EGLConfig getEGLConfig() const { return mEGLConfig; }
+ // Creates an output image for rendering to
+ EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);
+
+ // Test-only methods
+ // Returns true iff mImageCache contains an image keyed by bufferId
+ bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
+ bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+
+protected:
+ Framebuffer* getFramebufferForDrawing() override;
+ void dump(std::string& result) override;
+ void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ ui::Transform::orientation_flags rotation) override;
+ void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color, float cornerRadius) override;
+ void setupLayerTexturing(const Texture& texture) override;
+ void setupLayerBlackedOut() override;
+ void setupFillWithColor(float r, float g, float b, float a) override;
+ void setColorTransform(const mat4& colorTransform) override;
+ void disableTexturing() override;
+ void disableBlending() override;
+ void setupCornerRadiusCropSize(float width, float height) override;
+
+ // HDR and color management related functions and state
+ void setSourceY410BT2020(bool enable) override;
+ void setSourceDataSpace(ui::Dataspace source) override;
+ void setOutputDataSpace(ui::Dataspace dataspace) override;
+ void setDisplayMaxLuminance(const float maxLuminance) override;
+
+ // drawing
+ void drawMesh(const Mesh& mesh) override;
+
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+private:
+ enum GlesVersion {
+ GLES_VERSION_1_0 = 0x10000,
+ GLES_VERSION_1_1 = 0x10001,
+ GLES_VERSION_2_0 = 0x20000,
+ GLES_VERSION_3_0 = 0x30000,
+ };
+
+ static GlesVersion parseGlesVersion(const char* str);
+ static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority,
+ Protection protection);
+ static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection);
+ bool waitSync(EGLSyncKHR sync, EGLint flags);
+
+ // A data space is considered HDR data space if it has BT2020 color space
+ // with PQ or HLG transfer function.
+ bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
+ bool needsXYZTransformMatrix() const;
+ // Defines the viewport, and sets the projection matrix to the projection
+ // defined by the clip.
+ void setViewportAndProjection(Rect viewport, Rect clip);
+ // Evicts stale images from the buffer cache.
+ void evictImages(const std::vector<LayerSettings>& layers);
+ // Computes the cropping window for the layer and sets up cropping
+ // coordinates for the mesh.
+ FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
+
+ EGLDisplay mEGLDisplay;
+ EGLConfig mEGLConfig;
+ EGLContext mEGLContext;
+ EGLSurface mDummySurface;
+ EGLContext mProtectedEGLContext;
+ EGLSurface mProtectedDummySurface;
+ GLuint mProtectedTexName;
+ GLint mMaxViewportDims[2];
+ GLint mMaxTextureSize;
+ GLuint mVpWidth;
+ GLuint mVpHeight;
+ Description mState;
+
+ mat4 mSrgbToXyz;
+ mat4 mDisplayP3ToXyz;
+ mat4 mBt2020ToXyz;
+ mat4 mXyzToSrgb;
+ mat4 mXyzToDisplayP3;
+ mat4 mXyzToBt2020;
+ mat4 mSrgbToDisplayP3;
+ mat4 mSrgbToBt2020;
+ mat4 mDisplayP3ToSrgb;
+ mat4 mDisplayP3ToBt2020;
+ mat4 mBt2020ToSrgb;
+ mat4 mBt2020ToDisplayP3;
+
+ bool mInProtectedContext = false;
+ // If set to true, then enables tracing flush() and finish() to systrace.
+ bool mTraceGpuCompletion = false;
+ int32_t mFboHeight = 0;
+ // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
+ // the last recently used buffer should be kicked out.
+ uint32_t mFramebufferImageCacheSize = 0;
+
+ // Cache of output images, keyed by corresponding GraphicBuffer ID.
+ std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache;
+
+ // Current dataspace of layer being rendered
+ ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
+
+ // Current output dataspace of the render engine
+ ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
+
+ // Whether device supports color management, currently color management
+ // supports sRGB, DisplayP3 color spaces.
+ const bool mUseColorManagement = false;
+
+ // Cache of GL images that we'll store per GraphicBuffer ID
+ std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
+ // Mutex guarding rendering operations, so that:
+ // 1. GL operations aren't interleaved, and
+ // 2. Internal state related to rendering that is potentially modified by
+ // multiple threads is guaranteed thread-safe.
+ std::mutex mRenderingMutex;
+
+ // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
+ // is held.
+ status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer,
+ sp<Fence> fence) REQUIRES(mRenderingMutex);
+
+ std::unique_ptr<Framebuffer> mDrawingBuffer;
+
+ class FlushTracer {
+ public:
+ FlushTracer(GLESRenderEngine* engine);
+ ~FlushTracer();
+ void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex);
+
+ struct QueueEntry {
+ EGLSyncKHR mSync = nullptr;
+ uint64_t mFrameNum = 0;
+ };
+
+ private:
+ void loop();
+ GLESRenderEngine* const mEngine;
+ std::thread mThread;
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+ std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
+ uint64_t mFramesQueued GUARDED_BY(mMutex) = 0;
+ bool mRunning = true;
+ };
+ friend class FlushTracer;
+ std::unique_ptr<FlushTracer> mFlushTracer;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
similarity index 60%
rename from services/surfaceflinger/RenderEngine/GLExtensions.cpp
rename to libs/renderengine/gl/GLExtensions.cpp
index dc09a37..2924b0e 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -14,33 +14,45 @@
* limitations under the License.
*/
+#include "GLExtensions.h"
+
+#include <string>
+#include <unordered_set>
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include "GLExtensions.h"
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
namespace android {
-// ---------------------------------------------------------------------------
+namespace renderengine {
+namespace gl {
-ANDROID_SINGLETON_STATIC_INSTANCE(GLExtensions)
+namespace {
-SortedVector<String8> GLExtensions::parseExtensionString(char const* extensions) {
- SortedVector<String8> list;
+class ExtensionSet {
+public:
+ ExtensionSet(const char* extensions) {
+ char const* curr = extensions;
+ char const* head = curr;
+ do {
+ head = strchr(curr, ' ');
+ size_t len = head ? head - curr : strlen(curr);
+ if (len > 0) {
+ mExtensions.emplace(curr, len);
+ }
+ curr = head + 1;
+ } while (head);
+ }
- char const* curr = extensions;
- char const* head = curr;
- do {
- head = strchr(curr, ' ');
- String8 s(curr, head ? head - curr : strlen(curr));
- if (s.length()) {
- list.add(s);
- }
- curr = head + 1;
- } while (head);
+ bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; }
- return list;
-}
+private:
+ std::unordered_set<std::string> mExtensions;
+};
+
+} // anonymous namespace
void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer,
GLubyte const* version, GLubyte const* extensions) {
@@ -48,12 +60,11 @@
mRenderer = (char const*)renderer;
mVersion = (char const*)version;
mExtensions = (char const*)extensions;
- mExtensionList = parseExtensionString(mExtensions);
-}
-bool GLExtensions::hasExtension(char const* extension) const {
- const String8 s(extension);
- return mExtensionList.indexOf(s) >= 0;
+ ExtensionSet extensionSet(mExtensions.c_str());
+ if (extensionSet.hasExtension("GL_EXT_protected_textures")) {
+ mHasProtectedTexture = true;
+ }
}
char const* GLExtensions::getVendor() const {
@@ -75,7 +86,8 @@
void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
mEGLVersion = eglVersion;
mEGLExtensions = eglExtensions;
- mEGLExtensionList = parseExtensionString(mEGLExtensions);
+
+ ExtensionSet extensionSet(eglExtensions);
// EGL_ANDROIDX_no_config_context is an experimental extension with no
// written specification. It will be replaced by something more formal.
@@ -85,30 +97,29 @@
//
// EGL_KHR_no_config_context is official extension to allow creating a
// context that works with any surface of a display.
- if (hasEGLExtension("EGL_ANDROIDX_no_config_context") ||
- hasEGLExtension("EGL_KHR_no_config_context")) {
+ if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") ||
+ extensionSet.hasExtension("EGL_KHR_no_config_context")) {
mHasNoConfigContext = true;
}
- if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+ if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) {
mHasNativeFenceSync = true;
}
- if (hasEGLExtension("EGL_KHR_fence_sync")) {
+ if (extensionSet.hasExtension("EGL_KHR_fence_sync")) {
mHasFenceSync = true;
}
- if (hasEGLExtension("EGL_KHR_wait_sync")) {
+ if (extensionSet.hasExtension("EGL_KHR_wait_sync")) {
mHasWaitSync = true;
}
-
- if (hasEGLExtension("EGL_ANDROID_image_crop")) {
- mHasImageCrop = true;
- }
- if (hasEGLExtension("EGL_EXT_protected_content")) {
+ if (extensionSet.hasExtension("EGL_EXT_protected_content")) {
mHasProtectedContent = true;
}
- if (hasEGLExtension("EGL_IMG_context_priority")) {
+ if (extensionSet.hasExtension("EGL_IMG_context_priority")) {
mHasContextPriority = true;
}
+ if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
+ mHasSurfacelessContext = true;
+ }
}
char const* GLExtensions::getEGLVersion() const {
@@ -119,10 +130,6 @@
return mEGLExtensions.string();
}
-bool GLExtensions::hasEGLExtension(char const* extension) const {
- const String8 s(extension);
- return mEGLExtensionList.indexOf(s) >= 0;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
similarity index 80%
rename from services/surfaceflinger/RenderEngine/GLExtensions.h
rename to libs/renderengine/gl/GLExtensions.h
index 0d8c10b..ef00009 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -20,55 +20,27 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Singleton.h>
-#include <utils/SortedVector.h>
-#include <utils/String8.h>
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
namespace android {
-// ---------------------------------------------------------------------------
+namespace renderengine {
+namespace gl {
class GLExtensions : public Singleton<GLExtensions> {
- friend class Singleton<GLExtensions>;
-
- bool mHasNoConfigContext = false;
- bool mHasNativeFenceSync = false;
- bool mHasFenceSync = false;
- bool mHasWaitSync = false;
- bool mHasImageCrop = false;
- bool mHasProtectedContent = false;
- bool mHasContextPriority = false;
-
- String8 mVendor;
- String8 mRenderer;
- String8 mVersion;
- String8 mExtensions;
- SortedVector<String8> mExtensionList;
-
- String8 mEGLVersion;
- String8 mEGLExtensions;
- SortedVector<String8> mEGLExtensionList;
-
- static SortedVector<String8> parseExtensionString(char const* extensions);
-
- GLExtensions(const GLExtensions&);
- GLExtensions& operator=(const GLExtensions&);
-
-protected:
- GLExtensions() = default;
-
public:
bool hasNoConfigContext() const { return mHasNoConfigContext; }
bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
bool hasFenceSync() const { return mHasFenceSync; }
bool hasWaitSync() const { return mHasWaitSync; }
- bool hasImageCrop() const { return mHasImageCrop; }
bool hasProtectedContent() const { return mHasProtectedContent; }
bool hasContextPriority() const { return mHasContextPriority; }
+ bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
+ bool hasProtectedTexture() const { return mHasProtectedTexture; }
void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
GLubyte const* extensions);
@@ -76,15 +48,39 @@
char const* getRenderer() const;
char const* getVersion() const;
char const* getExtensions() const;
- bool hasExtension(char const* extension) const;
void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
char const* getEGLVersion() const;
char const* getEGLExtensions() const;
- bool hasEGLExtension(char const* extension) const;
+
+protected:
+ GLExtensions() = default;
+
+private:
+ friend class Singleton<GLExtensions>;
+
+ bool mHasNoConfigContext = false;
+ bool mHasNativeFenceSync = false;
+ bool mHasFenceSync = false;
+ bool mHasWaitSync = false;
+ bool mHasProtectedContent = false;
+ bool mHasContextPriority = false;
+ bool mHasSurfacelessContext = false;
+ bool mHasProtectedTexture = false;
+
+ String8 mVendor;
+ String8 mRenderer;
+ String8 mVersion;
+ String8 mExtensions;
+ String8 mEGLVersion;
+ String8 mEGLExtensions;
+
+ GLExtensions(const GLExtensions&);
+ GLExtensions& operator=(const GLExtensions&);
};
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace gl
+} // namespace renderengine
+} // namespace android
#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
new file mode 100644
index 0000000..c45598c
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 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 "GLFramebuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
+ : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
+ glGenTextures(1, &mTextureName);
+ glGenFramebuffers(1, &mFramebufferName);
+}
+
+GLFramebuffer::~GLFramebuffer() {
+ glDeleteFramebuffers(1, &mFramebufferName);
+ glDeleteTextures(1, &mTextureName);
+}
+
+bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
+ ATRACE_CALL();
+ if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ mEGLImage = EGL_NO_IMAGE_KHR;
+ mBufferWidth = 0;
+ mBufferHeight = 0;
+ }
+
+ if (nativeBuffer) {
+ mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected);
+ if (mEGLImage == EGL_NO_IMAGE_KHR) {
+ return false;
+ }
+ mBufferWidth = nativeBuffer->width;
+ mBufferHeight = nativeBuffer->height;
+ }
+ return true;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
new file mode 100644
index 0000000..1289fbf
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 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 <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <renderengine/Framebuffer.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLFramebuffer : public renderengine::Framebuffer {
+public:
+ explicit GLFramebuffer(GLESRenderEngine& engine);
+ ~GLFramebuffer() override;
+
+ bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override;
+ EGLImageKHR getEGLImage() const { return mEGLImage; }
+ uint32_t getTextureName() const { return mTextureName; }
+ uint32_t getFramebufferName() const { return mFramebufferName; }
+ int32_t getBufferHeight() const { return mBufferHeight; }
+ int32_t getBufferWidth() const { return mBufferWidth; }
+
+private:
+ GLESRenderEngine& mEngine;
+ EGLDisplay mEGLDisplay;
+ EGLImageKHR mEGLImage;
+ uint32_t mTextureName, mFramebufferName;
+
+ int32_t mBufferHeight = 0;
+ int32_t mBufferWidth = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Image.cpp b/libs/renderengine/gl/GLImage.cpp
similarity index 61%
rename from services/surfaceflinger/RenderEngine/Image.cpp
rename to libs/renderengine/gl/GLImage.cpp
index 0d06422..77e648e 100644
--- a/services/surfaceflinger/RenderEngine/Image.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,30 +14,22 @@
* limitations under the License.
*/
-#include "Image.h"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLImage.h"
#include <vector>
#include <log/log.h>
-
+#include <utils/Trace.h>
+#include "GLESRenderEngine.h"
#include "GLExtensions.h"
-#include "RenderEngine.h"
namespace android {
-namespace RE {
+namespace renderengine {
+namespace gl {
-Image::~Image() = default;
-
-namespace impl {
-
-Image::Image(const RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
-
-Image::~Image() {
- setNativeWindowBuffer(nullptr, false, 0, 0);
-}
-
-static std::vector<EGLint> buildAttributeList(bool isProtected, int32_t cropWidth,
- int32_t cropHeight) {
+static std::vector<EGLint> buildAttributeList(bool isProtected) {
std::vector<EGLint> attrs;
attrs.reserve(16);
@@ -49,24 +41,19 @@
attrs.push_back(EGL_TRUE);
}
- if (cropWidth > 0 && cropHeight > 0) {
- attrs.push_back(EGL_IMAGE_CROP_LEFT_ANDROID);
- attrs.push_back(0);
- attrs.push_back(EGL_IMAGE_CROP_TOP_ANDROID);
- attrs.push_back(0);
- attrs.push_back(EGL_IMAGE_CROP_RIGHT_ANDROID);
- attrs.push_back(cropWidth);
- attrs.push_back(EGL_IMAGE_CROP_BOTTOM_ANDROID);
- attrs.push_back(cropHeight);
- }
-
attrs.push_back(EGL_NONE);
return attrs;
}
-bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
- int32_t cropHeight) {
+GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
+
+GLImage::~GLImage() {
+ setNativeWindowBuffer(nullptr, false);
+}
+
+bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
+ ATRACE_CALL();
if (mEGLImage != EGL_NO_IMAGE_KHR) {
if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
ALOGE("failed to destroy image: %#x", eglGetError());
@@ -75,18 +62,19 @@
}
if (buffer) {
- std::vector<EGLint> attrs = buildAttributeList(isProtected, cropWidth, cropHeight);
+ std::vector<EGLint> attrs = buildAttributeList(isProtected);
mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
static_cast<EGLClientBuffer>(buffer), attrs.data());
if (mEGLImage == EGL_NO_IMAGE_KHR) {
ALOGE("failed to create EGLImage: %#x", eglGetError());
return false;
}
+ mProtected = isProtected;
}
return true;
}
-} // namespace impl
-} // namespace RE
+} // namespace gl
+} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h
new file mode 100644
index 0000000..59d6ce3
--- /dev/null
+++ b/libs/renderengine/gl/GLImage.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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 <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android-base/macros.h>
+#include <renderengine/Image.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLImage : public renderengine::Image {
+public:
+ explicit GLImage(const GLESRenderEngine& engine);
+ ~GLImage() override;
+
+ bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
+
+ EGLImageKHR getEGLImage() const { return mEGLImage; }
+ bool isProtected() const { return mProtected; }
+
+private:
+ EGLDisplay mEGLDisplay;
+ EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+ bool mProtected = false;
+
+ DISALLOW_COPY_AND_ASSIGN(GLImage);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/libs/renderengine/gl/Program.cpp
similarity index 82%
rename from services/surfaceflinger/RenderEngine/Program.cpp
rename to libs/renderengine/gl/Program.cpp
index fe536f0..fe9d909 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -14,17 +14,18 @@
* limitations under the License.
*/
+#include "Program.h"
+
#include <stdint.h>
#include <log/log.h>
-#include <utils/String8.h>
-
#include <math/mat4.h>
-#include "Description.h"
-#include "Program.h"
+#include <utils/String8.h>
#include "ProgramCache.h"
namespace android {
+namespace renderengine {
+namespace gl {
Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
: mInitialized(false) {
@@ -35,6 +36,7 @@
glAttachShader(programId, fragmentId);
glBindAttribLocation(programId, position, "position");
glBindAttribLocation(programId, texCoords, "texCoords");
+ glBindAttribLocation(programId, cropCoords, "cropCoords");
glLinkProgram(programId);
GLint status;
@@ -65,6 +67,8 @@
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
+ mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
+ mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
// set-up the default values for our uniforms
glUseProgram(programId);
@@ -73,8 +77,6 @@
}
}
-Program::~Program() {}
-
bool Program::isValid() const {
return mInitialized;
}
@@ -111,45 +113,41 @@
return shader;
}
-String8& Program::dumpShader(String8& result, GLenum /*type*/) {
- GLuint shader = GL_FRAGMENT_SHADER ? mFragmentShader : mVertexShader;
- GLint l;
- glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l);
- char* src = new char[l];
- glGetShaderSource(shader, l, nullptr, src);
- result.append(src);
- delete[] src;
- return result;
-}
-
void Program::setUniforms(const Description& desc) {
// TODO: we should have a mechanism here to not always reset uniforms that
// didn't change for this program.
if (mSamplerLoc >= 0) {
glUniform1i(mSamplerLoc, 0);
- glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray());
+ glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray());
}
if (mColorLoc >= 0) {
- const float color[4] = {desc.mColor.r, desc.mColor.g, desc.mColor.b, desc.mColor.a};
+ const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
glUniform4fv(mColorLoc, 1, color);
}
if (mInputTransformMatrixLoc >= 0) {
- mat4 inputTransformMatrix = mat4(desc.mInputTransformMatrix);
+ mat4 inputTransformMatrix = desc.inputTransformMatrix;
glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
}
if (mOutputTransformMatrixLoc >= 0) {
// The output transform matrix and color matrix can be combined as one matrix
// that is applied right before applying OETF.
- mat4 outputTransformMatrix = desc.mColorMatrix * desc.mOutputTransformMatrix;
- glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE,
- outputTransformMatrix.asArray());
+ mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix;
+ glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray());
}
if (mDisplayMaxLuminanceLoc >= 0) {
- glUniform1f(mDisplayMaxLuminanceLoc, desc.mDisplayMaxLuminance);
+ glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
+ }
+ if (mCornerRadiusLoc >= 0) {
+ glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
+ }
+ if (mCropCenterLoc >= 0) {
+ glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
}
// these uniforms are always present
- glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray());
+ glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
}
-} /* namespace android */
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Program.h b/libs/renderengine/gl/Program.h
similarity index 78%
rename from services/surfaceflinger/RenderEngine/Program.h
rename to libs/renderengine/gl/Program.h
index ae796c5..bc9cf08 100644
--- a/services/surfaceflinger/RenderEngine/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -20,24 +20,35 @@
#include <stdint.h>
#include <GLES2/gl2.h>
-
-#include "Description.h"
+#include <renderengine/private/Description.h>
#include "ProgramCache.h"
namespace android {
class String8;
+namespace renderengine {
+namespace gl {
+
/*
* Abstracts a GLSL program comprising a vertex and fragment shader
*/
class Program {
public:
// known locations for position and texture coordinates
- enum { position = 0, texCoords = 1 };
+ enum {
+ /* position of each vertex for vertex shader */
+ position = 0,
+
+ /* UV coordinates for texture mapping */
+ texCoords = 1,
+
+ /* Crop coordinates, in pixels */
+ cropCoords = 2
+ };
Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
- ~Program();
+ ~Program() = default;
/* whether this object is usable */
bool isValid() const;
@@ -56,7 +67,6 @@
private:
GLuint buildShader(const char* source, GLenum type);
- String8& dumpShader(String8& result, GLenum type);
// whether the initialization succeeded
bool mInitialized;
@@ -84,8 +94,16 @@
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
GLint mOutputTransformMatrixLoc;
+
+ /* location of corner radius uniform */
+ GLint mCornerRadiusLoc;
+
+ /* location of surface crop origin uniform, for rounded corner clipping */
+ GLint mCropCenterLoc;
};
-} /* namespace android */
+} // namespace gl
+} // namespace renderengine
+} // namespace android
#endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
similarity index 85%
rename from services/surfaceflinger/RenderEngine/ProgramCache.cpp
rename to libs/renderengine/gl/ProgramCache.cpp
index 2073b05..49bdd2a 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -16,18 +16,21 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include "Description.h"
-#include "Program.h"
#include "ProgramCache.h"
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <log/log.h>
+#include <renderengine/private/Description.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+#include "Program.h"
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache)
+
namespace android {
-// -----------------------------------------------------------------------------------------------
+namespace renderengine {
+namespace gl {
/*
* A simple formatter class to automatically add the endl and
@@ -74,17 +77,11 @@
return f;
}
-// -----------------------------------------------------------------------------------------------
-
-ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache)
-
-ProgramCache::ProgramCache() {}
-
-ProgramCache::~ProgramCache() {}
-
-void ProgramCache::primeCache(bool hasWideColor) {
+void ProgramCache::primeCache(EGLContext context, bool useColorManagement) {
+ auto& cache = mCaches[context];
uint32_t shaderCount = 0;
- uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK;
+ uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
+ | Key::ROUNDED_CORNERS_MASK;
// Prime the cache for all combinations of the above masks,
// leaving off the experimental color matrix mask options.
@@ -96,16 +93,14 @@
if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
continue;
}
- Program* program = mCache.valueFor(shaderKey);
- if (program == nullptr) {
- program = generateProgram(shaderKey);
- mCache.add(shaderKey, program);
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
shaderCount++;
}
}
// Prime for sRGB->P3 conversion
- if (hasWideColor) {
+ if (useColorManagement) {
Key shaderKey;
shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK |
Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK,
@@ -115,10 +110,8 @@
shaderKey.set(Key::OPACITY_MASK,
(i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
- Program* program = mCache.valueFor(shaderKey);
- if (program == nullptr) {
- program = generateProgram(shaderKey);
- mCache.add(shaderKey, program);
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
shaderCount++;
}
}
@@ -132,31 +125,35 @@
ProgramCache::Key ProgramCache::computeKey(const Description& description) {
Key needs;
needs.set(Key::TEXTURE_MASK,
- !description.mTextureEnabled
+ !description.textureEnabled
? Key::TEXTURE_OFF
- : description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
+ : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
? Key::TEXTURE_EXT
- : description.mTexture.getTextureTarget() == GL_TEXTURE_2D
+ : description.texture.getTextureTarget() == GL_TEXTURE_2D
? Key::TEXTURE_2D
: Key::TEXTURE_OFF)
- .set(Key::ALPHA_MASK,
- (description.mColor.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
+ .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
.set(Key::BLEND_MASK,
- description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
+ description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
.set(Key::OPACITY_MASK,
- description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
+ description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
.set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix() ?
- Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ description.hasInputTransformMatrix()
+ ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
- description.hasOutputTransformMatrix() || description.hasColorMatrix() ?
- Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF);
+ description.hasOutputTransformMatrix() || description.hasColorMatrix()
+ ? Key::OUTPUT_TRANSFORM_MATRIX_ON
+ : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::ROUNDED_CORNERS_MASK,
+ description.cornerRadius > 0
+ ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
needs.set(Key::Y410_BT2020_MASK,
- description.mY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+ description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
- switch (description.mInputTransferFunction) {
+ if (needs.hasTransformMatrix() ||
+ (description.inputTransferFunction != description.outputTransferFunction)) {
+ switch (description.inputTransferFunction) {
case Description::TransferFunction::LINEAR:
default:
needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
@@ -172,7 +169,7 @@
break;
}
- switch (description.mOutputTransferFunction) {
+ switch (description.outputTransferFunction) {
case Description::TransferFunction::LINEAR:
default:
needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
@@ -522,6 +519,10 @@
vs << "attribute vec4 texCoords;"
<< "varying vec2 outTexCoords;";
}
+ if (needs.hasRoundedCorners()) {
+ vs << "attribute lowp vec4 cropCoords;";
+ vs << "varying lowp vec2 outCropCoords;";
+ }
vs << "attribute vec4 position;"
<< "uniform mat4 projection;"
<< "uniform mat4 texture;"
@@ -529,6 +530,9 @@
if (needs.isTexturing()) {
vs << "outTexCoords = (texture * texCoords).st;";
}
+ if (needs.hasRoundedCorners()) {
+ vs << "outCropCoords = cropCoords.st;";
+ }
vs << dedent << "}";
return vs.getString();
}
@@ -550,6 +554,27 @@
<< "varying vec2 outTexCoords;";
}
+ if (needs.hasRoundedCorners()) {
+ // Rounded corners implementation using a signed distance function.
+ fs << R"__SHADER__(
+ uniform float cornerRadius;
+ uniform vec2 cropCenter;
+ varying vec2 outCropCoords;
+
+ /**
+ * This function takes the current crop coordinates and calculates an alpha value based
+ * on the corner radius and distance from the crop center.
+ */
+ float applyCornerRadius(vec2 cropCoords)
+ {
+ vec2 position = cropCoords - cropCenter;
+ vec2 dist = abs(position) + vec2(cornerRadius) - cropCenter;
+ float plane = length(max(dist, vec2(0.0)));
+ return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
fs << "uniform vec4 color;";
}
@@ -640,18 +665,27 @@
// avoid divide by 0 by adding 0.5/256 to the alpha channel
fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
}
- fs << "gl_FragColor.rgb = OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+ fs << "gl_FragColor.rgb = "
+ "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
if (!needs.isOpaque() && needs.isPremultiplied()) {
// and re-premultiply if needed after gamma correction
fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
}
}
+ if (needs.hasRoundedCorners()) {
+ if (needs.isPremultiplied()) {
+ fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
+ } else {
+ fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
+ }
+ }
+
fs << dedent << "}";
return fs.getString();
}
-Program* ProgramCache::generateProgram(const Key& needs) {
+std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
ATRACE_CALL();
// vertex shader
@@ -660,32 +694,34 @@
// fragment shader
String8 fs = generateFragmentShader(needs);
- Program* program = new Program(needs, vs.string(), fs.string());
- return program;
+ return std::make_unique<Program>(needs, vs.string(), fs.string());
}
-void ProgramCache::useProgram(const Description& description) {
+void ProgramCache::useProgram(EGLContext context, const Description& description) {
// generate the key for the shader based on the description
Key needs(computeKey(description));
// look-up the program in the cache
- Program* program = mCache.valueFor(needs);
- if (program == nullptr) {
+ auto& cache = mCaches[context];
+ auto it = cache.find(needs);
+ if (it == cache.end()) {
// we didn't find our program, so generate one...
- nsecs_t time = -systemTime();
- program = generateProgram(needs);
- mCache.add(needs, program);
- time += systemTime();
+ nsecs_t time = systemTime();
+ it = cache.emplace(needs, generateProgram(needs)).first;
+ time = systemTime() - time;
- ALOGV(">>> generated new program: needs=%08X, time=%u ms (%zu programs)", needs.mKey,
- uint32_t(ns2ms(time)), mCache.size());
+ ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
+ context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
}
// here we have a suitable program for this description
+ std::unique_ptr<Program>& program = it->second;
if (program->isValid()) {
program->use();
program->setUniforms(description);
}
}
-} /* namespace android */
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
similarity index 79%
rename from services/surfaceflinger/RenderEngine/ProgramCache.h
rename to libs/renderengine/gl/ProgramCache.h
index 983e7ba..400ad74 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -17,20 +17,27 @@
#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
#define SF_RENDER_ENGINE_PROGRAMCACHE_H
-#include <GLES2/gl2.h>
+#include <memory>
+#include <unordered_map>
-#include <utils/KeyedVector.h>
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
#include <utils/Singleton.h>
#include <utils/TypeHelpers.h>
-#include "Description.h"
-
namespace android {
-class Description;
+class String8;
+
+namespace renderengine {
+
+struct Description;
+
+namespace gl {
+
class Formatter;
class Program;
-class String8;
/*
* This class generates GLSL programs suitable to handle a given
@@ -72,31 +79,36 @@
TEXTURE_EXT = 1 << TEXTURE_SHIFT,
TEXTURE_2D = 2 << TEXTURE_SHIFT,
- INPUT_TRANSFORM_MATRIX_SHIFT = 5,
+ ROUNDED_CORNERS_SHIFT = 5,
+ ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
+ ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
+ ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
+
+ INPUT_TRANSFORM_MATRIX_SHIFT = 6,
INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
- OUTPUT_TRANSFORM_MATRIX_SHIFT = 6,
+ OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
- INPUT_TF_SHIFT = 7,
+ INPUT_TF_SHIFT = 8,
INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
- OUTPUT_TF_SHIFT = 9,
+ OUTPUT_TF_SHIFT = 10,
OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
- Y410_BT2020_SHIFT = 11,
+ Y410_BT2020_SHIFT = 12,
Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
@@ -115,6 +127,9 @@
inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
+ inline bool hasRoundedCorners() const {
+ return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
+ }
inline bool hasInputTransformMatrix() const {
return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
}
@@ -151,21 +166,26 @@
}
inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
- // this is the definition of a friend function -- not a method of class Needs
- friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
- return (lhs.mKey < rhs.mKey) ? 1 : 0;
- }
+ // for use by std::unordered_map
+
+ bool operator==(const Key& other) const { return mKey == other.mKey; }
+
+ struct Hash {
+ size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); }
+ };
};
- ProgramCache();
- ~ProgramCache();
+ ProgramCache() = default;
+ ~ProgramCache() = default;
// Generate shaders to populate the cache
- void primeCache(bool hasWideColor);
+ void primeCache(const EGLContext context, bool useColorManagement);
+
+ size_t getSize(const EGLContext context) { return mCaches[context].size(); }
// useProgram lookup a suitable program in the cache or generates one
// if none can be found.
- void useProgram(const Description& description);
+ void useProgram(const EGLContext context, const Description& description);
private:
// compute a cache Key from a Description
@@ -179,19 +199,23 @@
// Generate OETF based from Key.
static void generateOETF(Formatter& fs, const Key& needs);
// generates a program from the Key
- static Program* generateProgram(const Key& needs);
+ static std::unique_ptr<Program> generateProgram(const Key& needs);
// generates the vertex shader from the Key
static String8 generateVertexShader(const Key& needs);
// generates the fragment shader from the Key
static String8 generateFragmentShader(const Key& needs);
// Key/Value map used for caching Programs. Currently the cache
- // is never shrunk.
- DefaultKeyedVector<Key, Program*> mCache;
+ // is never shrunk (and the GL program objects are never deleted).
+ std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>>
+ mCaches;
};
-ANDROID_BASIC_TYPES_TRAITS(ProgramCache::Key)
+} // namespace gl
+} // namespace renderengine
-} /* namespace android */
+ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
+
+} // namespace android
#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
new file mode 100644
index 0000000..af8de23
--- /dev/null
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 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 <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+
+// DisplaySettings contains the settings that are applicable when drawing all
+// layers for a given display.
+struct DisplaySettings {
+ // Rectangle describing the physical display. We will project from the
+ // logical clip onto this rectangle.
+ Rect physicalDisplay = Rect::INVALID_RECT;
+
+ // Rectangle bounded by the x,y- clipping planes in the logical display, so
+ // that the orthographic projection matrix can be computed. When
+ // constructing this matrix, z-coordinate bound are assumed to be at z=0 and
+ // z=1.
+ Rect clip = Rect::INVALID_RECT;
+
+ // Global transform to apply to all layers.
+ mat4 globalTransform = mat4();
+
+ // Maximum luminance pulled from the display's HDR capabilities.
+ float maxLuminance = 1.0f;
+
+ // Output dataspace that will be populated if wide color gamut is used, or
+ // DataSpace::UNKNOWN otherwise.
+ ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
+
+ // Additional color transform to apply in linear space after transforming
+ // to the output dataspace.
+ mat4 colorTransform = mat4();
+
+ // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
+ // RenderEngine will transform the clearRegion passed in here, by
+ // globalTransform, so that it will be in the same coordinate space as the
+ // rendered layers.
+ Region clearRegion = Region::INVALID_REGION;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/Framebuffer.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/Framebuffer.h
index e6ac6bf..66eb9ef 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,21 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <cstdint>
+
+struct ANativeWindowBuffer;
namespace android {
-namespace mock {
+namespace renderengine {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Framebuffer {
+public:
+ virtual ~Framebuffer() = default;
-} // namespace mock
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) = 0;
+};
+
+} // namespace renderengine
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/Image.h
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/Image.h
index e6ac6bf..3bb4731 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/Image.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,18 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+struct ANativeWindowBuffer;
namespace android {
-namespace mock {
+namespace renderengine {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Image {
+public:
+ virtual ~Image() = default;
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
+};
-} // namespace mock
+} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
new file mode 100644
index 0000000..b8bf801
--- /dev/null
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2018 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 <math/mat4.h>
+#include <math/vec3.h>
+#include <renderengine/Texture.h>
+#include <ui/Fence.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace renderengine {
+
+// Metadata describing the input buffer to render from.
+struct Buffer {
+ // Buffer containing the image that we will render.
+ // If buffer == nullptr, then the rest of the fields in this struct will be
+ // ignored.
+ sp<GraphicBuffer> buffer = nullptr;
+
+ // Fence that will fire when the buffer is ready to be bound.
+ sp<Fence> fence = nullptr;
+
+ // Texture identifier to bind the external texture to.
+ // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
+ uint32_t textureName = 0;
+
+ // Whether to use filtering when rendering the texture.
+ bool useTextureFiltering = false;
+
+ // Transform matrix to apply to texture coordinates.
+ mat4 textureTransform = mat4();
+
+ // Wheteher to use pre-multiplied alpha
+ bool usePremultipliedAlpha = true;
+
+ // Override flag that alpha for each pixel in the buffer *must* be 1.0.
+ // LayerSettings::alpha is still used if isOpaque==true - this flag only
+ // overrides the alpha channel of the buffer.
+ bool isOpaque = false;
+
+ // HDR color-space setting for Y410.
+ bool isY410BT2020 = false;
+};
+
+// Metadata describing the layer geometry.
+struct Geometry {
+ // Boundaries of the layer.
+ FloatRect boundaries = FloatRect();
+
+ // Transform matrix to apply to mesh coordinates.
+ mat4 positionTransform = mat4();
+
+ // Radius of rounded corners, if greater than 0. Otherwise, this layer's
+ // corners are not rounded.
+ // Having corner radius will force GPU composition on the layer and its children, drawing it
+ // with a special shader. The shader will receive the radius and the crop rectangle as input,
+ // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+ // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+ // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+ // in local layer coordinate space, so we have to take the layer transform into account when
+ // walking up the tree.
+ float roundedCornersRadius = 0.0;
+
+ // Rectangle within which corners will be rounded.
+ FloatRect roundedCornersCrop = FloatRect();
+};
+
+// Descriptor of the source pixels for this layer.
+struct PixelSource {
+ // Source buffer
+ Buffer buffer = Buffer();
+
+ // The solid color with which to fill the layer.
+ // This should only be populated if we don't render from an application
+ // buffer.
+ half3 solidColor = half3(0.0f, 0.0f, 0.0f);
+};
+
+// The settings that RenderEngine requires for correctly rendering a Layer.
+struct LayerSettings {
+ // Geometry information
+ Geometry geometry = Geometry();
+
+ // Source pixels for this layer.
+ PixelSource source = PixelSource();
+
+ // Alpha option to blend with the source pixels
+ half alpha = half(0.0);
+
+ // Color space describing how the source pixels should be interpreted.
+ ui::Dataspace sourceDataspace = ui::Dataspace::UNKNOWN;
+
+ // Additional layer-specific color transform to be applied before the global
+ // transform.
+ mat4 colorTransform = mat4();
+
+ // True if blending will be forced to be disabled.
+ bool disableBlending = false;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
similarity index 84%
rename from services/surfaceflinger/RenderEngine/Mesh.h
rename to libs/renderengine/include/renderengine/Mesh.h
index d0a9ac0..7618424 100644
--- a/services/surfaceflinger/RenderEngine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -17,9 +17,12 @@
#ifndef SF_RENDER_ENGINE_MESH_H
#define SF_RENDER_ENGINE_MESH_H
+#include <vector>
+
#include <stdint.h>
namespace android {
+namespace renderengine {
class Mesh {
public:
@@ -30,7 +33,7 @@
};
Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
- ~Mesh();
+ ~Mesh() = default;
/*
* VertexArray handles the stride automatically.
@@ -59,14 +62,22 @@
return VertexArray<TYPE>(getTexCoords(), mStride);
}
+ template <typename TYPE>
+ VertexArray<TYPE> getCropCoordArray() {
+ return VertexArray<TYPE>(getCropCoords(), mStride);
+ }
+
Primitive getPrimitive() const;
// returns a pointer to the vertices positions
float const* getPositions() const;
- // returns a pointer to the vertices texture coordinates
+ // returns a pointer to the vertices texture coordinates
float const* getTexCoords() const;
+ // returns a pointer to the vertices crop coordinates
+ float const* getCropCoords() const;
+
// number of vertices in this mesh
size_t getVertexCount() const;
@@ -89,7 +100,9 @@
float* getPositions();
float* getTexCoords();
- float* mVertices;
+ float* getCropCoords();
+
+ std::vector<float> mVertices;
size_t mVertexCount;
size_t mVertexSize;
size_t mTexCoordsSize;
@@ -97,5 +110,6 @@
Primitive mPrimitive;
};
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
#endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
new file mode 100644
index 0000000..ab34274
--- /dev/null
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright 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 SF_RENDERENGINE_H_
+#define SF_RENDERENGINE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <math/mat4.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/Framebuffer.h>
+#include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+/**
+ * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported).
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
+
+struct ANativeWindowBuffer;
+
+namespace android {
+
+class Rect;
+class Region;
+
+namespace renderengine {
+
+class BindNativeBufferAsFramebuffer;
+class Image;
+class Mesh;
+class Texture;
+
+namespace impl {
+class RenderEngine;
+}
+
+enum class Protection {
+ UNPROTECTED = 1,
+ PROTECTED = 2,
+};
+
+class RenderEngine {
+public:
+ enum FeatureFlag {
+ USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color
+ USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context
+ };
+
+ static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags,
+ uint32_t imageCacheSize);
+
+ virtual ~RenderEngine() = 0;
+
+ // ----- BEGIN DEPRECATED INTERFACE -----
+ // This interface, while still in use until a suitable replacement is built,
+ // should be considered deprecated, minus some methods which still may be
+ // used to support legacy behavior.
+
+ virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
+ virtual std::unique_ptr<Image> createImage() = 0;
+
+ virtual void primeCache() const = 0;
+
+ // dump the extension strings. always call the base class.
+ virtual void dump(std::string& result) = 0;
+
+ virtual bool useNativeFenceSync() const = 0;
+ virtual bool useWaitSync() const = 0;
+
+ virtual bool isCurrent() const = 0;
+
+ // helpers
+ // flush submits RenderEngine command stream for execution and returns a
+ // native fence fd that is signaled when the execution has completed. It
+ // returns -1 on errors.
+ virtual base::unique_fd flush() = 0;
+ // finish waits until RenderEngine command stream has been executed. It
+ // returns false on errors.
+ virtual bool finish() = 0;
+ // waitFence inserts a wait on an external fence fd to RenderEngine
+ // command stream. It returns false on errors.
+ virtual bool waitFence(base::unique_fd fenceFd) = 0;
+
+ virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
+ virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
+ float alpha) = 0;
+
+ virtual void setScissor(const Rect& region) = 0;
+ virtual void disableScissor() = 0;
+ virtual void genTextures(size_t count, uint32_t* names) = 0;
+ virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
+ virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
+ virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
+ sp<Fence> fence) = 0;
+ // Removes internal resources referenced by the bufferId. This method should be
+ // invoked when the caller will no longer hold a reference to a GraphicBuffer
+ // and needs to clean up its resources.
+ virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
+ // When binding a native buffer, it must be done before setViewportAndProjection
+ // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
+ virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
+ virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+ // set-up
+ virtual void checkErrors() const = 0;
+ virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ ui::Transform::orientation_flags rotation) = 0;
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color, float cornerRadius) = 0;
+ virtual void setupLayerTexturing(const Texture& texture) = 0;
+ virtual void setupLayerBlackedOut() = 0;
+ virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
+ // Sets up the crop size for corner radius clipping.
+ //
+ // Having corner radius will force GPU composition on the layer and its children, drawing it
+ // with a special shader. The shader will receive the radius and the crop rectangle as input,
+ // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+ // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+ // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+ // in local layer coordinate space, so we have to take the layer transform into account when
+ // walking up the tree.
+ virtual void setupCornerRadiusCropSize(float width, float height) = 0;
+
+ // Set a color transform matrix that is applied in linear space right before OETF.
+ virtual void setColorTransform(const mat4& /* colorTransform */) = 0;
+ virtual void disableTexturing() = 0;
+ virtual void disableBlending() = 0;
+
+ // HDR and color management support
+ virtual void setSourceY410BT2020(bool enable) = 0;
+ virtual void setSourceDataSpace(ui::Dataspace source) = 0;
+ virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0;
+ virtual void setDisplayMaxLuminance(const float maxLuminance) = 0;
+
+ // drawing
+ virtual void drawMesh(const Mesh& mesh) = 0;
+
+ // queries
+ virtual size_t getMaxTextureSize() const = 0;
+ virtual size_t getMaxViewportDims() const = 0;
+
+ // ----- END DEPRECATED INTERFACE -----
+
+ // ----- BEGIN NEW INTERFACE -----
+
+ virtual bool isProtected() const = 0;
+ virtual bool supportsProtectedContent() const = 0;
+ virtual bool useProtectedContext(bool useProtectedContext) = 0;
+
+ // Renders layers for a particular display via GPU composition. This method
+ // should be called for every display that needs to be rendered via the GPU.
+ // @param display The display-wide settings that should be applied prior to
+ // drawing any layers.
+ // @param layers The layers to draw onto the display, in Z-order.
+ // @param buffer The buffer which will be drawn to. This buffer will be
+ // ready once drawFence fires.
+ // @param bufferFence Fence signalling that the buffer is ready to be drawn
+ // to.
+ // @param drawFence A pointer to a fence, which will fire when the buffer
+ // has been drawn to and is ready to be examined. The fence will be
+ // initialized by this method. The caller will be responsible for owning the
+ // fence.
+ // @return An error code indicating whether drawing was successful. For
+ // now, this always returns NO_ERROR.
+ virtual status_t drawLayers(const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) = 0;
+
+protected:
+ // Gets a framebuffer to render to. This framebuffer may or may not be
+ // cached depending on the implementation.
+ //
+ // Note that this method does not transfer ownership, so the caller most not
+ // live longer than RenderEngine.
+ virtual Framebuffer* getFramebufferForDrawing() = 0;
+ friend class BindNativeBufferAsFramebuffer;
+};
+
+class BindNativeBufferAsFramebuffer {
+public:
+ BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
+ : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected())
+ ? mEngine.bindFrameBuffer(mFramebuffer)
+ : NO_MEMORY;
+ }
+ ~BindNativeBufferAsFramebuffer() {
+ mFramebuffer->setNativeWindowBuffer(nullptr, false);
+ mEngine.unbindFrameBuffer(mFramebuffer);
+ }
+ status_t getStatus() const { return mStatus; }
+
+private:
+ RenderEngine& mEngine;
+ Framebuffer* mFramebuffer;
+ status_t mStatus;
+};
+
+namespace impl {
+
+// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
+class RenderEngine : public renderengine::RenderEngine {
+public:
+ virtual ~RenderEngine() = 0;
+
+ bool useNativeFenceSync() const override;
+ bool useWaitSync() const override;
+
+protected:
+ RenderEngine(uint32_t featureFlags);
+ const uint32_t mFeatureFlags;
+};
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/Texture.h b/libs/renderengine/include/renderengine/Texture.h
similarity index 94%
rename from services/surfaceflinger/RenderEngine/Texture.h
rename to libs/renderengine/include/renderengine/Texture.h
index 56b6b31..c69ace0 100644
--- a/services/surfaceflinger/RenderEngine/Texture.h
+++ b/libs/renderengine/include/renderengine/Texture.h
@@ -14,22 +14,17 @@
* limitations under the License.
*/
-#include <math/mat4.h>
-#include <stdint.h>
-
#ifndef SF_RENDER_ENGINE_TEXTURE_H
#define SF_RENDER_ENGINE_TEXTURE_H
+#include <stdint.h>
+
+#include <math/mat4.h>
+
namespace android {
+namespace renderengine {
class Texture {
- uint32_t mTextureName;
- uint32_t mTextureTarget;
- size_t mWidth;
- size_t mHeight;
- bool mFiltering;
- mat4 mTextureMatrix;
-
public:
enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 };
@@ -50,7 +45,16 @@
bool getFiltering() const;
size_t getWidth() const;
size_t getHeight() const;
+
+private:
+ uint32_t mTextureName;
+ uint32_t mTextureTarget;
+ size_t mWidth;
+ size_t mHeight;
+ bool mFiltering;
+ mat4 mTextureMatrix;
};
-} /* namespace android */
+} // namespace renderengine
+} // namespace android
#endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/mock/Framebuffer.h
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/mock/Framebuffer.h
index e6ac6bf..7695885 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/mock/Framebuffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,23 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/Framebuffer.h>
namespace android {
+namespace renderengine {
namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Framebuffer : public renderengine::Framebuffer {
+public:
+ Framebuffer();
+ ~Framebuffer() override;
+
+ MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool));
+};
} // namespace mock
+} // namespace renderengine
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/include/renderengine/mock/Image.h
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/mock/Image.h
index e6ac6bf..2b0eed1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/include/renderengine/mock/Image.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,23 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include <gmock/gmock.h>
+#include <renderengine/Image.h>
namespace android {
+namespace renderengine {
namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class Image : public renderengine::Image {
+public:
+ Image();
+ ~Image() override;
+
+ MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected));
+};
} // namespace mock
+} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
new file mode 100644
index 0000000..ddf7420
--- /dev/null
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 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 <gmock/gmock.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/Texture.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class RenderEngine : public renderengine::RenderEngine {
+public:
+ RenderEngine();
+ ~RenderEngine() override;
+
+ MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>());
+ MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
+ MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
+ MOCK_CONST_METHOD0(primeCache, void());
+ MOCK_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD0(useNativeFenceSync, bool());
+ MOCK_CONST_METHOD0(useWaitSync, bool());
+ MOCK_CONST_METHOD0(isCurrent, bool());
+ MOCK_METHOD0(flush, base::unique_fd());
+ MOCK_METHOD0(finish, bool());
+ MOCK_METHOD1(waitFence, bool(base::unique_fd*));
+ bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
+ MOCK_METHOD4(clearWithColor, void(float, float, float, float));
+ MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float));
+ MOCK_METHOD1(setScissor, void(const Rect&));
+ MOCK_METHOD0(disableScissor, void());
+ MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
+ MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
+ MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
+ MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>));
+ MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
+ MOCK_CONST_METHOD0(checkErrors, void());
+ MOCK_METHOD4(setViewportAndProjection,
+ void(size_t, size_t, Rect, ui::Transform::orientation_flags));
+ MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float));
+ MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
+ MOCK_METHOD0(setupLayerBlackedOut, void());
+ MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
+ MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float));
+ MOCK_METHOD1(setColorTransform, void(const mat4&));
+ MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
+ MOCK_METHOD0(disableTexturing, void());
+ MOCK_METHOD0(disableBlending, void());
+ MOCK_METHOD1(setSourceY410BT2020, void(bool));
+ MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace));
+ MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace));
+ MOCK_METHOD1(setDisplayMaxLuminance, void(const float));
+ MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
+ MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
+ MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
+ MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
+ MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
+ MOCK_CONST_METHOD0(isProtected, bool());
+ MOCK_CONST_METHOD0(supportsProtectedContent, bool());
+ MOCK_METHOD1(useProtectedContext, bool(bool));
+ MOCK_METHOD5(drawLayers,
+ status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+ ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*));
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
new file mode 100644
index 0000000..bd2055f
--- /dev/null
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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 SF_RENDER_ENGINE_DESCRIPTION_H_
+#define SF_RENDER_ENGINE_DESCRIPTION_H_
+
+#include <renderengine/Texture.h>
+#include <ui/GraphicTypes.h>
+
+namespace android {
+namespace renderengine {
+
+/*
+ * This is the structure that holds the state of the rendering engine.
+ * This class is used to generate a corresponding GLSL program and set the
+ * appropriate uniform.
+ */
+struct Description {
+ enum class TransferFunction : int {
+ LINEAR,
+ SRGB,
+ ST2084,
+ HLG, // Hybrid Log-Gamma for HDR.
+ };
+
+ static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace);
+
+ Description() = default;
+ ~Description() = default;
+
+ bool hasInputTransformMatrix() const;
+ bool hasOutputTransformMatrix() const;
+ bool hasColorMatrix() const;
+
+ // whether textures are premultiplied
+ bool isPremultipliedAlpha = false;
+ // whether this layer is marked as opaque
+ bool isOpaque = true;
+
+ // corner radius of the layer
+ float cornerRadius = 0;
+
+ // Size of the rounded rectangle we are cropping to
+ half2 cropSize;
+
+ // Texture this layer uses
+ Texture texture;
+ bool textureEnabled = false;
+
+ // color used when texturing is disabled or when setting alpha.
+ half4 color;
+
+ // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
+ bool isY410BT2020 = false;
+
+ // transfer functions for the input/output
+ TransferFunction inputTransferFunction = TransferFunction::LINEAR;
+ TransferFunction outputTransferFunction = TransferFunction::LINEAR;
+
+ float displayMaxLuminance;
+
+ // projection matrix
+ mat4 projectionMatrix;
+
+ // The color matrix will be applied in linear space right before OETF.
+ mat4 colorMatrix;
+ mat4 inputTransformMatrix;
+ mat4 outputTransformMatrix;
+};
+
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/renderengine/mock/Framebuffer.cpp
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/mock/Framebuffer.cpp
index e6ac6bf..fbdcaab 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/renderengine/mock/Framebuffer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <renderengine/mock/Framebuffer.h>
namespace android {
+namespace renderengine {
namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Framebuffer::Framebuffer() = default;
+Framebuffer::~Framebuffer() = default;
} // namespace mock
+} // namespace renderengine
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/libs/renderengine/mock/Image.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
copy to libs/renderengine/mock/Image.cpp
index a98bece..57f4346 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/libs/renderengine/mock/Image.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,22 +14,17 @@
* limitations under the License.
*/
-#include "mock/RenderEngine/MockRenderEngine.h"
+#include <renderengine/mock/Image.h>
namespace android {
-namespace RE {
+namespace renderengine {
namespace mock {
-// Explicit default instantiation is recommended.
-RenderEngine::RenderEngine() = default;
-RenderEngine::~RenderEngine() = default;
-
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
Image::Image() = default;
Image::~Image() = default;
} // namespace mock
-} // namespace RE
+} // namespace renderengine
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/libs/renderengine/mock/RenderEngine.cpp
similarity index 70%
rename from services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
rename to libs/renderengine/mock/RenderEngine.cpp
index a98bece..261636d 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/libs/renderengine/mock/RenderEngine.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,22 +14,17 @@
* limitations under the License.
*/
-#include "mock/RenderEngine/MockRenderEngine.h"
+#include <renderengine/mock/RenderEngine.h>
namespace android {
-namespace RE {
+namespace renderengine {
namespace mock {
-// Explicit default instantiation is recommended.
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
RenderEngine::RenderEngine() = default;
RenderEngine::~RenderEngine() = default;
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
-Image::Image() = default;
-Image::~Image() = default;
-
} // namespace mock
-} // namespace RE
+} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
new file mode 100644
index 0000000..9b483ef
--- /dev/null
+++ b/libs/renderengine/tests/Android.bp
@@ -0,0 +1,38 @@
+// Copyright 2018 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: "librenderengine_test",
+ defaults: ["surfaceflinger_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "RenderEngineTest.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "librenderengine",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
new file mode 100644
index 0000000..8c93cf4
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -0,0 +1,1006 @@
+/*
+ * Copyright 2018 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 <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <ui/PixelFormat.h>
+#include "../gl/GLESRenderEngine.h"
+
+constexpr int DEFAULT_DISPLAY_WIDTH = 128;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
+constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+
+namespace android {
+
+struct RenderEngineTest : public ::testing::Test {
+ static void SetUpTestSuite() {
+ sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
+ ui::PixelFormat::RGBA_8888),
+ 0, 1);
+ }
+
+ static void TearDownTestSuite() {
+ // The ordering here is important - sCurrentBuffer must live longer
+ // than RenderEngine to avoid a null reference on tear-down.
+ sRE = nullptr;
+ sCurrentBuffer = nullptr;
+ }
+
+ static sp<GraphicBuffer> allocateDefaultBuffer() {
+ return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER,
+ "output");
+ }
+
+ // Allocates a 1x1 buffer to fill with a solid color
+ static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
+ return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "input");
+ }
+
+ RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
+
+ ~RenderEngineTest() {
+ for (uint32_t texName : mTexNames) {
+ sRE->deleteTextures(1, &texName);
+ }
+ }
+
+ void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ uint8_t* pixels;
+ mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
+ uint8_t tmp = a >= b ? a - b : b - a;
+ return tmp <= tolerance;
+ };
+ int32_t maxFails = 10;
+ int32_t fails = 0;
+ for (int32_t j = 0; j < region.getHeight(); j++) {
+ const uint8_t* src =
+ pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
+ for (int32_t i = 0; i < region.getWidth(); i++) {
+ const uint8_t expected[4] = {r, g, b, a};
+ bool equal = std::equal(src, src + 4, expected, colorCompare);
+ EXPECT_TRUE(equal)
+ << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
+ << "expected (" << static_cast<uint32_t>(r) << ", "
+ << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
+ << static_cast<uint32_t>(a) << "), "
+ << "got (" << static_cast<uint32_t>(src[0]) << ", "
+ << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
+ << ", " << static_cast<uint32_t>(src[3]) << ")";
+ src += 4;
+ if (!equal && ++fails >= maxFails) {
+ break;
+ }
+ }
+ if (fails >= maxFails) {
+ break;
+ }
+ }
+ mBuffer->unlock();
+ }
+
+ static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
+
+ static Rect offsetRect() {
+ return Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT);
+ }
+
+ static Rect offsetRectAtZero() {
+ return Rect(DEFAULT_DISPLAY_WIDTH - DEFAULT_DISPLAY_OFFSET,
+ DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
+ }
+
+ void invokeDraw(renderengine::DisplaySettings settings,
+ std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
+ base::unique_fd fence;
+ status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
+ base::unique_fd(), &fence);
+ sCurrentBuffer = buffer;
+
+ int fd = fence.release();
+ if (fd >= 0) {
+ sync_wait(fd, -1);
+ close(fd);
+ }
+
+ ASSERT_EQ(NO_ERROR, status);
+ if (layers.size() > 0) {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
+ }
+
+ void drawEmptyLayers() {
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ // Meaningless buffer since we don't do any drawing
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ invokeDraw(settings, layers, buffer);
+ }
+
+ template <typename SourceVariant>
+ void fillBuffer(half r, half g, half b, half a);
+
+ template <typename SourceVariant>
+ void fillRedBuffer();
+
+ template <typename SourceVariant>
+ void fillGreenBuffer();
+
+ template <typename SourceVariant>
+ void fillBlueBuffer();
+
+ template <typename SourceVariant>
+ void fillRedTransparentBuffer();
+
+ template <typename SourceVariant>
+ void fillRedOffsetBuffer();
+
+ template <typename SourceVariant>
+ void fillBufferPhysicalOffset();
+
+ template <typename SourceVariant>
+ void fillBufferCheckers(mat4 transform);
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate0();
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate90();
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate180();
+
+ template <typename SourceVariant>
+ void fillBufferCheckersRotate270();
+
+ template <typename SourceVariant>
+ void fillBufferWithLayerTransform();
+
+ template <typename SourceVariant>
+ void fillBufferLayerTransform();
+
+ template <typename SourceVariant>
+ void fillBufferWithColorTransform();
+
+ template <typename SourceVariant>
+ void fillBufferColorTransform();
+
+ template <typename SourceVariant>
+ void fillRedBufferWithRoundedCorners();
+
+ template <typename SourceVariant>
+ void fillBufferWithRoundedCorners();
+
+ template <typename SourceVariant>
+ void overlayCorners();
+
+ void fillRedBufferTextureTransform();
+
+ void fillBufferTextureTransform();
+
+ void fillRedBufferWithPremultiplyAlpha();
+
+ void fillBufferWithPremultiplyAlpha();
+
+ void fillRedBufferWithoutPremultiplyAlpha();
+
+ void fillBufferWithoutPremultiplyAlpha();
+
+ void fillGreenColorBufferThenClearRegion();
+
+ void clearLeftRegion();
+
+ void clearRegion();
+
+ // Keep around the same renderengine object to save on initialization time.
+ // For now, exercise the GL backend directly so that some caching specifics
+ // can be tested without changing the interface.
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
+ // be freed *after* RenderEngine is destroyed, so that the EGL image is
+ // destroyed first.
+ static sp<GraphicBuffer> sCurrentBuffer;
+
+ sp<GraphicBuffer> mBuffer;
+
+ std::vector<uint32_t> mTexNames;
+};
+
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
+
+struct ColorSourceVariant {
+ static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
+ RenderEngineTest* /*fixture*/) {
+ layer.source.solidColor = half3(r, g, b);
+ }
+};
+
+struct RelaxOpaqueBufferVariant {
+ static void setOpaqueBit(renderengine::LayerSettings& layer) {
+ layer.source.buffer.isOpaque = false;
+ }
+
+ static uint8_t getAlphaChannel() { return 255; }
+};
+
+struct ForceOpaqueBufferVariant {
+ static void setOpaqueBit(renderengine::LayerSettings& layer) {
+ layer.source.buffer.isOpaque = true;
+ }
+
+ static uint8_t getAlphaChannel() {
+ // The isOpaque bit will override the alpha channel, so this should be
+ // arbitrary.
+ return 10;
+ }
+};
+
+template <typename OpaquenessVariant>
+struct BufferSourceVariant {
+ static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
+ RenderEngineTest* fixture) {
+ sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ fixture->sRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ for (int32_t j = 0; j < buf->getHeight(); j++) {
+ uint8_t* iter = pixels + (buf->getStride() * j) * 4;
+ for (int32_t i = 0; i < buf->getWidth(); i++) {
+ iter[0] = uint8_t(r * 255);
+ iter[1] = uint8_t(g * 255);
+ iter[2] = uint8_t(b * 255);
+ iter[3] = OpaquenessVariant::getAlphaChannel();
+ iter += 4;
+ }
+ }
+
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ OpaquenessVariant::setOpaqueBit(layer);
+ }
+};
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBuffer(half r, half g, half b, half a) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(layer, r, g, b, this);
+ layer.alpha = a;
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedBuffer() {
+ fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, 1.0f);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillGreenBuffer() {
+ fillBuffer<SourceVariant>(0.0f, 1.0f, 0.0f, 1.0f);
+ expectBufferColor(fullscreenRect(), 0, 255, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBlueBuffer() {
+ fillBuffer<SourceVariant>(0.0f, 0.0f, 1.0f, 1.0f);
+ expectBufferColor(fullscreenRect(), 0, 0, 255, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedTransparentBuffer() {
+ fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, .2f);
+ expectBufferColor(fullscreenRect(), 51, 0, 0, 51);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedOffsetBuffer() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = offsetRect();
+ settings.clip = offsetRectAtZero();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = offsetRectAtZero().toFloatRect();
+ SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0f;
+
+ layers.push_back(layer);
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferPhysicalOffset() {
+ fillRedOffsetBuffer<SourceVariant>();
+
+ expectBufferColor(Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+ Rect offsetRegionLeft(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT);
+ Rect offsetRegionTop(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_OFFSET);
+
+ expectBufferColor(offsetRegionLeft, 0, 0, 0, 0);
+ expectBufferColor(offsetRegionTop, 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckers(mat4 transform) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 2x2
+ settings.clip = Rect(2, 2);
+ settings.globalTransform = transform;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layerOne;
+ Rect rectOne(0, 0, 1, 1);
+ layerOne.geometry.boundaries = rectOne.toFloatRect();
+ SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
+ layerOne.alpha = 1.0f;
+
+ renderengine::LayerSettings layerTwo;
+ Rect rectTwo(0, 1, 1, 2);
+ layerTwo.geometry.boundaries = rectTwo.toFloatRect();
+ SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
+ layerTwo.alpha = 1.0f;
+
+ renderengine::LayerSettings layerThree;
+ Rect rectThree(1, 0, 2, 1);
+ layerThree.geometry.boundaries = rectThree.toFloatRect();
+ SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
+ layerThree.alpha = 1.0f;
+
+ layers.push_back(layerOne);
+ layers.push_back(layerTwo);
+ layers.push_back(layerThree);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate0() {
+ fillBufferCheckers<SourceVariant>(mat4());
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0,
+ 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 0, 0, 255, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate90() {
+ mat4 matrix = mat4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1);
+ fillBufferCheckers<SourceVariant>(matrix);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0,
+ 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 255, 0, 0, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 255, 255);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate180() {
+ mat4 matrix = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1);
+ fillBufferCheckers<SourceVariant>(matrix);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0,
+ 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 0, 255, 0, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 255, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferCheckersRotate270() {
+ mat4 matrix = mat4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1);
+ fillBufferCheckers<SourceVariant>(matrix);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255,
+ 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT / 2),
+ 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2,
+ DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithLayerTransform() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 2x2
+ settings.clip = Rect(2, 2);
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ // Translate one pixel diagonally
+ layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1);
+ SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ layer.alpha = 1.0f;
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferLayerTransform() {
+ fillBufferWithLayerTransform<SourceVariant>();
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0);
+ expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 255, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransform() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransform() {
+ fillBufferWithColorTransform<SourceVariant>();
+ expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillRedBufferWithRoundedCorners() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ layer.geometry.roundedCornersRadius = 5.0f;
+ layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0f;
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithRoundedCorners() {
+ fillRedBufferWithRoundedCorners<SourceVariant>();
+ // Corners should be ignored...
+ expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0);
+ expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+ // ...And the non-rounded portion should be red.
+ // Other pixels may be anti-aliased, so let's not check those.
+ expectBufferColor(Rect(5, 5, DEFAULT_DISPLAY_WIDTH - 5, DEFAULT_DISPLAY_HEIGHT - 5), 255, 0, 0,
+ 255);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::overlayCorners() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layersFirst;
+
+ renderengine::LayerSettings layerOne;
+ layerOne.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0);
+ SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
+ layerOne.alpha = 0.2;
+
+ layersFirst.push_back(layerOne);
+ invokeDraw(settings, layersFirst, mBuffer);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+
+ std::vector<renderengine::LayerSettings> layersSecond;
+ renderengine::LayerSettings layerTwo;
+ layerTwo.geometry.boundaries =
+ FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
+ layerTwo.alpha = 1.0f;
+
+ layersSecond.push_back(layerTwo);
+ invokeDraw(settings, layersSecond, mBuffer);
+
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
+ DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 0, 255, 0, 255);
+}
+
+void RenderEngineTest::fillRedBufferTextureTransform() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ // Here will allocate a checker board texture, but transform texture
+ // coordinates so that only the upper left is applied.
+ sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
+ uint32_t texName;
+ RenderEngineTest::sRE->genTextures(1, &texName);
+ this->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ // Red top left, Green top right, Blue bottom left, Black bottom right
+ pixels[0] = 255;
+ pixels[1] = 0;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ pixels[4] = 0;
+ pixels[5] = 255;
+ pixels[6] = 0;
+ pixels[7] = 255;
+ pixels[8] = 0;
+ pixels[9] = 0;
+ pixels[10] = 255;
+ pixels[11] = 255;
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ // Transform coordinates to only be inside the red quadrant.
+ layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1));
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferTextureTransform() {
+ fillRedBufferTextureTransform();
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 1x1
+ settings.clip = Rect(1, 1);
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ RenderEngineTest::sRE->genTextures(1, &texName);
+ this->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 255;
+ pixels[1] = 0;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ layer.source.buffer.usePremultipliedAlpha = true;
+ layer.alpha = 0.5f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
+ fillRedBufferWithPremultiplyAlpha();
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 128);
+}
+
+void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 1x1
+ settings.clip = Rect(1, 1);
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ RenderEngineTest::sRE->genTextures(1, &texName);
+ this->mTexNames.push_back(texName);
+
+ uint8_t* pixels;
+ buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 255;
+ pixels[1] = 0;
+ pixels[2] = 0;
+ pixels[3] = 255;
+ buf->unlock();
+
+ layer.source.buffer.buffer = buf;
+ layer.source.buffer.textureName = texName;
+ layer.source.buffer.usePremultipliedAlpha = false;
+ layer.alpha = 0.5f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
+ fillRedBufferWithoutPremultiplyAlpha();
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+}
+
+void RenderEngineTest::clearLeftRegion() {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ // Here logical space is 4x4
+ settings.clip = Rect(4, 4);
+ settings.globalTransform = mat4::scale(vec4(2, 4, 0, 1));
+ settings.clearRegion = Region(Rect(1, 1));
+ std::vector<renderengine::LayerSettings> layers;
+ // dummy layer, without bounds should not render anything
+ renderengine::LayerSettings layer;
+ layers.push_back(layer);
+ invokeDraw(settings, layers, mBuffer);
+}
+
+void RenderEngineTest::clearRegion() {
+ // Reuse mBuffer
+ clearLeftRegion();
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
+ expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT),
+ 0, 0, 0, 0);
+}
+
+TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
+ drawEmptyLayers();
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layers.push_back(layer);
+ base::unique_fd fence;
+ status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence);
+
+ ASSERT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+ layer.alpha = 1.0;
+ layers.push_back(layer);
+
+ status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(),
+ base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+ fillRedBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+ fillGreenBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+ fillBlueBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+ fillRedTransparentBuffer<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+ fillBufferPhysicalOffset<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+ fillBufferCheckersRotate0<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+ fillBufferCheckersRotate90<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+ fillBufferCheckersRotate180<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+ fillBufferCheckersRotate270<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+ fillBufferLayerTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+ fillBufferLayerTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+ fillBufferWithRoundedCorners<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+ overlayCorners<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+ fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+ fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+ fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+ fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+ fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+ fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+ fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+ fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+ fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+ fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+ overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+ fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+ fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+ fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+ fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+ fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+ fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+ fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+ fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+ fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+ fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+ fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+ overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+ fillBufferTextureTransform();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+ fillBufferWithPremultiplyAlpha();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+ fillBufferWithoutPremultiplyAlpha();
+}
+
+TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+ clearRegion();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.geometry.boundaries = fullscreenRect().toFloatRect();
+ BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+
+ layers.push_back(layer);
+ invokeDraw(settings, layers, mBuffer);
+ uint64_t bufferId = layer.source.buffer.buffer->getId();
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ sRE->unbindExternalTextureBuffer(bufferId);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
+ status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint32_t texName;
+ sRE->genTextures(1, &texName);
+ mTexNames.push_back(texName);
+
+ sRE->bindExternalTextureBuffer(texName, buf, nullptr);
+ uint64_t bufferId = buf->getId();
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ sRE->unbindExternalTextureBuffer(bufferId);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+} // namespace android
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index a0e368c..d9a986e 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -318,7 +318,7 @@
// If the sensor is protected by a permission we need to know if it is
// a runtime one to determine whether we can use the permission cache.
sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder != 0) {
+ if (binder != nullptr) {
sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
String16(mRequiredPermission));
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 6f68fb5..4438d45 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -29,6 +29,7 @@
#include <sensor/ISensorEventConnection.h>
#include <android/sensor.h>
+#include <hardware/sensors-base.h>
using std::min;
@@ -37,7 +38,7 @@
// ----------------------------------------------------------------------------
SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
- : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
+ : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0),
mNumAcksToSend(0) {
mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
}
@@ -82,9 +83,9 @@
sp<Looper> SensorEventQueue::getLooper() const
{
Mutex::Autolock _l(mLock);
- if (mLooper == 0) {
+ if (mLooper == nullptr) {
mLooper = new Looper(true);
- mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
+ mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, nullptr, nullptr);
}
return mLooper;
}
@@ -97,7 +98,7 @@
int events;
int32_t result;
do {
- result = looper->pollOnce(-1, NULL, &events, NULL);
+ result = looper->pollOnce(-1, nullptr, &events, nullptr);
if (result == ALOOPER_POLL_ERROR) {
ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
result = -EPIPE; // unknown error, so we make up one
@@ -188,6 +189,52 @@
return;
}
+ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const {
+ // Check if this Sensor Event Queue is registered to receive each type of event. If it is not,
+ // then do not copy the event into the final buffer. Minimize the number of copy operations by
+ // finding consecutive sequences of events that the Sensor Event Queue should receive and only
+ // copying the events once an unregistered event type is reached.
+ bool intervalStartLocSet = false;
+ size_t intervalStartLoc = 0;
+ size_t eventsInInterval = 0;
+ ssize_t eventsCopied = 0;
+
+ for (size_t i = 0; i < count; i++) {
+ bool includeEvent =
+ (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo);
+
+ if (includeEvent) {
+ // Do not copy events yet since there may be more consecutive events that should be
+ // copied together. Track the start location and number of events in the current
+ // sequence.
+ if (!intervalStartLocSet) {
+ intervalStartLoc = i;
+ intervalStartLocSet = true;
+ eventsInInterval = 0;
+ }
+ eventsInInterval++;
+ }
+
+ // Shift the events from the already processed interval once an event that should not be
+ // included is reached or if this is the final event to be processed.
+ if (!includeEvent || (i + 1 == count)) {
+ // Only shift the events if the interval did not start with the first event. If the
+ // interval started with the first event, the events are already in their correct
+ // location.
+ if (intervalStartLoc > 0) {
+ memmove(&events[eventsCopied], &events[intervalStartLoc],
+ eventsInInterval * sizeof(ASensorEvent));
+ }
+ eventsCopied += eventsInInterval;
+
+ // Reset the interval information
+ eventsInInterval = 0;
+ intervalStartLocSet = false;
+ }
+ }
+ return eventsCopied;
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index b9ae524..5840d51 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -62,7 +62,7 @@
// to the wrong package and stats based on app ops may be slightly off.
if (opPackageName.size() <= 0) {
sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder != 0) {
+ if (binder != nullptr) {
const uid_t uid = IPCThreadState::self()->getCallingUid();
Vector<String16> packages;
interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
@@ -93,7 +93,7 @@
}
SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+ : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
// okay we're not locked here, but it's not needed during construction
assertStateLocked();
}
@@ -128,13 +128,13 @@
Mutex::Autolock _l(mLock);
mSensorServer.clear();
free(mSensorList);
- mSensorList = NULL;
+ mSensorList = nullptr;
mSensors.clear();
}
status_t SensorManager::assertStateLocked() {
bool initSensorManager = false;
- if (mSensorServer == NULL) {
+ if (mSensorServer == nullptr) {
initSensorManager = true;
} else {
// Ping binder to check if sensorservice is alive.
@@ -164,7 +164,7 @@
size_t count = mSensors.size();
mSensorList =
static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
- LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
+ LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
for (size_t i=0 ; i<count ; i++) {
mSensorList[i] = mSensors.array() + i;
@@ -222,7 +222,7 @@
}
}
}
- return NULL;
+ return nullptr;
}
sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
@@ -232,10 +232,10 @@
while (assertStateLocked() == NO_ERROR) {
sp<ISensorEventConnection> connection =
mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
- if (connection == NULL) {
+ if (connection == nullptr) {
// SensorService just died or the app doesn't have required permissions.
ALOGE("createEventQueue: connection is NULL.");
- return NULL;
+ return nullptr;
}
queue = new SensorEventQueue(connection);
break;
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 8176578..8c3fde0 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -34,6 +34,7 @@
// Concrete types for the NDK
struct ASensorEventQueue {
ALooper* looper;
+ bool requestAdditionalInfo;
};
// ----------------------------------------------------------------------------
@@ -92,6 +93,13 @@
void sendAck(const ASensorEvent* events, int count);
status_t injectSensorEvent(const ASensorEvent& event);
+
+ // Filters the given sensor events in place and returns the new number of events.
+ //
+ // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is
+ // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed.
+ ssize_t filterEvents(ASensorEvent* events, size_t count) const;
+
private:
sp<Looper> getLooper() const;
sp<ISensorEventConnection> mSensorEventConnection;
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 9fd84bc..c9a7668 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -21,6 +21,7 @@
srcs: [
"Sensor_test.cpp",
+ "SensorEventQueue_test.cpp",
],
shared_libs: [
diff --git a/libs/sensor/tests/SensorEventQueue_test.cpp b/libs/sensor/tests/SensorEventQueue_test.cpp
new file mode 100644
index 0000000..1eb5883
--- /dev/null
+++ b/libs/sensor/tests/SensorEventQueue_test.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 <stdint.h>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+#include <android/sensor.h>
+#include <hardware/sensors-base.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
+
+namespace android {
+
+class SensorEventQueueTest : public ::testing::Test {
+protected:
+ typedef std::vector<int32_t> Events;
+
+ SensorEventQueueTest() {};
+
+ virtual void SetUp() override {
+ SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest"));
+ mQueue = manager.createEventQueue();
+ }
+
+ void configureAdditionalInfo(bool enable) {
+ mQueue->requestAdditionalInfo = enable;
+ }
+
+ Events filterEvents(const Events &types) const {
+ // Convert the events into SensorEvent array
+ ASensorEvent* events = new ASensorEvent[types.size()];
+ for (size_t i = 0; i < types.size(); i++) {
+ events[i].type = types[i];
+ }
+
+ // Filter the events
+ ssize_t filteredCount = mQueue->filterEvents(events, types.size());
+
+ // Copy the result into an output vector
+ Events result;
+ for (size_t i = 0; i < filteredCount; i++) {
+ result.push_back(events[i].type);
+ }
+ delete[] events;
+
+ return result;
+ }
+
+ Events getExpectedEvents(const Events &events) const {
+ Events output;
+ for (size_t i = 0; i != events.size(); i++) {
+ // Copy events if the event queue is configured to receive them
+ if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) {
+ output.push_back(events[i]);
+ }
+ }
+ return output;
+ }
+
+ void runFilterTest(const Events& events) {
+ Events filtered = filterEvents(events);
+ Events expected = getExpectedEvents(events);
+ EXPECT_EQ(expected.size(), filtered.size());
+ EXPECT_EQ(expected, filtered);
+ }
+
+private:
+ sp<SensorEventQueue> mQueue;
+};
+
+TEST_F(SensorEventQueueTest, FilterZeroEvents) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) {
+ configureAdditionalInfo(true /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_MAGNETIC_FIELD,
+ SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) {
+ configureAdditionalInfo(false /* enable */);
+ runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) {
+ configureAdditionalInfo(false /* enable */);
+ const Events events = {SENSOR_TYPE_ACCELEROMETER,
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_ADDITIONAL_INFO,
+ SENSOR_TYPE_MAGNETIC_FIELD,
+ SENSOR_TYPE_ADDITIONAL_INFO};
+ runFilterTest(events);
+
+ // Update setting to request Additional Info
+ configureAdditionalInfo(true /* enable */);
+ runFilterTest(events);
+
+ // Update setting to stop requesting Additional Info
+ configureAdditionalInfo(true /* enable */);
+ runFilterTest(events);
+}
+
+} // namespace android
diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp
new file mode 100644
index 0000000..e0e3469
--- /dev/null
+++ b/libs/sensorprivacy/Android.bp
@@ -0,0 +1,47 @@
+// Copyright 2018 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: "libsensorprivacy",
+
+ aidl: {
+ export_aidl_headers: true,
+ local_include_dirs: ["aidl"],
+ },
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wzero-as-null-pointer-constant",
+ ],
+
+ srcs: [
+ "aidl/android/hardware/ISensorPrivacyListener.aidl",
+ "aidl/android/hardware/ISensorPrivacyManager.aidl",
+ "SensorPrivacyManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libhardware",
+ ],
+
+ export_include_dirs: ["include"],
+
+ export_shared_lib_headers: ["libbinder"],
+}
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
new file mode 100644
index 0000000..f973cba
--- /dev/null
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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 <mutex>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <sensorprivacy/SensorPrivacyManager.h>
+
+#include <utils/SystemClock.h>
+
+namespace android {
+
+SensorPrivacyManager::SensorPrivacyManager()
+{
+}
+
+sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService()
+{
+ std::lock_guard<Mutex> scoped_lock(mLock);
+ int64_t startTime = 0;
+ sp<hardware::ISensorPrivacyManager> service = mService;
+ while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy"));
+ if (binder == nullptr) {
+ // Wait for the sensor privacy service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGI("Waiting for sensor privacy service");
+ } else if ((uptimeMillis() - startTime) > 1000000) {
+ ALOGW("Waiting too long for sensor privacy service, giving up");
+ service = nullptr;
+ break;
+ }
+ usleep(25000);
+ } else {
+ service = interface_cast<hardware::ISensorPrivacyManager>(binder);
+ mService = service;
+ }
+ }
+ return service;
+}
+
+void SensorPrivacyManager::addSensorPrivacyListener(
+ const sp<hardware::ISensorPrivacyListener>& listener)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ service->addSensorPrivacyListener(listener);
+ }
+}
+
+void SensorPrivacyManager::removeSensorPrivacyListener(
+ const sp<hardware::ISensorPrivacyListener>& listener)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ service->removeSensorPrivacyListener(listener);
+ }
+}
+
+bool SensorPrivacyManager::isSensorPrivacyEnabled()
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ bool result;
+ service->isSensorPrivacyEnabled(&result);
+ return result;
+ }
+ // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+ return false;
+}
+
+status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ return IInterface::asBinder(service)->linkToDeath(recipient);
+ }
+ return INVALID_OPERATION;
+}
+
+status_t SensorPrivacyManager::unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient)
+{
+ sp<hardware::ISensorPrivacyManager> service = getService();
+ if (service != nullptr) {
+ return IInterface::asBinder(service)->unlinkToDeath(recipient);
+ }
+ return INVALID_OPERATION;
+}
+
+}; // namespace android
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
new file mode 100644
index 0000000..58177d8
--- /dev/null
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2018, 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.hardware;
+
+/**
+ * @hide
+ */
+oneway interface ISensorPrivacyListener {
+ void onSensorPrivacyChanged(boolean enabled);
+}
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
new file mode 100644
index 0000000..4c2d5db
--- /dev/null
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2018, 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.hardware;
+
+import android.hardware.ISensorPrivacyListener;
+
+/** @hide */
+interface ISensorPrivacyManager {
+ void addSensorPrivacyListener(in ISensorPrivacyListener listener);
+
+ void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
+
+ boolean isSensorPrivacyEnabled();
+
+ void setSensorPrivacy(boolean enable);
+}
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
new file mode 100644
index 0000000..2546a68
--- /dev/null
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_PRIVACY_MANAGER_H
+#define ANDROID_SENSOR_PRIVACY_MANAGER_H
+
+#include "android/hardware/ISensorPrivacyListener.h"
+#include "android/hardware/ISensorPrivacyManager.h"
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class SensorPrivacyManager
+{
+public:
+ SensorPrivacyManager();
+
+ void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+ void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+ bool isSensorPrivacyEnabled();
+
+ status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+ status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+
+private:
+ Mutex mLock;
+ sp<hardware::ISensorPrivacyManager> mService;
+ sp<hardware::ISensorPrivacyManager> getService();
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SENSOR_PRIVACY_MANAGER_H
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ec7f927..e521b61 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -55,11 +55,16 @@
srcs: [
"ColorSpace.cpp",
+ "BufferHubBuffer.cpp",
+ "BufferHubEventFd.cpp",
+ "BufferHubMetadata.cpp",
"DebugUtils.cpp",
"Fence.cpp",
"FenceTime.cpp",
"FrameStats.cpp",
+ "Gralloc.cpp",
"Gralloc2.cpp",
+ "Gralloc3.cpp",
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
@@ -68,6 +73,8 @@
"PublicFormat.cpp",
"Rect.cpp",
"Region.cpp",
+ "Size.cpp",
+ "Transform.cpp",
"UiConfig.cpp",
],
@@ -76,12 +83,13 @@
],
shared_libs: [
+ "android.frameworks.bufferhub@1.0",
"android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
- "android.hardware.configstore@1.0",
- "android.hardware.configstore-utils",
+ "android.hardware.graphics.mapper@3.0",
"libbase",
"libcutils",
"libhardware",
@@ -95,7 +103,7 @@
],
export_shared_lib_headers: [
- "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
],
static_libs: [
@@ -104,9 +112,32 @@
"libmath",
],
+ // bufferhub is not used when building libgui for vendors
+ target: {
+ vendor: {
+ cflags: ["-DLIBUI_IN_VNDK"],
+ exclude_srcs: [
+ "BufferHubBuffer.cpp",
+ "BufferHubEventFd.cpp",
+ "BufferHubMetadata.cpp",
+ ],
+ exclude_header_libs: [
+ "libbufferhub_headers",
+ "libdvr_headers",
+ ],
+ exclude_shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libpdx_default_transport",
+ ],
+ },
+ },
+
header_libs: [
"libbase_headers",
+ "libbufferhub_headers",
+ "libdvr_headers",
"libnativebase_headers",
+ "libnativewindow_headers",
"libhardware_headers",
"libui_headers",
"libpdx_headers",
@@ -120,6 +151,7 @@
export_header_lib_headers: [
"libbase_headers",
"libnativebase_headers",
+ "libnativewindow_headers",
"libhardware_headers",
"libui_headers",
],
@@ -131,6 +163,7 @@
vendor_available: true,
target: {
vendor: {
+ cflags: ["-DLIBUI_IN_VNDK"],
override_export_include_dirs: ["include_vndk"],
},
},
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
new file mode 100644
index 0000000..da91a97
--- /dev/null
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2018 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 <poll.h>
+
+#include <android-base/unique_fd.h>
+#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <log/log.h>
+#include <ui/BufferHubBuffer.h>
+#include <ui/BufferHubDefs.h>
+#include <utils/Trace.h>
+
+using ::android::base::unique_fd;
+using ::android::BufferHubDefs::isAnyClientAcquired;
+using ::android::BufferHubDefs::isAnyClientGained;
+using ::android::BufferHubDefs::isClientAcquired;
+using ::android::BufferHubDefs::isClientGained;
+using ::android::BufferHubDefs::isClientPosted;
+using ::android::BufferHubDefs::isClientReleased;
+using ::android::frameworks::bufferhub::V1_0::BufferHubStatus;
+using ::android::frameworks::bufferhub::V1_0::BufferTraits;
+using ::android::frameworks::bufferhub::V1_0::IBufferClient;
+using ::android::frameworks::bufferhub::V1_0::IBufferHub;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription;
+
+namespace android {
+
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::create(uint32_t width, uint32_t height,
+ uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize) {
+ auto buffer = std::unique_ptr<BufferHubBuffer>(
+ new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize));
+ return buffer->isValid() ? std::move(buffer) : nullptr;
+}
+
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const sp<NativeHandle>& token) {
+ if (token == nullptr || token.get() == nullptr) {
+ ALOGE("%s: token cannot be nullptr!", __FUNCTION__);
+ return nullptr;
+ }
+
+ auto buffer = std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(token));
+ return buffer->isValid() ? std::move(buffer) : nullptr;
+}
+
+BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount,
+ uint32_t format, uint64_t usage, size_t userMetadataSize) {
+ ATRACE_CALL();
+ ALOGD("%s: width=%u height=%u layerCount=%u, format=%u "
+ "usage=%" PRIx64 " mUserMetadataSize=%zu",
+ __FUNCTION__, width, height, layerCount, format, usage, userMetadataSize);
+
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ if (bufferhub.get() == nullptr) {
+ ALOGE("%s: BufferHub service not found!", __FUNCTION__);
+ return;
+ }
+
+ AHardwareBuffer_Desc aDesc = {width, height, layerCount, format,
+ usage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+ HardwareBufferDescription desc;
+ memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
+
+ BufferHubStatus ret;
+ sp<IBufferClient> client;
+ BufferTraits bufferTraits;
+ IBufferHub::allocateBuffer_cb allocCb = [&](const auto& status, const auto& outClient,
+ const auto& outTraits) {
+ ret = status;
+ client = std::move(outClient);
+ bufferTraits = std::move(outTraits);
+ };
+
+ if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), allocCb).isOk()) {
+ ALOGE("%s: allocateBuffer transaction failed!", __FUNCTION__);
+ return;
+ } else if (ret != BufferHubStatus::NO_ERROR) {
+ ALOGE("%s: allocateBuffer failed with error %u.", __FUNCTION__, ret);
+ return;
+ } else if (client == nullptr) {
+ ALOGE("%s: allocateBuffer got null BufferClient.", __FUNCTION__);
+ return;
+ }
+
+ const int importRet = initWithBufferTraits(bufferTraits);
+ if (importRet < 0) {
+ ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet));
+ client->close();
+ }
+ mBufferClient = std::move(client);
+}
+
+BufferHubBuffer::BufferHubBuffer(const sp<NativeHandle>& token) {
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ if (bufferhub.get() == nullptr) {
+ ALOGE("%s: BufferHub service not found!", __FUNCTION__);
+ return;
+ }
+
+ BufferHubStatus ret;
+ sp<IBufferClient> client;
+ BufferTraits bufferTraits;
+ IBufferHub::importBuffer_cb importCb = [&](const auto& status, const auto& outClient,
+ const auto& outTraits) {
+ ret = status;
+ client = std::move(outClient);
+ bufferTraits = std::move(outTraits);
+ };
+
+ // hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership
+ // transfer.
+ if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), importCb).isOk()) {
+ ALOGE("%s: importBuffer transaction failed!", __FUNCTION__);
+ return;
+ } else if (ret != BufferHubStatus::NO_ERROR) {
+ ALOGE("%s: importBuffer failed with error %u.", __FUNCTION__, ret);
+ return;
+ } else if (client == nullptr) {
+ ALOGE("%s: importBuffer got null BufferClient.", __FUNCTION__);
+ return;
+ }
+
+ const int importRet = initWithBufferTraits(bufferTraits);
+ if (importRet < 0) {
+ ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet));
+ client->close();
+ }
+ mBufferClient = std::move(client);
+}
+
+BufferHubBuffer::~BufferHubBuffer() {
+ // Close buffer client to avoid possible race condition: user could first duplicate and hold
+ // token with the original buffer gone, and then try to import the token. The close function
+ // will explicitly invalidate the token to avoid this.
+ if (mBufferClient != nullptr) {
+ if (!mBufferClient->close().isOk()) {
+ ALOGE("%s: close BufferClient transaction failed!", __FUNCTION__);
+ }
+ }
+}
+
+int BufferHubBuffer::initWithBufferTraits(const BufferTraits& bufferTraits) {
+ ATRACE_CALL();
+
+ if (bufferTraits.bufferInfo.getNativeHandle() == nullptr) {
+ ALOGE("%s: missing buffer info handle.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (bufferTraits.bufferHandle.getNativeHandle() == nullptr) {
+ ALOGE("%s: missing gralloc handle.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ // Import fds. Dup fds because hidl_handle owns the fds.
+ unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0));
+ mMetadata = BufferHubMetadata::import(std::move(ashmemFd));
+ if (!mMetadata.isValid()) {
+ ALOGE("%s: Received an invalid metadata.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0));
+ if (!mEventFd.isValid()) {
+ ALOGE("%s: Received ad invalid event fd.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ int bufferId = bufferTraits.bufferInfo->data[2];
+ if (bufferId < 0) {
+ ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ uint32_t clientBitMask;
+ memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask));
+ if (clientBitMask == 0U) {
+ ALOGE("%s: Received an invalid client state mask.", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ uint32_t userMetadataSize;
+ memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize));
+ if (mMetadata.userMetadataSize() != userMetadataSize) {
+ ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__,
+ userMetadataSize, mMetadata.userMetadataSize());
+ return -EINVAL;
+ }
+
+ size_t metadataSize = static_cast<size_t>(mMetadata.metadataSize());
+ if (metadataSize < BufferHubDefs::kMetadataHeaderSize) {
+ ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize);
+ return -EINVAL;
+ }
+
+ // Populate shortcuts to the atomics in metadata.
+ auto metadataHeader = mMetadata.metadataHeader();
+ mBufferState = &metadataHeader->bufferState;
+ mFenceState = &metadataHeader->fenceState;
+ mActiveClientsBitMask = &metadataHeader->activeClientsBitMask;
+ // The C++ standard recommends (but does not require) that lock-free atomic operations are
+ // also address-free, that is, suitable for communication between processes using shared
+ // memory.
+ LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) ||
+ !std::atomic_is_lock_free(mFenceState) ||
+ !std::atomic_is_lock_free(mActiveClientsBitMask),
+ "Atomic variables in ashmen are not lock free.");
+
+ // Import the buffer: We only need to hold on the native_handle_t here so that
+ // GraphicBuffer instance can be created in future.
+ mBufferHandle = std::move(bufferTraits.bufferHandle);
+ memcpy(&mBufferDesc, &bufferTraits.bufferDesc, sizeof(AHardwareBuffer_Desc));
+
+ mId = bufferId;
+ mClientStateMask = clientBitMask;
+
+ // TODO(b/112012161) Set up shared fences.
+ ALOGD("%s: id=%d, mBufferState=%" PRIx32 ".", __FUNCTION__, mId,
+ mBufferState->load(std::memory_order_acquire));
+ return 0;
+}
+
+int BufferHubBuffer::gain() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ if (isClientGained(currentBufferState, mClientStateMask)) {
+ ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__,
+ mClientStateMask);
+ return 0;
+ }
+ do {
+ if (isAnyClientGained(currentBufferState & (~mClientStateMask)) ||
+ isAnyClientAcquired(currentBufferState)) {
+ ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
+ __FUNCTION__, mId, mClientStateMask, currentBufferState);
+ return -EBUSY;
+ }
+ // Change the buffer state to gained state, whose value happens to be the same as
+ // mClientStateMask.
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, mClientStateMask,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
+ // TODO(b/119837586): Update fence state and return GPU fence.
+ return 0;
+}
+
+int BufferHubBuffer::post() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ uint32_t updatedBufferState = (~mClientStateMask) & BufferHubDefs::kHighBitsMask;
+ do {
+ if (!isClientGained(currentBufferState, mClientStateMask)) {
+ ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d "
+ "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
+ __FUNCTION__, mId, mClientStateMask, currentBufferState);
+ return -EBUSY;
+ }
+ // Set the producer client buffer state to released, other clients' buffer state to posted.
+ // Post to all existing and non-existing clients.
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
+ // TODO(b/119837586): Update fence state and return GPU fence if needed.
+ return 0;
+}
+
+int BufferHubBuffer::acquire() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ if (isClientAcquired(currentBufferState, mClientStateMask)) {
+ ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__,
+ mClientStateMask);
+ return 0;
+ }
+ uint32_t updatedBufferState = 0U;
+ do {
+ if (!isClientPosted(currentBufferState, mClientStateMask)) {
+ ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d "
+ "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".",
+ __FUNCTION__, mId, mClientStateMask, currentBufferState);
+ return -EBUSY;
+ }
+ // Change the buffer state for this consumer from posted to acquired.
+ updatedBufferState = currentBufferState ^ mClientStateMask;
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
+ // TODO(b/119837586): Update fence state and return GPU fence.
+ return 0;
+}
+
+int BufferHubBuffer::release() {
+ uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire);
+ if (isClientReleased(currentBufferState, mClientStateMask)) {
+ ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__,
+ mClientStateMask);
+ return 0;
+ }
+ uint32_t updatedBufferState = 0U;
+ do {
+ updatedBufferState = currentBufferState & (~mClientStateMask);
+ } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire));
+ // TODO(b/119837586): Update fence state and return GPU fence if needed.
+ return 0;
+}
+
+bool BufferHubBuffer::isReleased() const {
+ return (mBufferState->load(std::memory_order_acquire) &
+ mActiveClientsBitMask->load(std::memory_order_acquire)) == 0;
+}
+
+bool BufferHubBuffer::isValid() const {
+ return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
+ mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr;
+}
+
+sp<NativeHandle> BufferHubBuffer::duplicate() {
+ if (mBufferClient == nullptr) {
+ ALOGE("%s: missing BufferClient!", __FUNCTION__);
+ return nullptr;
+ }
+
+ hidl_handle token;
+ BufferHubStatus ret;
+ IBufferClient::duplicate_cb dupCb = [&](const auto& outToken, const auto& status) {
+ token = std::move(outToken);
+ ret = status;
+ };
+
+ if (!mBufferClient->duplicate(dupCb).isOk()) {
+ ALOGE("%s: duplicate transaction failed!", __FUNCTION__);
+ return nullptr;
+ } else if (ret != BufferHubStatus::NO_ERROR) {
+ ALOGE("%s: duplicate failed with error %u.", __FUNCTION__, ret);
+ return nullptr;
+ } else if (token.getNativeHandle() == nullptr) {
+ ALOGE("%s: duplicate got null token.", __FUNCTION__);
+ return nullptr;
+ }
+
+ return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true);
+}
+
+} // namespace android
diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp
new file mode 100644
index 0000000..bffc2ca
--- /dev/null
+++ b/libs/ui/BufferHubEventFd.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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 <sys/eventfd.h>
+
+#include <log/log.h>
+#include <ui/BufferHubEventFd.h>
+
+namespace android {
+
+BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {}
+
+BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {}
+
+status_t BufferHubEventFd::signal() const {
+ if (!isValid()) {
+ ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ eventfd_write(mFd.get(), 1);
+ return OK;
+}
+
+status_t BufferHubEventFd::clear() const {
+ if (!isValid()) {
+ ALOGE("%s: cannot clear an invalid eventfd.", __FUNCTION__);
+ return DEAD_OBJECT;
+ }
+
+ eventfd_t value;
+ eventfd_read(mFd.get(), &value);
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
new file mode 100644
index 0000000..05bc7dd
--- /dev/null
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <limits>
+
+#include <cutils/ashmem.h>
+#include <log/log.h>
+#include <ui/BufferHubMetadata.h>
+
+namespace android {
+
+namespace {
+
+static const int kAshmemProt = PROT_READ | PROT_WRITE;
+
+} // namespace
+
+using BufferHubDefs::kMetadataHeaderSize;
+using BufferHubDefs::MetadataHeader;
+
+/* static */
+BufferHubMetadata BufferHubMetadata::create(size_t userMetadataSize) {
+ // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it
+ // cannot overflow uint32_t.
+ if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) {
+ ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize);
+ return {};
+ }
+
+ const size_t metadataSize = userMetadataSize + kMetadataHeaderSize;
+ int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize);
+ if (fd < 0) {
+ ALOGE("BufferHubMetadata::Create: failed to create ashmem region.");
+ return {};
+ }
+
+ // Hand over the ownership of the fd to a unique_fd immediately after the successful
+ // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd
+ // leaks during error handling.
+ unique_fd ashmemFd{fd};
+
+ if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) {
+ ALOGE("BufferHubMetadata::Create: failed to set protect region.");
+ return {};
+ }
+
+ return BufferHubMetadata::import(std::move(ashmemFd));
+}
+
+/* static */
+BufferHubMetadata BufferHubMetadata::import(unique_fd ashmemFd) {
+ if (!ashmem_valid(ashmemFd.get())) {
+ ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
+ return {};
+ }
+
+ size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get()));
+ size_t userMetadataSize = metadataSize - kMetadataHeaderSize;
+
+ // Note that here the buffer state is mapped from shared memory as an atomic object. The
+ // std::atomic's constructor will not be called so that the original value stored in the memory
+ // region can be preserved.
+ auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt,
+ MAP_SHARED, ashmemFd.get(),
+ /*offset=*/0));
+ if (metadataHeader == nullptr) {
+ ALOGE("BufferHubMetadata::Import: failed to map region.");
+ return {};
+ }
+
+ return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader);
+}
+
+BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
+ MetadataHeader* metadataHeader)
+ : mUserMetadataSize(userMetadataSize),
+ mAshmemFd(std::move(ashmemFd)),
+ mMetadataHeader(metadataHeader) {}
+
+BufferHubMetadata::~BufferHubMetadata() {
+ if (mMetadataHeader != nullptr) {
+ int ret = munmap(mMetadataHeader, metadataSize());
+ ALOGE_IF(ret != 0,
+ "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno);
+ mMetadataHeader = nullptr;
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
index 5b4bf23..7a14af1 100644
--- a/libs/ui/ColorSpace.cpp
+++ b/libs/ui/ColorSpace.cpp
@@ -351,13 +351,12 @@
};
}
-std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size,
- const ColorSpace& src, const ColorSpace& dst) {
-
+std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src,
+ const ColorSpace& dst) {
size = clamp(size, 2u, 256u);
float m = 1.0f / float(size - 1);
- std::unique_ptr<float3> lut(new float3[size * size * size]);
+ std::unique_ptr<float3[]> lut(new float3[size * size * size]);
float3* data = lut.get();
ColorSpaceConnector connector(src, dst);
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 61df02d..ee06d93 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -234,6 +234,9 @@
case ColorMode::BT2020:
return std::string("ColorMode::BT2020");
+ case ColorMode::DISPLAY_BT2020:
+ return std::string("ColorMode::DISPLAY_BT2020");
+
case ColorMode::BT2100_PQ:
return std::string("ColorMode::BT2100_PQ");
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 1414766..340231d 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -33,18 +33,6 @@
const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
-void* FenceTime::operator new(size_t byteCount) noexcept {
- void *p = nullptr;
- if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
- return nullptr;
- }
- return p;
-}
-
-void FenceTime::operator delete(void *p) {
- free(p);
-}
-
FenceTime::FenceTime(const sp<Fence>& fence)
: mState(((fence.get() != nullptr) && fence->isValid()) ?
State::VALID : State::INVALID),
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Gralloc.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Gralloc.cpp
index e6ac6bf..8e09a13 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Gralloc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#define LOG_TAG "Gralloc"
+
+#include <ui/Gralloc.h>
namespace android {
-namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+GrallocMapper::~GrallocMapper() {}
-} // namespace mock
+GrallocAllocator::~GrallocAllocator() {}
+
} // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 37cf617..5dc4530 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -27,9 +27,15 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
-namespace android {
+using android::hardware::graphics::allocator::V2_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::common::V1_1::PixelFormat;
+using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V2_0::Error;
+using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using android::hardware::graphics::mapper::V2_1::IMapper;
-namespace Gralloc2 {
+namespace android {
namespace {
@@ -42,9 +48,6 @@
for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) {
bits = bits | bit;
}
- // TODO(b/72323293, b/72703005): Remove these additional bits
- bits = bits | (1 << 10) | (1 << 13);
-
return bits;
}();
return valid10UsageBits;
@@ -62,17 +65,26 @@
return valid11UsageBits;
}
+static inline IMapper::Rect sGralloc2Rect(const Rect& rect) {
+ IMapper::Rect outRect{};
+ outRect.left = rect.left;
+ outRect.top = rect.top;
+ outRect.width = rect.width();
+ outRect.height = rect.height();
+ return outRect;
+}
+
} // anonymous namespace
-void Mapper::preload() {
+void Gralloc2Mapper::preload() {
android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
}
-Mapper::Mapper()
-{
+Gralloc2Mapper::Gralloc2Mapper() {
mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
if (mMapper == nullptr) {
- LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+ ALOGW("mapper 2.x is not supported");
+ return;
}
if (mMapper->isRemote()) {
LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
@@ -82,30 +94,37 @@
mMapperV2_1 = IMapper::castFrom(mMapper);
}
-Gralloc2::Error Mapper::validateBufferDescriptorInfo(
- const IMapper::BufferDescriptorInfo& descriptorInfo) const {
+bool Gralloc2Mapper::isLoaded() const {
+ return mMapper != nullptr;
+}
+
+status_t Gralloc2Mapper::validateBufferDescriptorInfo(
+ IMapper::BufferDescriptorInfo* descriptorInfo) const {
uint64_t validUsageBits = getValid10UsageBits();
if (mMapperV2_1 != nullptr) {
validUsageBits = validUsageBits | getValid11UsageBits();
}
- if (descriptorInfo.usage & ~validUsageBits) {
+ if (descriptorInfo->usage & ~validUsageBits) {
ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
- descriptorInfo.usage & ~validUsageBits);
- return Error::BAD_VALUE;
+ descriptorInfo->usage & ~validUsageBits);
+ return BAD_VALUE;
}
- return Error::NONE;
+ return NO_ERROR;
}
-Error Mapper::createDescriptor(
- const IMapper::BufferDescriptorInfo& descriptorInfo,
- BufferDescriptor* outDescriptor) const
-{
- Error error = validateBufferDescriptorInfo(descriptorInfo);
- if (error != Error::NONE) {
- return error;
+status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo,
+ void* outBufferDescriptor) const {
+ IMapper::BufferDescriptorInfo* descriptorInfo =
+ static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+ BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+ status_t status = validateBufferDescriptorInfo(descriptorInfo);
+ if (status != NO_ERROR) {
+ return status;
}
+ Error error;
auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor)
{
error = tmpError;
@@ -118,24 +137,23 @@
hardware::Return<void> ret;
if (mMapperV2_1 != nullptr) {
- ret = mMapperV2_1->createDescriptor_2_1(descriptorInfo, hidl_cb);
+ ret = mMapperV2_1->createDescriptor_2_1(*descriptorInfo, hidl_cb);
} else {
const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = {
- descriptorInfo.width,
- descriptorInfo.height,
- descriptorInfo.layerCount,
- static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo.format),
- descriptorInfo.usage,
+ descriptorInfo->width,
+ descriptorInfo->height,
+ descriptorInfo->layerCount,
+ static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo->format),
+ descriptorInfo->usage,
};
ret = mMapper->createDescriptor(info, hidl_cb);
}
- return (ret.isOk()) ? error : kTransactionError;
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}
-Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
- buffer_handle_t* outBufferHandle) const
-{
+status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const {
Error error;
auto ret = mMapper->importBuffer(rawHandle,
[&](const auto& tmpError, const auto& tmpBuffer)
@@ -148,11 +166,10 @@
*outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
});
- return (ret.isOk()) ? error : kTransactionError;
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}
-void Mapper::freeBuffer(buffer_handle_t bufferHandle) const
-{
+void Gralloc2Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
auto buffer = const_cast<native_handle_t*>(bufferHandle);
auto ret = mMapper->freeBuffer(buffer);
@@ -161,23 +178,29 @@
buffer, error);
}
-Error Mapper::validateBufferSize(buffer_handle_t bufferHandle,
- const IMapper::BufferDescriptorInfo& descriptorInfo,
- uint32_t stride) const
-{
+status_t Gralloc2Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+ uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const {
if (mMapperV2_1 == nullptr) {
- return Error::NONE;
+ return NO_ERROR;
}
+ IMapper::BufferDescriptorInfo descriptorInfo = {};
+ descriptorInfo.width = width;
+ descriptorInfo.height = height;
+ descriptorInfo.layerCount = layerCount;
+ descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+ descriptorInfo.usage = usage;
+
auto buffer = const_cast<native_handle_t*>(bufferHandle);
auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride);
- return (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+ return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
}
-void Mapper::getTransportSize(buffer_handle_t bufferHandle,
- uint32_t* outNumFds, uint32_t* outNumInts) const
-{
+void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const {
*outNumFds = uint32_t(bufferHandle->numFds);
*outNumInts = uint32_t(bufferHandle->numInts);
@@ -198,19 +221,24 @@
*outNumInts = tmpNumInts;
});
- if (!ret.isOk()) {
- error = kTransactionError;
- }
- ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d",
- buffer, error);
+ error = (ret.isOk()) ? error : kTransactionError;
+
+ ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
}
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
- const IMapper::Rect& accessRegion,
- int acquireFence, void** outData) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const {
+ if (outBytesPerPixel) {
+ *outBytesPerPixel = -1;
+ }
+ if (outBytesPerStride) {
+ *outBytesPerStride = -1;
+ }
auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
// put acquireFence in a hidl_handle
hardware::hidl_handle acquireFenceHandle;
NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -237,15 +265,19 @@
close(acquireFence);
}
- return (ret.isOk()) ? error : kTransactionError;
+ error = (ret.isOk()) ? error : kTransactionError;
+
+ ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+ return static_cast<status_t>(error);
}
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
- const IMapper::Rect& accessRegion,
- int acquireFence, YCbCrLayout* outLayout) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* ycbcr) const {
auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
// put acquireFence in a hidl_handle
hardware::hidl_handle acquireFenceHandle;
NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -255,6 +287,7 @@
acquireFenceHandle = h;
}
+ YCbCrLayout layout;
Error error;
auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion,
acquireFenceHandle,
@@ -265,19 +298,27 @@
return;
}
- *outLayout = tmpLayout;
+ layout = tmpLayout;
});
+ if (error == Error::NONE) {
+ ycbcr->y = layout.y;
+ ycbcr->cb = layout.cb;
+ ycbcr->cr = layout.cr;
+ ycbcr->ystride = static_cast<size_t>(layout.yStride);
+ ycbcr->cstride = static_cast<size_t>(layout.cStride);
+ ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+ }
+
// we own acquireFence even on errors
if (acquireFence >= 0) {
close(acquireFence);
}
- return (ret.isOk()) ? error : kTransactionError;
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
}
-int Mapper::unlock(buffer_handle_t bufferHandle) const
-{
+int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const {
auto buffer = const_cast<native_handle_t*>(bufferHandle);
int releaseFence = -1;
@@ -302,10 +343,7 @@
}
});
- if (!ret.isOk()) {
- error = kTransactionError;
- }
-
+ error = (ret.isOk()) ? error : kTransactionError;
if (error != Error::NONE) {
ALOGE("unlock(%p) failed with %d", buffer, error);
}
@@ -313,17 +351,25 @@
return releaseFence;
}
-Allocator::Allocator(const Mapper& mapper)
- : mMapper(mapper)
-{
+status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
+ android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/, bool* /*outSupported*/) const {
+ return INVALID_OPERATION;
+}
+
+Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
- LOG_ALWAYS_FATAL("gralloc-alloc is missing");
+ ALOGW("allocator 2.x is not supported");
+ return;
}
}
-std::string Allocator::dumpDebugInfo() const
-{
+bool Gralloc2Allocator::isLoaded() const {
+ return mAllocator != nullptr;
+}
+
+std::string Gralloc2Allocator::dumpDebugInfo() const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -333,40 +379,51 @@
return debugInfo;
}
-Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-{
- Error error;
- auto ret = mAllocator->allocate(descriptor, count,
- [&](const auto& tmpError, const auto& tmpStride,
- const auto& tmpBuffers) {
- error = tmpError;
- if (tmpError != Error::NONE) {
- return;
- }
+status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+ IMapper::BufferDescriptorInfo descriptorInfo = {};
+ descriptorInfo.width = width;
+ descriptorInfo.height = height;
+ descriptorInfo.layerCount = layerCount;
+ descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+ descriptorInfo.usage = usage;
- // import buffers
- for (uint32_t i = 0; i < count; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != Error::NONE) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
- }
- return;
- }
- }
+ BufferDescriptor descriptor;
+ status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+ static_cast<void*>(&descriptor));
+ if (error != NO_ERROR) {
+ return error;
+ }
- *outStride = tmpStride;
- });
+ auto ret = mAllocator->allocate(descriptor, bufferCount,
+ [&](const auto& tmpError, const auto& tmpStride,
+ const auto& tmpBuffers) {
+ error = static_cast<status_t>(tmpError);
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ // import buffers
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
+ }
+ }
+
+ *outStride = tmpStride;
+ });
// make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
hardware::IPCThreadState::self()->flushCommands();
- return (ret.isOk()) ? error : kTransactionError;
+ return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
}
-} // namespace Gralloc2
-
} // namespace android
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
new file mode 100644
index 0000000..7f8e57c
--- /dev/null
+++ b/libs/ui/Gralloc3.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Gralloc3"
+
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc3.h>
+
+#include <inttypes.h>
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+using android::hardware::graphics::allocator::V3_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::mapper::V3_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V3_0::Error;
+using android::hardware::graphics::mapper::V3_0::IMapper;
+using android::hardware::graphics::mapper::V3_0::YCbCrLayout;
+
+namespace android {
+
+namespace {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+uint64_t getValidUsageBits() {
+ static const uint64_t validUsageBits = []() -> uint64_t {
+ uint64_t bits = 0;
+ for (const auto bit :
+ hardware::hidl_enum_range<hardware::graphics::common::V1_0::BufferUsage>()) {
+ bits = bits | bit;
+ }
+ for (const auto bit :
+ hardware::hidl_enum_range<hardware::graphics::common::V1_1::BufferUsage>()) {
+ bits = bits | bit;
+ }
+ return bits;
+ }();
+ return validUsageBits;
+}
+
+static inline IMapper::Rect sGralloc3Rect(const Rect& rect) {
+ IMapper::Rect outRect{};
+ outRect.left = rect.left;
+ outRect.top = rect.top;
+ outRect.width = rect.width();
+ outRect.height = rect.height();
+ return outRect;
+}
+static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+ outDescriptorInfo->width = width;
+ outDescriptorInfo->height = height;
+ outDescriptorInfo->layerCount = layerCount;
+ outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+ outDescriptorInfo->usage = usage;
+}
+
+} // anonymous namespace
+
+void Gralloc3Mapper::preload() {
+ android::hardware::preloadPassthroughService<IMapper>();
+}
+
+Gralloc3Mapper::Gralloc3Mapper() {
+ mMapper = IMapper::getService();
+ if (mMapper == nullptr) {
+ ALOGW("mapper 3.x is not supported");
+ return;
+ }
+ if (mMapper->isRemote()) {
+ LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+ }
+}
+
+bool Gralloc3Mapper::isLoaded() const {
+ return mMapper != nullptr;
+}
+
+status_t Gralloc3Mapper::validateBufferDescriptorInfo(
+ IMapper::BufferDescriptorInfo* descriptorInfo) const {
+ uint64_t validUsageBits = getValidUsageBits();
+
+ if (descriptorInfo->usage & ~validUsageBits) {
+ ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
+ descriptorInfo->usage & ~validUsageBits);
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo,
+ void* outBufferDescriptor) const {
+ IMapper::BufferDescriptorInfo* descriptorInfo =
+ static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+ BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+ status_t status = validateBufferDescriptorInfo(descriptorInfo);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ Error error;
+ auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outDescriptor = tmpDescriptor;
+ };
+
+ hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb);
+
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const {
+ Error error;
+ auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+ });
+
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+void Gralloc3Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->freeBuffer(buffer);
+
+ auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+ ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+ uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
+
+ return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
+}
+
+void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const {
+ *outNumFds = uint32_t(bufferHandle->numFds);
+ *outNumInts = uint32_t(bufferHandle->numInts);
+
+ Error error;
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->getTransportSize(buffer,
+ [&](const auto& tmpError, const auto& tmpNumFds,
+ const auto& tmpNumInts) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outNumFds = tmpNumFds;
+ *outNumInts = tmpNumInts;
+ });
+
+ error = (ret.isOk()) ? error : kTransactionError;
+
+ ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+ // put acquireFence in a hidl_handle
+ hardware::hidl_handle acquireFenceHandle;
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ Error error;
+ auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpData,
+ const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outData = tmpData;
+ if (outBytesPerPixel) {
+ *outBytesPerPixel = tmpBytesPerPixel;
+ }
+ if (outBytesPerStride) {
+ *outBytesPerStride = tmpBytesPerStride;
+ }
+ });
+
+ // we own acquireFence even on errors
+ if (acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ error = (ret.isOk()) ? error : kTransactionError;
+
+ ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+ return static_cast<status_t>(error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* ycbcr) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+ // put acquireFence in a hidl_handle
+ hardware::hidl_handle acquireFenceHandle;
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ YCbCrLayout layout;
+ Error error;
+ auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpLayout) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ layout = tmpLayout;
+ });
+
+ if (error == Error::NONE) {
+ ycbcr->y = layout.y;
+ ycbcr->cb = layout.cb;
+ ycbcr->cr = layout.cr;
+ ycbcr->ystride = static_cast<size_t>(layout.yStride);
+ ycbcr->cstride = static_cast<size_t>(layout.cStride);
+ ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+ }
+
+ // we own acquireFence even on errors
+ if (acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+int Gralloc3Mapper::unlock(buffer_handle_t bufferHandle) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ int releaseFence = -1;
+ Error error;
+ auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ auto fenceHandle = tmpReleaseFence.getNativeHandle();
+ if (fenceHandle && fenceHandle->numFds == 1) {
+ int fd = dup(fenceHandle->data[0]);
+ if (fd >= 0) {
+ releaseFence = fd;
+ } else {
+ ALOGD("failed to dup unlock release fence");
+ sync_wait(fenceHandle->data[0], -1);
+ }
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("unlock(%p) failed with %d", buffer, error);
+ }
+
+ return releaseFence;
+}
+
+status_t Gralloc3Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ bool* outSupported) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+ Error error;
+ auto ret = mMapper->isSupported(descriptorInfo,
+ [&](const auto& tmpError, const auto& tmpSupported) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ if (outSupported) {
+ *outSupported = tmpSupported;
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount,
+ error);
+ }
+
+ return static_cast<status_t>(error);
+}
+
+Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
+ mAllocator = IAllocator::getService();
+ if (mAllocator == nullptr) {
+ ALOGW("allocator 3.x is not supported");
+ return;
+ }
+}
+
+bool Gralloc3Allocator::isLoaded() const {
+ return mAllocator != nullptr;
+}
+
+std::string Gralloc3Allocator::dumpDebugInfo() const {
+ std::string debugInfo;
+
+ mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
+
+ return debugInfo;
+}
+
+status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+ BufferDescriptor descriptor;
+ status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+ static_cast<void*>(&descriptor));
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ auto ret = mAllocator->allocate(descriptor, bufferCount,
+ [&](const auto& tmpError, const auto& tmpStride,
+ const auto& tmpBuffers) {
+ error = static_cast<status_t>(tmpError);
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ // import buffers
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
+ }
+ }
+ *outStride = tmpStride;
+ });
+
+ // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+ hardware::IPCThreadState::self()->flushCommands();
+
+ return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
+}
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 4aa9f62..f800627 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "GraphicBuffer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <ui/GraphicBuffer.h>
@@ -22,10 +23,14 @@
#include <grallocusage/GrallocUsageConversion.h>
-#include <ui/DetachedBufferHandle.h>
+#ifndef LIBUI_IN_VNDK
+#include <ui/BufferHubBuffer.h>
+#endif // LIBUI_IN_VNDK
+
#include <ui/Gralloc2.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
+#include <utils/Trace.h>
namespace android {
@@ -44,6 +49,22 @@
return static_cast<GraphicBuffer *>(anwb);
}
+GraphicBuffer* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer* buffer) {
+ return reinterpret_cast<GraphicBuffer*>(buffer);
+}
+
+GraphicBuffer const* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer const* buffer) {
+ return reinterpret_cast<GraphicBuffer const*>(buffer);
+}
+
+AHardwareBuffer* GraphicBuffer::toAHardwareBuffer() {
+ return reinterpret_cast<AHardwareBuffer*>(this);
+}
+
+AHardwareBuffer const* GraphicBuffer::toAHardwareBuffer() const {
+ return reinterpret_cast<AHardwareBuffer const*>(this);
+}
+
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
@@ -90,8 +111,25 @@
inUsage, inStride);
}
+#ifndef LIBUI_IN_VNDK
+GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() {
+ if (buffer == nullptr) {
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+
+ mInitCheck = initWithHandle(buffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+ buffer->desc().width, buffer->desc().height,
+ static_cast<PixelFormat>(buffer->desc().format),
+ buffer->desc().layers, buffer->desc().usage, buffer->desc().stride);
+ mBufferId = buffer->id();
+ mBufferHubBuffer = std::move(buffer);
+}
+#endif // LIBUI_IN_VNDK
+
GraphicBuffer::~GraphicBuffer()
{
+ ATRACE_CALL();
if (handle) {
free_handle();
}
@@ -217,15 +255,15 @@
return NO_ERROR;
}
-status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr)
-{
+status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
const Rect lockBounds(width, height);
- status_t res = lock(inUsage, lockBounds, vaddr);
+ status_t res = lock(inUsage, lockBounds, vaddr, outBytesPerPixel, outBytesPerStride);
return res;
}
-status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr)
-{
+status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
if (rect.left < 0 || rect.right > width ||
rect.top < 0 || rect.bottom > height) {
ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -233,7 +271,10 @@
width, height);
return BAD_VALUE;
}
- status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr);
+
+ status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel,
+ outBytesPerStride);
+
return res;
}
@@ -264,22 +305,22 @@
return res;
}
-status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd)
-{
+status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
const Rect lockBounds(width, height);
- status_t res = lockAsync(inUsage, lockBounds, vaddr, fenceFd);
+ status_t res =
+ lockAsync(inUsage, lockBounds, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
return res;
}
-status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect,
- void** vaddr, int fenceFd)
-{
- return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd);
+status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
+ return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
}
-status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage,
- uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd)
-{
+status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
+ const Rect& rect, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
if (rect.left < 0 || rect.right > width ||
rect.top < 0 || rect.bottom > height) {
ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -287,8 +328,10 @@
width, height);
return BAD_VALUE;
}
- status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
- inConsumerUsage, rect, vaddr, fenceFd);
+
+ status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect,
+ vaddr, fenceFd, outBytesPerPixel, outBytesPerStride);
+
return res;
}
@@ -320,6 +363,13 @@
return res;
}
+status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint64_t inUsage,
+ bool* outSupported) const {
+ return mBufferMapper.isSupported(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+ outSupported);
+}
+
size_t GraphicBuffer::getFlattenedSize() const {
return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int);
}
@@ -484,23 +534,11 @@
return NO_ERROR;
}
-bool GraphicBuffer::isDetachedBuffer() const {
- return mDetachedBufferHandle && mDetachedBufferHandle->isValid();
+#ifndef LIBUI_IN_VNDK
+bool GraphicBuffer::isBufferHubBuffer() const {
+ return mBufferHubBuffer != nullptr;
}
-
-status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) {
- if (isDetachedBuffer()) {
- ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this "
- "GraphicBuffer. Replacing the old one.");
- }
-
- mDetachedBufferHandle = std::move(channel);
- return NO_ERROR;
-}
-
-std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() {
- return std::move(mDetachedBufferHandle);
-}
+#endif // LIBUI_IN_VNDK
// ---------------------------------------------------------------------------
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index eaba1ed..5a67dc4 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -24,72 +24,73 @@
#include <grallocusage/GrallocUsageConversion.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include <utils/Singleton.h>
-#include <utils/String8.h>
#include <utils/Trace.h>
+#include <ui/Gralloc.h>
#include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
#include <ui/GraphicBufferMapper.h>
namespace android {
// ---------------------------------------------------------------------------
+using base::StringAppendF;
+
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator )
Mutex GraphicBufferAllocator::sLock;
KeyedVector<buffer_handle_t,
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
-GraphicBufferAllocator::GraphicBufferAllocator()
- : mMapper(GraphicBufferMapper::getInstance()),
- mAllocator(std::make_unique<Gralloc2::Allocator>(
- mMapper.getGrallocMapper()))
-{
+GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
+ mAllocator = std::make_unique<const Gralloc3Allocator>(
+ reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
+ if (!mAllocator->isLoaded()) {
+ mAllocator = std::make_unique<const Gralloc2Allocator>(
+ reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+ }
+
+ if (!mAllocator->isLoaded()) {
+ LOG_ALWAYS_FATAL("gralloc-allocator is missing");
+ }
}
GraphicBufferAllocator::~GraphicBufferAllocator() {}
-void GraphicBufferAllocator::dump(String8& result) const
-{
+void GraphicBufferAllocator::dump(std::string& result) const {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
size_t total = 0;
- const size_t SIZE = 4096;
- char buffer[SIZE];
- snprintf(buffer, SIZE, "Allocated buffers:\n");
- result.append(buffer);
+ result.append("Allocated buffers:\n");
const size_t c = list.size();
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
if (rec.size) {
- snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
- " | %s\n",
- list.keyAt(i), rec.size/1024.0,
- rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
- rec.usage, rec.requestorName.c_str());
+ StringAppendF(&result,
+ "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
+ list.keyAt(i), rec.size / 1024.0, rec.width, rec.stride, rec.height,
+ rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
} else {
- snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
- " | %s\n",
- list.keyAt(i),
- rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
- rec.usage, rec.requestorName.c_str());
+ StringAppendF(&result,
+ "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
+ list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount,
+ rec.format, rec.usage, rec.requestorName.c_str());
}
- result.append(buffer);
total += rec.size;
}
- snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0);
- result.append(buffer);
+ StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", total / 1024.0);
- std::string deviceDump = mAllocator->dumpDebugInfo();
- result.append(deviceDump.c_str(), deviceDump.size());
+ result.append(mAllocator->dumpDebugInfo());
}
void GraphicBufferAllocator::dumpToSystemLog()
{
- String8 s;
+ std::string s;
GraphicBufferAllocator::getInstance().dump(s);
- ALOGD("%s", s.string());
+ ALOGD("%s", s.c_str());
}
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
@@ -108,15 +109,12 @@
if (layerCount < 1)
layerCount = 1;
- Gralloc2::IMapper::BufferDescriptorInfo info = {};
- info.width = width;
- info.height = height;
- info.layerCount = layerCount;
- info.format = static_cast<Gralloc2::PixelFormat>(format);
- info.usage = usage;
+ // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
+ usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
- Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
- if (error == Gralloc2::Error::NONE) {
+ status_t error =
+ mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+ if (error == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint32_t bpp = bytesPerPixel(format);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 2d8e582..25b7247 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -32,7 +32,9 @@
#include <utils/Log.h>
#include <utils/Trace.h>
+#include <ui/Gralloc.h>
#include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
#include <ui/GraphicBuffer.h>
#include <system/graphics.h>
@@ -43,12 +45,22 @@
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
void GraphicBufferMapper::preloadHal() {
- Gralloc2::Mapper::preload();
+ Gralloc2Mapper::preload();
+ Gralloc3Mapper::preload();
}
-GraphicBufferMapper::GraphicBufferMapper()
- : mMapper(std::make_unique<const Gralloc2::Mapper>())
-{
+GraphicBufferMapper::GraphicBufferMapper() {
+ mMapper = std::make_unique<const Gralloc3Mapper>();
+ if (!mMapper->isLoaded()) {
+ mMapper = std::make_unique<const Gralloc2Mapper>();
+ mMapperVersion = Version::GRALLOC_2;
+ } else {
+ mMapperVersion = Version::GRALLOC_3;
+ }
+
+ if (!mMapper->isLoaded()) {
+ LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+ }
}
status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
@@ -59,22 +71,15 @@
ATRACE_CALL();
buffer_handle_t bufferHandle;
- Gralloc2::Error error = mMapper->importBuffer(
- hardware::hidl_handle(rawHandle), &bufferHandle);
- if (error != Gralloc2::Error::NONE) {
+ status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle);
+ if (error != NO_ERROR) {
ALOGW("importBuffer(%p) failed: %d", rawHandle, error);
- return static_cast<status_t>(error);
+ return error;
}
- Gralloc2::IMapper::BufferDescriptorInfo info = {};
- info.width = width;
- info.height = height;
- info.layerCount = layerCount;
- info.format = static_cast<Gralloc2::PixelFormat>(format);
- info.usage = usage;
-
- error = mMapper->validateBufferSize(bufferHandle, info, stride);
- if (error != Gralloc2::Error::NONE) {
+ error = mMapper->validateBufferSize(bufferHandle, width, height, format, layerCount, usage,
+ stride);
+ if (error != NO_ERROR) {
ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error);
freeBuffer(bufferHandle);
return static_cast<status_t>(error);
@@ -100,19 +105,10 @@
return NO_ERROR;
}
-static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) {
- Gralloc2::IMapper::Rect outRect{};
- outRect.left = rect.left;
- outRect.top = rect.top;
- outRect.width = rect.width();
- outRect.height = rect.height();
- return outRect;
-}
-
-status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
- const Rect& bounds, void** vaddr)
-{
- return lockAsync(handle, usage, bounds, vaddr, -1);
+status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+ void** vaddr, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
+ return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride);
}
status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage,
@@ -132,27 +128,23 @@
return error;
}
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
-{
- return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+ void** vaddr, int fenceFd, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
+ return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel,
+ outBytesPerStride);
}
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
- uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
- void** vaddr, int fenceFd)
-{
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage,
+ uint64_t consumerUsage, const Rect& bounds, void** vaddr,
+ int fenceFd, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) {
ATRACE_CALL();
const uint64_t usage = static_cast<uint64_t>(
android_convertGralloc1To0Usage(producerUsage, consumerUsage));
- Gralloc2::Error error = mMapper->lock(handle, usage,
- asGralloc2Rect(bounds), fenceFd, vaddr);
-
- ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d",
- handle, error);
-
- return static_cast<status_t>(error);
+ return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel,
+ outBytesPerStride);
}
status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
@@ -160,19 +152,7 @@
{
ATRACE_CALL();
- Gralloc2::YCbCrLayout layout;
- Gralloc2::Error error = mMapper->lock(handle, usage,
- asGralloc2Rect(bounds), fenceFd, &layout);
- if (error == Gralloc2::Error::NONE) {
- ycbcr->y = layout.y;
- ycbcr->cb = layout.cb;
- ycbcr->cr = layout.cr;
- ycbcr->ystride = static_cast<size_t>(layout.yStride);
- ycbcr->cstride = static_cast<size_t>(layout.cStride);
- ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
- }
-
- return static_cast<status_t>(error);
+ return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr);
}
status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
@@ -184,5 +164,10 @@
return NO_ERROR;
}
+status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, bool* outSupported) {
+ return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
+}
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
index 26eb771..70e3ce7 100644
--- a/libs/ui/PublicFormat.cpp
+++ b/libs/ui/PublicFormat.cpp
@@ -14,16 +14,21 @@
* limitations under the License.
*/
+#include <ui/GraphicTypes.h> // ui::Dataspace
#include <ui/PublicFormat.h>
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
+using ui::Dataspace;
+
int mapPublicFormatToHalFormat(PublicFormat f) {
switch (f) {
case PublicFormat::JPEG:
case PublicFormat::DEPTH_POINT_CLOUD:
+ case PublicFormat::DEPTH_JPEG:
+ case PublicFormat::HEIC:
return HAL_PIXEL_FORMAT_BLOB;
case PublicFormat::DEPTH16:
return HAL_PIXEL_FORMAT_Y16;
@@ -37,29 +42,43 @@
}
android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) {
+ Dataspace dataspace;
switch (f) {
case PublicFormat::JPEG:
- return HAL_DATASPACE_V0_JFIF;
+ dataspace = Dataspace::V0_JFIF;
+ break;
case PublicFormat::DEPTH_POINT_CLOUD:
case PublicFormat::DEPTH16:
case PublicFormat::RAW_DEPTH:
- return HAL_DATASPACE_DEPTH;
+ dataspace = Dataspace::DEPTH;
+ break;
case PublicFormat::RAW_SENSOR:
case PublicFormat::RAW_PRIVATE:
case PublicFormat::RAW10:
case PublicFormat::RAW12:
- return HAL_DATASPACE_ARBITRARY;
+ dataspace = Dataspace::ARBITRARY;
+ break;
case PublicFormat::YUV_420_888:
case PublicFormat::NV21:
case PublicFormat::YV12:
- return HAL_DATASPACE_V0_JFIF;
+ dataspace = Dataspace::V0_JFIF;
+ break;
+ case PublicFormat::DEPTH_JPEG:
+ dataspace = Dataspace::DYNAMIC_DEPTH;
+ break;
+ case PublicFormat::HEIC:
+ dataspace = Dataspace::HEIF;
+ break;
default:
// Most formats map to UNKNOWN
- return HAL_DATASPACE_UNKNOWN;
+ dataspace = Dataspace::UNKNOWN;
+ break;
}
+ return static_cast<android_dataspace>(dataspace);
}
PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) {
+ Dataspace ds = static_cast<Dataspace>(dataSpace);
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
@@ -75,8 +94,8 @@
// Enums overlap in both name and value
return static_cast<PublicFormat>(format);
case HAL_PIXEL_FORMAT_RAW16:
- switch (dataSpace) {
- case HAL_DATASPACE_DEPTH:
+ switch (ds) {
+ case Dataspace::DEPTH:
return PublicFormat::RAW_DEPTH;
default:
return PublicFormat::RAW_SENSOR;
@@ -98,8 +117,8 @@
return PublicFormat::PRIVATE;
case HAL_PIXEL_FORMAT_Y16:
// Dataspace-dependent
- switch (dataSpace) {
- case HAL_DATASPACE_DEPTH:
+ switch (ds) {
+ case Dataspace::DEPTH:
return PublicFormat::DEPTH16;
default:
// Assume non-depth Y16 is just Y16.
@@ -107,14 +126,20 @@
}
case HAL_PIXEL_FORMAT_BLOB:
// Dataspace-dependent
- switch (dataSpace) {
- case HAL_DATASPACE_DEPTH:
+ switch (ds) {
+ case Dataspace::DEPTH:
return PublicFormat::DEPTH_POINT_CLOUD;
- case HAL_DATASPACE_V0_JFIF:
+ case Dataspace::V0_JFIF:
return PublicFormat::JPEG;
+ case Dataspace::HEIF:
+ return PublicFormat::HEIC;
default:
- // Assume otherwise-marked blobs are also JPEG
- return PublicFormat::JPEG;
+ if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) {
+ return PublicFormat::DEPTH_JPEG;
+ } else {
+ // Assume otherwise-marked blobs are also JPEG
+ return PublicFormat::JPEG;
+ }
}
case HAL_PIXEL_FORMAT_BGRA_8888:
// Not defined in public API
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index d8702e5..13fed3a 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -72,6 +72,14 @@
return *this;
}
+Rect& Rect::inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom) {
+ this->left += _left;
+ this->top += _top;
+ this->right -= _right;
+ this->bottom -= _bottom;
+ return *this;
+}
+
const Rect Rect::operator +(const Point& rhs) const {
const Rect result(left + rhs.x, top + rhs.y, right + rhs.x, bottom + rhs.y);
return result;
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index fe4ae6c..3bd3748 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -19,8 +19,9 @@
#include <inttypes.h>
#include <limits.h>
+#include <android-base/stringprintf.h>
+
#include <utils/Log.h>
-#include <utils/String8.h>
#include <utils/CallStack.h>
#include <ui/Rect.h>
@@ -41,6 +42,8 @@
namespace android {
// ----------------------------------------------------------------------------
+using base::StringAppendF;
+
enum {
op_nand = region_operator<Rect>::op_nand,
op_and = region_operator<Rect>::op_and,
@@ -325,6 +328,20 @@
return *this;
}
+Region& Region::scaleSelf(float sx, float sy) {
+ size_t count = mStorage.size();
+ Rect* rects = mStorage.editArray();
+ while (count) {
+ rects->left = static_cast<int32_t>(rects->left * sx + 0.5f);
+ rects->right = static_cast<int32_t>(rects->right * sx + 0.5f);
+ rects->top = static_cast<int32_t>(rects->top * sy + 0.5f);
+ rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f);
+ rects++;
+ count--;
+ }
+ return *this;
+}
+
// ----------------------------------------------------------------------------
const Region Region::merge(const Rect& rhs) const {
@@ -854,16 +871,14 @@
// ----------------------------------------------------------------------------
-void Region::dump(String8& out, const char* what, uint32_t /* flags */) const
-{
+void Region::dump(std::string& out, const char* what, uint32_t /* flags */) const {
const_iterator head = begin();
const_iterator const tail = end();
- out.appendFormat(" Region %s (this=%p, count=%" PRIdPTR ")\n",
- what, this, tail - head);
+ StringAppendF(&out, " Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail - head);
while (head != tail) {
- out.appendFormat(" [%3d, %3d, %3d, %3d]\n", head->left, head->top,
- head->right, head->bottom);
+ StringAppendF(&out, " [%3d, %3d, %3d, %3d]\n", head->left, head->top, head->right,
+ head->bottom);
++head;
}
}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Size.cpp
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Size.cpp
index e6ac6bf..d2996d1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Size.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <ui/Size.h>
-namespace android {
-namespace mock {
+namespace android::ui {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+const Size Size::INVALID{-1, -1};
+const Size Size::EMPTY{0, 0};
-} // namespace mock
-} // namespace android
+} // namespace android::ui
diff --git a/services/surfaceflinger/Transform.cpp b/libs/ui/Transform.cpp
similarity index 69%
rename from services/surfaceflinger/Transform.cpp
rename to libs/ui/Transform.cpp
index e05ed53..28c3f7b 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -16,18 +16,14 @@
#include <math.h>
+#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
-#include <utils/String8.h>
#include <ui/Region.h>
-
-#include "Transform.h"
-#include "clz.h"
-
-// ---------------------------------------------------------------------------
+#include <ui/Transform.h>
+#include <utils/String8.h>
namespace android {
-
-// ---------------------------------------------------------------------------
+namespace ui {
Transform::Transform() {
reset();
@@ -41,8 +37,7 @@
set(orientation, 0, 0);
}
-Transform::~Transform() {
-}
+Transform::~Transform() = default;
static const float EPSILON = 0.0f;
@@ -67,7 +62,7 @@
const mat33& A(mMatrix);
const mat33& B(rhs.mMatrix);
mat33& D(r.mMatrix);
- for (int i=0 ; i<3 ; i++) {
+ for (size_t i = 0; i < 3; i++) {
const float v0 = A[0][i];
const float v1 = A[1][i];
const float v2 = A[2][i];
@@ -83,6 +78,12 @@
return r;
}
+Transform& Transform::operator=(const Transform& other) {
+ mMatrix = other.mMatrix;
+ mType = other.mType;
+ return *this;
+}
+
const vec3& Transform::operator [] (size_t i) const {
return mMatrix[i];
}
@@ -95,12 +96,20 @@
return mMatrix[2][1];
}
+float Transform::sx() const {
+ return mMatrix[0][0];
+}
+
+float Transform::sy() const {
+ return mMatrix[1][1];
+}
+
void Transform::reset() {
mType = IDENTITY;
- for(int i=0 ; i<3 ; i++) {
+ for(size_t i = 0; i < 3; i++) {
vec3& v(mMatrix[i]);
- for (int j=0 ; j<3 ; j++)
- v[j] = ((i==j) ? 1.0f : 0.0f);
+ for (size_t j = 0; j < 3; j++)
+ v[j] = ((i == j) ? 1.0f : 0.0f);
}
}
@@ -137,7 +146,7 @@
Transform H, V, R;
if (flags & ROT_90) {
// w & h are inverted when rotating by 90 degrees
- swap(w, h);
+ std::swap(w, h);
}
if (flags & FLIP_H) {
@@ -210,15 +219,15 @@
rb = transform(rb);
if (roundOutwards) {
- r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]));
- r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]));
- r.right = ceilf(max(lt[0], rt[0], lb[0], rb[0]));
- r.bottom = ceilf(max(lt[1], rt[1], lb[1], rb[1]));
+ r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]})));
+ r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]})));
+ r.right = static_cast<int32_t>(ceilf(std::max({lt[0], rt[0], lb[0], rb[0]})));
+ r.bottom = static_cast<int32_t>(ceilf(std::max({lt[1], rt[1], lb[1], rb[1]})));
} else {
- r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
- r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
- r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
- r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+ r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}) + 0.5f));
+ r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}) + 0.5f));
+ r.right = static_cast<int32_t>(floorf(std::max({lt[0], rt[0], lb[0], rb[0]}) + 0.5f));
+ r.bottom = static_cast<int32_t>(floorf(std::max({lt[1], rt[1], lb[1], rb[1]}) + 0.5f));
}
return r;
@@ -237,10 +246,10 @@
rb = transform(rb);
FloatRect r;
- r.left = min(lt[0], rt[0], lb[0], rb[0]);
- r.top = min(lt[1], rt[1], lb[1], rb[1]);
- r.right = max(lt[0], rt[0], lb[0], rb[0]);
- r.bottom = max(lt[1], rt[1], lb[1], rb[1]);
+ r.left = std::min({lt[0], rt[0], lb[0], rb[0]});
+ r.top = std::min({lt[1], rt[1], lb[1], rb[1]});
+ r.right = std::max({lt[0], rt[0], lb[0], rb[0]});
+ r.bottom = std::max({lt[1], rt[1], lb[1], rb[1]});
return r;
}
@@ -259,8 +268,8 @@
out.set(transform(reg.bounds()));
}
} else {
- int xpos = floorf(tx() + 0.5f);
- int ypos = floorf(ty() + 0.5f);
+ int xpos = static_cast<int>(floorf(tx() + 0.5f));
+ int ypos = static_cast<int>(floorf(ty() + 0.5f));
out = reg.translate(xpos, ypos);
}
return out;
@@ -343,7 +352,7 @@
const float x = M[2][0];
const float y = M[2][1];
- const float idet = 1.0 / (a*d - b*c);
+ const float idet = 1.0f / (a*d - b*c);
result.mMatrix[0][0] = d*idet;
result.mMatrix[0][1] = -c*idet;
result.mMatrix[1][0] = -b*idet;
@@ -372,60 +381,79 @@
return (getOrientation() & ROT_INVALID) ? false : true;
}
-void Transform::dump(const char* name) const
-{
- type(); // updates the type
+mat4 Transform::asMatrix4() const {
+ // Internally Transform uses a 3x3 matrix since the transform is meant for
+ // two-dimensional values. An equivalent 4x4 matrix means inserting an extra
+ // row and column which adds as an identity transform on the third
+ // dimension.
- String8 flags, type;
- const mat33& m(mMatrix);
- uint32_t orient = mType >> 8;
+ mat4 m = mat4{mat4::NO_INIT}; // NO_INIT since we explicitly set every element
- if (orient&ROT_INVALID) {
- flags.append("ROT_INVALID ");
+ m[0][0] = mMatrix[0][0];
+ m[0][1] = mMatrix[0][1];
+ m[0][2] = 0.f;
+ m[0][3] = mMatrix[0][2];
+
+ m[1][0] = mMatrix[1][0];
+ m[1][1] = mMatrix[1][1];
+ m[1][2] = 0.f;
+ m[1][3] = mMatrix[1][2];
+
+ m[2][0] = 0.f;
+ m[2][1] = 0.f;
+ m[2][2] = 1.f;
+ m[2][3] = 0.f;
+
+ m[3][0] = mMatrix[2][0];
+ m[3][1] = mMatrix[2][1];
+ m[3][2] = 0.f;
+ m[3][3] = mMatrix[2][2];
+
+ return m;
+}
+
+void Transform::dump(std::string& out, const char* name) const {
+ using android::base::StringAppendF;
+
+ type(); // Ensure the information in mType is up to date
+
+ const uint32_t type = mType;
+ const uint32_t orient = type >> 8;
+
+ StringAppendF(&out, "%s 0x%08x (", name, orient);
+
+ if (orient & ROT_INVALID) {
+ out.append("ROT_INVALID ");
} else {
- if (orient&ROT_90) {
- flags.append("ROT_90 ");
+ if (orient & ROT_90) {
+ out.append("ROT_90 ");
} else {
- flags.append("ROT_0 ");
+ out.append("ROT_0 ");
}
- if (orient&FLIP_V)
- flags.append("FLIP_V ");
- if (orient&FLIP_H)
- flags.append("FLIP_H ");
+ if (orient & FLIP_V) out.append("FLIP_V ");
+ if (orient & FLIP_H) out.append("FLIP_H ");
}
- if (!(mType&(SCALE|ROTATE|TRANSLATE)))
- type.append("IDENTITY ");
- if (mType&SCALE)
- type.append("SCALE ");
- if (mType&ROTATE)
- type.append("ROTATE ");
- if (mType&TRANSLATE)
- type.append("TRANSLATE ");
+ StringAppendF(&out, ") 0x%02x (", type);
- ALOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string());
- ALOGD("%.4f %.4f %.4f", m[0][0], m[1][0], m[2][0]);
- ALOGD("%.4f %.4f %.4f", m[0][1], m[1][1], m[2][1]);
- ALOGD("%.4f %.4f %.4f", m[0][2], m[1][2], m[2][2]);
-}
+ if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
+ if (type & SCALE) out.append("SCALE ");
+ if (type & ROTATE) out.append("ROTATE ");
+ if (type & TRANSLATE) out.append("TRANSLATE ");
-Transform::orientation_flags Transform::fromRotation(ISurfaceComposer::Rotation rotation) {
- // Convert to surfaceflinger's internal rotation type.
- switch (rotation) {
- case ISurfaceComposer::eRotateNone:
- return Transform::ROT_0;
- case ISurfaceComposer::eRotate90:
- return Transform::ROT_90;
- case ISurfaceComposer::eRotate180:
- return Transform::ROT_180;
- case ISurfaceComposer::eRotate270:
- return Transform::ROT_270;
- default:
- ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
- return Transform::ROT_0;
+ out.append(")\n");
+
+ for (size_t i = 0; i < 3; i++) {
+ StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][i]),
+ static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
}
}
-// ---------------------------------------------------------------------------
+void Transform::dump(const char* name) const {
+ std::string out;
+ dump(out, name);
+ ALOGD("%s", out.c_str());
+}
-}; // namespace android
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp
index 7730690..0ac863d 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/ui/UiConfig.cpp
@@ -18,8 +18,7 @@
namespace android {
-void appendUiConfigString(String8& configStr)
-{
+void appendUiConfigString(std::string& configStr) {
static const char* config =
" [libui]";
configStr.append(config);
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
new file mode 100644
index 0000000..5ba189c
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 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_BUFFER_HUB_BUFFER_H_
+#define ANDROID_BUFFER_HUB_BUFFER_H_
+
+#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
+#include <ui/BufferHubDefs.h>
+#include <ui/BufferHubEventFd.h>
+#include <ui/BufferHubMetadata.h>
+#include <utils/NativeHandle.h>
+
+namespace android {
+
+class BufferHubBuffer {
+public:
+ // Allocates a standalone BufferHubBuffer.
+ static std::unique_ptr<BufferHubBuffer> create(uint32_t width, uint32_t height,
+ uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize);
+
+ // Imports the given token to a BufferHubBuffer. Not taking ownership of the token.
+ static std::unique_ptr<BufferHubBuffer> import(const sp<NativeHandle>& token);
+
+ BufferHubBuffer(const BufferHubBuffer&) = delete;
+ void operator=(const BufferHubBuffer&) = delete;
+
+ virtual ~BufferHubBuffer();
+
+ // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in
+ // BufferHub share the same buffer id.
+ int id() const { return mId; }
+
+ // Returns the buffer description, which is guaranteed to be faithful values from BufferHub.
+ const AHardwareBuffer_Desc& desc() const { return mBufferDesc; }
+
+ // Duplicate the underlying Gralloc buffer handle. Caller is responsible to free the handle
+ // after use.
+ native_handle_t* duplicateHandle() {
+ return native_handle_clone(mBufferHandle.getNativeHandle());
+ }
+
+ const BufferHubEventFd& eventFd() const { return mEventFd; }
+
+ // Returns the current value of MetadataHeader::bufferState.
+ uint32_t bufferState() const { return mBufferState->load(std::memory_order_acquire); }
+
+ // A state mask which is unique to a buffer hub client among all its siblings sharing the same
+ // concrete graphic buffer.
+ uint32_t clientStateMask() const { return mClientStateMask; }
+
+ size_t userMetadataSize() const { return mMetadata.userMetadataSize(); }
+
+ // Returns true if the BufferClient is still alive.
+ bool isConnected() const { return mBufferClient->ping().isOk(); }
+
+ // Returns true if the buffer is valid: non-null buffer handle, valid id, valid client bit mask,
+ // valid metadata and valid buffer client
+ bool isValid() const;
+
+ // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is
+ // gained.
+ // The buffer can be gained as long as there is no other client in acquired or gained state.
+ int gain();
+
+ // Posts the gained buffer for other buffer clients to use the buffer.
+ // The buffer can be posted iff the buffer state for this client is gained.
+ // After posting the buffer, this client is put to released state and does not have access to
+ // the buffer for this cycle of the usage of the buffer.
+ int post();
+
+ // Acquires the buffer for shared read permission.
+ // The buffer can be acquired iff the buffer state for this client is posted.
+ int acquire();
+
+ // Releases the buffer.
+ // The buffer can be released from any buffer state.
+ // After releasing the buffer, this client no longer have any permissions to the buffer for the
+ // current cycle of the usage of the buffer.
+ int release();
+
+ // Returns whether the buffer is released by all active clients or not.
+ bool isReleased() const;
+
+ // Creates a token that stands for this BufferHubBuffer client and could be used for Import to
+ // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
+ // gralloc buffer and ashmem region for metadata. Not taking ownership of the token.
+ // Returns a valid token on success, nullptr on failure.
+ sp<NativeHandle> duplicate();
+
+private:
+ BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize);
+
+ BufferHubBuffer(const sp<NativeHandle>& token);
+
+ int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits);
+
+ // Global id for the buffer that is consistent across processes.
+ int mId = 0;
+
+ // Client state mask of this BufferHubBuffer object. It is unique amoung all
+ // clients/users of the buffer.
+ uint32_t mClientStateMask = 0U;
+
+ // Stores ground truth of the buffer.
+ AHardwareBuffer_Desc mBufferDesc;
+
+ // Wraps the gralloc buffer handle of this buffer.
+ hardware::hidl_handle mBufferHandle;
+
+ // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer.
+ BufferHubEventFd mEventFd;
+
+ // An ashmem-based metadata object. The same shared memory are mapped to the
+ // bufferhubd daemon and all buffer clients.
+ BufferHubMetadata mMetadata;
+ // Shortcuts to the atomics inside the header of mMetadata.
+ std::atomic<uint32_t>* mBufferState = nullptr;
+ std::atomic<uint32_t>* mFenceState = nullptr;
+ std::atomic<uint32_t>* mActiveClientsBitMask = nullptr;
+
+ // HwBinder backend
+ sp<frameworks::bufferhub::V1_0::IBufferClient> mBufferClient;
+};
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_BUFFER_H_
diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h
new file mode 100644
index 0000000..10f274f
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubDefs.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 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_BUFFER_HUB_DEFS_H_
+#define ANDROID_BUFFER_HUB_DEFS_H_
+
+#include <atomic>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpacked"
+// TODO(b/118893702): remove dependency once DvrNativeBufferMetadata moved out of libdvr
+#include <dvr/dvr_api.h>
+#pragma clang diagnostic pop
+
+namespace android {
+
+namespace BufferHubDefs {
+
+// Single buffer clients (up to 16) ownership signal.
+// 32-bit atomic unsigned int.
+// Each client takes 2 bits. The first bit locates in the first 16 bits of
+// bufferState; the second bit locates in the last 16 bits of bufferState.
+// Client states:
+// Gained state 11. Exclusive write state.
+// Posted state 10.
+// Acquired state 01. Shared read state.
+// Released state 00.
+//
+// MSB LSB
+// | |
+// v v
+// [C15|...|C1|C0|C15| ... |C1|C0]
+
+// Maximum number of clients a buffer can have.
+static constexpr int kMaxNumberOfClients = 16;
+
+// Definition of bit masks.
+// MSB LSB
+// | kHighBitsMask | kLowbitsMask |
+// v v v
+// [b31| ... |b16|b15| ... |b0]
+
+// The location of lower 16 bits in the 32-bit buffer state.
+static constexpr uint32_t kLowbitsMask = (1U << kMaxNumberOfClients) - 1U;
+
+// The location of higher 16 bits in the 32-bit buffer state.
+static constexpr uint32_t kHighBitsMask = ~kLowbitsMask;
+
+// The client bit mask of the first client.
+static constexpr uint32_t kFirstClientBitMask = (1U << kMaxNumberOfClients) + 1U;
+
+// Returns true if any of the client is in gained state.
+static inline bool isAnyClientGained(uint32_t state) {
+ uint32_t highBits = state >> kMaxNumberOfClients;
+ uint32_t lowBits = state & kLowbitsMask;
+ return highBits == lowBits && lowBits != 0U;
+}
+
+// Returns true if the input client is in gained state.
+static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) {
+ return state == client_bit_mask;
+}
+
+// Returns true if any of the client is in posted state.
+static inline bool isAnyClientPosted(uint32_t state) {
+ uint32_t highBits = state >> kMaxNumberOfClients;
+ uint32_t lowBits = state & kLowbitsMask;
+ uint32_t postedOrAcquired = highBits ^ lowBits;
+ return postedOrAcquired & highBits;
+}
+
+// Returns true if the input client is in posted state.
+static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) {
+ uint32_t clientBits = state & client_bit_mask;
+ if (clientBits == 0U) return false;
+ uint32_t lowBits = clientBits & kLowbitsMask;
+ return lowBits == 0U;
+}
+
+// Return true if any of the client is in acquired state.
+static inline bool isAnyClientAcquired(uint32_t state) {
+ uint32_t highBits = state >> kMaxNumberOfClients;
+ uint32_t lowBits = state & kLowbitsMask;
+ uint32_t postedOrAcquired = highBits ^ lowBits;
+ return postedOrAcquired & lowBits;
+}
+
+// Return true if the input client is in acquired state.
+static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) {
+ uint32_t clientBits = state & client_bit_mask;
+ if (clientBits == 0U) return false;
+ uint32_t highBits = clientBits & kHighBitsMask;
+ return highBits == 0U;
+}
+
+// Returns true if the input client is in released state.
+static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) {
+ return (state & client_bit_mask) == 0U;
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) {
+ uint32_t lowUnion = union_bits & kLowbitsMask;
+ if (lowUnion == kLowbitsMask) return 0U;
+ uint32_t incremented = lowUnion + 1U;
+ uint32_t difference = incremented ^ lowUnion;
+ uint32_t newLowBit = (difference + 1U) >> 1;
+ return newLowBit + (newLowBit << kMaxNumberOfClients);
+}
+
+struct __attribute__((aligned(8))) MetadataHeader {
+ // Internal data format, which can be updated as long as the size, padding and field alignment
+ // of the struct is consistent within the same ABI. As this part is subject for future updates,
+ // it's not stable cross Android version, so don't have it visible from outside of the Android
+ // platform (include Apps and vendor HAL).
+
+ // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in
+ // bufferState.
+ std::atomic<uint32_t> bufferState;
+
+ // Every client takes up one bit in fenceState. Only the lower 32 bits are valid. The upper 32
+ // bits are there for easier manipulation, but the value should be ignored.
+ std::atomic<uint32_t> fenceState;
+
+ // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in
+ // activeClientsBitMask.
+ std::atomic<uint32_t> activeClientsBitMask;
+
+ // Explicit padding 4 bytes.
+ uint32_t padding;
+
+ // The index of the buffer queue where the buffer belongs to.
+ uint64_t queueIndex;
+
+ // Public data format, which should be updated with caution. See more details in dvr_api.h
+ DvrNativeBufferMetadata metadata;
+};
+
+static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size");
+static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader);
+
+/**
+ * android.frameworks.bufferhub@1.0::BufferTraits.bufferInfo is an opaque handle. See
+ * https://cs.corp.google.com/android/frameworks/hardware/interfaces/bufferhub/1.0/types.hal for
+ * more details about android.frameworks.bufferhub@1.0::BufferTraits.
+ *
+ * This definition could be changed, but implementation of BufferHubService::buildBufferInfo
+ * (frameworks/native/services/bufferhub), VtsHalBufferHubV1_0TargetTest
+ * (frameworks/hardware/interfaces/bufferhub) and BufferHubBuffer::readBufferTraits (libui) will
+ * also need to be updated.
+ *
+ * It's definition should follow the following format:
+ * {
+ * NumFds = 2,
+ * NumInts = 3,
+ * data[0] = Ashmem fd for BufferHubMetadata,
+ * data[1] = event fd,
+ * data[2] = buffer id,
+ * data[3] = client state bit mask,
+ * data[4] = user metadata size,
+ * }
+ */
+static constexpr int kBufferInfoNumFds = 2;
+static constexpr int kBufferInfoNumInts = 3;
+
+} // namespace BufferHubDefs
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_DEFS_H_
diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h
new file mode 100644
index 0000000..8772304
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubEventFd.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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_BUFFER_HUB_EVENT_FD_H_
+#define ANDROID_BUFFER_HUB_EVENT_FD_H_
+
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class BufferHubEventFd {
+public:
+ /**
+ * Constructs a valid event fd.
+ */
+ BufferHubEventFd();
+
+ /**
+ * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes
+ * ownership.
+ */
+ BufferHubEventFd(int fd);
+
+ /**
+ * Returns whether this BufferHubEventFd holds a valid event_fd.
+ */
+ bool isValid() const { return get() >= 0; }
+
+ /**
+ * Returns the fd number of the BufferHubEventFd object. Note that there is no ownership
+ * transfer.
+ */
+ int get() const { return mFd.get(); }
+
+ /**
+ * Signals the eventfd.
+ */
+ status_t signal() const;
+
+ /**
+ * Clears the signal from this eventfd if it is signaled.
+ */
+ status_t clear() const;
+
+private:
+ base::unique_fd mFd;
+};
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_EVENT_FD_H_
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
new file mode 100644
index 0000000..3482507
--- /dev/null
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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_BUFFER_HUB_METADATA_H_
+#define ANDROID_BUFFER_HUB_METADATA_H_
+
+#include <android-base/unique_fd.h>
+#include <ui/BufferHubDefs.h>
+
+namespace android {
+
+namespace {
+using base::unique_fd;
+} // namespace
+
+class BufferHubMetadata {
+public:
+ // Creates a new BufferHubMetadata backed by an ashmem region.
+ //
+ // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata
+ // shared memory region to be allocated is the size of canonical
+ // BufferHubDefs::MetadataHeader plus userMetadataSize.
+ static BufferHubMetadata create(size_t userMetadataSize);
+
+ // Imports an existing BufferHubMetadata from an ashmem FD.
+ //
+ // @param ashmemFd Ashmem file descriptor representing an ashmem region.
+ static BufferHubMetadata import(unique_fd ashmemFd);
+
+ BufferHubMetadata() = default;
+
+ BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); }
+
+ ~BufferHubMetadata();
+
+ BufferHubMetadata& operator=(BufferHubMetadata&& other) {
+ if (this != &other) {
+ mUserMetadataSize = other.mUserMetadataSize;
+ other.mUserMetadataSize = 0;
+
+ mAshmemFd = std::move(other.mAshmemFd);
+
+ // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will
+ // automatically mummap() the shared memory.
+ mMetadataHeader = other.mMetadataHeader;
+ other.mMetadataHeader = nullptr;
+ }
+ return *this;
+ }
+
+ // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem
+ // has been mapped into virtual address space.
+ bool isValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
+
+ size_t userMetadataSize() const { return mUserMetadataSize; }
+ size_t metadataSize() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
+
+ const unique_fd& ashmemFd() const { return mAshmemFd; }
+ BufferHubDefs::MetadataHeader* metadataHeader() { return mMetadataHeader; }
+
+private:
+ BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
+ BufferHubDefs::MetadataHeader* metadataHeader);
+
+ BufferHubMetadata(const BufferHubMetadata&) = delete;
+ void operator=(const BufferHubMetadata&) = delete;
+
+ size_t mUserMetadataSize = 0;
+ unique_fd mAshmemFd;
+ BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
+};
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_HUB_METADATA_H_
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include/ui/ColorSpace.h
index 8ccf6d3..241ec10 100644
--- a/libs/ui/include/ui/ColorSpace.h
+++ b/libs/ui/include/ui/ColorSpace.h
@@ -250,8 +250,8 @@
// axis is thus already flipped
// The source color space must define its values in the domain [0..1]
// The generated LUT transforms from gamma space to gamma space
- static std::unique_ptr<float3> createLUT(uint32_t size,
- const ColorSpace& src, const ColorSpace& dst);
+ static std::unique_ptr<float3[]> createLUT(uint32_t size, const ColorSpace& src,
+ const ColorSpace& dst);
private:
static constexpr mat3 computeXYZMatrix(
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/include/ui/ConfigStoreTypes.h
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/include/ui/ConfigStoreTypes.h
index e6ac6bf..4445ae9 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/include/ui/ConfigStoreTypes.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,24 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
namespace android {
-namespace mock {
+namespace ui {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+struct CieXyz {
+ float X;
+ float Y;
+ float Z;
+};
+struct DisplayPrimaries {
+ CieXyz red;
+ CieXyz green;
+ CieXyz blue;
+ CieXyz white;
+};
-} // namespace mock
-} // namespace android
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include/ui/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h
deleted file mode 100644
index f3c328d..0000000
--- a/libs/ui/include/ui/DetachedBufferHandle.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 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_DETACHED_BUFFER_HUB_HANDLE_H
-#define ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
-
-#include <pdx/channel_handle.h>
-
-#include <memory>
-
-namespace android {
-
-// A wrapper that holds a pdx::LocalChannelHandle object. From the handle, a BufferHub buffer can be
-// created. Current implementation assumes that the underlying transport is using libpdx (thus
-// holding a pdx::LocalChannelHandle object), but future implementation can change it to a Binder
-// backend if ever needed.
-class DetachedBufferHandle {
-public:
- static std::unique_ptr<DetachedBufferHandle> Create(pdx::LocalChannelHandle handle) {
- return std::unique_ptr<DetachedBufferHandle>(new DetachedBufferHandle(std::move(handle)));
- }
-
- // Accessors to get or take the internal pdx::LocalChannelHandle.
- pdx::LocalChannelHandle& handle() { return mHandle; }
- const pdx::LocalChannelHandle& handle() const { return mHandle; }
-
- // Returns whether the DetachedBufferHandle holds a BufferHub channel.
- bool isValid() const { return mHandle.valid(); }
-
-private:
- // Constructs a DetachedBufferHandle from a pdx::LocalChannelHandle.
- explicit DetachedBufferHandle(pdx::LocalChannelHandle handle) : mHandle(std::move(handle)) {}
-
- pdx::LocalChannelHandle mHandle;
-};
-
-} // namespace android
-
-#endif // ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 94caf6b..8976d2d 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -35,6 +35,8 @@
bool secure{false};
nsecs_t appVsyncOffset{0};
nsecs_t presentationDeadline{0};
+ uint32_t viewportW{0};
+ uint32_t viewportH{0};
};
/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/libs/ui/include/ui/DisplayedFrameStats.h b/libs/ui/include/ui/DisplayedFrameStats.h
new file mode 100644
index 0000000..7a70ea1
--- /dev/null
+++ b/libs/ui/include/ui/DisplayedFrameStats.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 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 <vector>
+
+namespace android {
+
+struct DisplayedFrameStats {
+ /* The number of frames represented by this sample. */
+ uint64_t numFrames = 0;
+ /* A histogram counting how many times a pixel of a given value was displayed onscreen for
+ * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets
+ * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that
+ * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels
+ * were displayed onscreen in range 0x40->0x7F, etc.
+ */
+ std::vector<uint64_t> component_0_sample = {};
+ /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */
+ std::vector<uint64_t> component_1_sample = {};
+ /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */
+ std::vector<uint64_t> component_2_sample = {};
+ /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */
+ std::vector<uint64_t> component_3_sample = {};
+};
+
+} // namespace android
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index ec67fa9..6efecd3 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -99,6 +99,12 @@
// be returned and errno will indicate the problem.
int dup() const;
+ // Return the underlying file descriptor without giving up ownership. The
+ // returned file descriptor is only valid for as long as the owning Fence
+ // object lives. (If the situation is unclear, dup() is always a safer
+ // option.)
+ int get() const { return mFenceFd.get(); }
+
// getSignalTime returns the system monotonic clock time at which the
// fence transitioned to the signaled state. If the fence is not signaled
// then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 871fcf2..a5a1fcb 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -113,11 +113,6 @@
void signalForTest(nsecs_t signalTime);
- // Override new and delete since this needs 8-byte alignment, which
- // is not guaranteed on x86.
- static void* operator new(size_t nbytes) noexcept;
- static void operator delete(void *p);
-
private:
// For tests only. If forceValidForTest is true, then getSignalTime will
// never return SIGNAL_TIME_INVALID and isValid will always return true.
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 6a7479a..4cd9a0b 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -28,7 +28,7 @@
float getHeight() const { return bottom - top; }
FloatRect intersect(const FloatRect& other) const {
- return {
+ FloatRect intersection = {
// Inline to avoid tromping on other min/max defines or adding a
// dependency on STL
(left > other.left) ? left : other.left,
@@ -36,6 +36,10 @@
(right < other.right) ? right : other.right,
(bottom < other.bottom) ? bottom : other.bottom
};
+ if (intersection.getWidth() < 0 || intersection.getHeight() < 0) {
+ return {0, 0, 0, 0};
+ }
+ return intersection;
}
float left = 0.0f;
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
new file mode 100644
index 0000000..6cc23f0
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GRALLOC_H
+#define ANDROID_UI_GRALLOC_H
+
+#include <string>
+
+#include <hidl/HidlSupport.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+// A wrapper to IMapper
+class GrallocMapper {
+public:
+ virtual ~GrallocMapper();
+
+ virtual bool isLoaded() const = 0;
+
+ virtual status_t createDescriptor(void* bufferDescriptorInfo,
+ void* outBufferDescriptor) const = 0;
+
+ // Import a buffer that is from another HAL, another process, or is
+ // cloned.
+ //
+ // The returned handle must be freed with freeBuffer.
+ virtual status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const = 0;
+
+ virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0;
+
+ virtual status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+ uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const = 0;
+
+ virtual void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const = 0;
+
+ // The ownership of acquireFence is always transferred to the callee, even
+ // on errors.
+ virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const = 0;
+
+ // The ownership of acquireFence is always transferred to the callee, even
+ // on errors.
+ virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* ycbcr) const = 0;
+
+ // unlock returns a fence sync object (or -1) and the fence sync object is
+ // owned by the caller
+ virtual int unlock(buffer_handle_t bufferHandle) const = 0;
+
+ // isSupported queries whether or not a buffer with the given width, height,
+ // format, layer count, and usage can be allocated on the device. If
+ // *outSupported is set to true, a buffer with the given specifications may be successfully
+ // allocated if resources are available. If false, a buffer with the given specifications will
+ // never successfully allocate on this device. Note that this function is not guaranteed to be
+ // supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
+ virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
+};
+
+// A wrapper to IAllocator
+class GrallocAllocator {
+public:
+ virtual ~GrallocAllocator();
+
+ virtual bool isLoaded() const = 0;
+
+ virtual std::string dumpDebugInfo() const = 0;
+
+ /*
+ * The returned buffers are already imported and must not be imported
+ * again. outBufferHandles must point to a space that can contain at
+ * least "bufferCount" buffer_handle_t.
+ */
+ virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_H
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 5a8dbda..948f597 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -23,119 +23,75 @@
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <android/hardware/graphics/mapper/2.1/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
#include <utils/StrongPointer.h>
namespace android {
-namespace Gralloc2 {
-
-using hardware::graphics::allocator::V2_0::IAllocator;
-using hardware::graphics::common::V1_1::BufferUsage;
-using hardware::graphics::common::V1_1::PixelFormat;
-using hardware::graphics::mapper::V2_1::IMapper;
-using hardware::graphics::mapper::V2_0::BufferDescriptor;
-using hardware::graphics::mapper::V2_0::Error;
-using hardware::graphics::mapper::V2_0::YCbCrLayout;
-
-// A wrapper to IMapper
-class Mapper {
+class Gralloc2Mapper : public GrallocMapper {
public:
static void preload();
- Mapper();
+ Gralloc2Mapper();
- Error createDescriptor(
- const IMapper::BufferDescriptorInfo& descriptorInfo,
- BufferDescriptor* outDescriptor) const;
+ bool isLoaded() const override;
- // Import a buffer that is from another HAL, another process, or is
- // cloned.
- //
- // The returned handle must be freed with freeBuffer.
- Error importBuffer(const hardware::hidl_handle& rawHandle,
- buffer_handle_t* outBufferHandle) const;
+ status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
- void freeBuffer(buffer_handle_t bufferHandle) const;
+ status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const override;
- Error validateBufferSize(buffer_handle_t bufferHandle,
- const IMapper::BufferDescriptorInfo& descriptorInfo,
- uint32_t stride) const;
+ void freeBuffer(buffer_handle_t bufferHandle) const override;
- void getTransportSize(buffer_handle_t bufferHandle,
- uint32_t* outNumFds, uint32_t* outNumInts) const;
+ status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const override;
- // The ownership of acquireFence is always transferred to the callee, even
- // on errors.
- Error lock(buffer_handle_t bufferHandle, uint64_t usage,
- const IMapper::Rect& accessRegion,
- int acquireFence, void** outData) const;
+ void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const override;
- // The ownership of acquireFence is always transferred to the callee, even
- // on errors.
- Error lock(buffer_handle_t bufferHandle, uint64_t usage,
- const IMapper::Rect& accessRegion,
- int acquireFence, YCbCrLayout* outLayout) const;
+ status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const override;
- // unlock returns a fence sync object (or -1) and the fence sync object is
- // owned by the caller
- int unlock(buffer_handle_t bufferHandle) const;
+ status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* ycbcr) const override;
+
+ int unlock(buffer_handle_t bufferHandle) const override;
+
+ status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
private:
// Determines whether the passed info is compatible with the mapper.
- Error validateBufferDescriptorInfo(
- const IMapper::BufferDescriptorInfo& descriptorInfo) const;
+ status_t validateBufferDescriptorInfo(
+ hardware::graphics::mapper::V2_1::IMapper::BufferDescriptorInfo* descriptorInfo) const;
sp<hardware::graphics::mapper::V2_0::IMapper> mMapper;
- sp<IMapper> mMapperV2_1;
+ sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1;
};
-// A wrapper to IAllocator
-class Allocator {
+class Gralloc2Allocator : public GrallocAllocator {
public:
// An allocator relies on a mapper, and that mapper must be alive at all
// time.
- Allocator(const Mapper& mapper);
+ Gralloc2Allocator(const Gralloc2Mapper& mapper);
- std::string dumpDebugInfo() const;
+ bool isLoaded() const override;
- /*
- * The returned buffers are already imported and must not be imported
- * again. outBufferHandles must point to a space that can contain at
- * least "count" buffer_handle_t.
- */
- Error allocate(BufferDescriptor descriptor, uint32_t count,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
+ std::string dumpDebugInfo() const override;
- Error allocate(BufferDescriptor descriptor,
- uint32_t* outStride, buffer_handle_t* outBufferHandle) const
- {
- return allocate(descriptor, 1, outStride, outBufferHandle);
- }
-
- Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
- uint32_t* outStride, buffer_handle_t* outBufferHandles) const
- {
- BufferDescriptor descriptor;
- Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
- if (error == Error::NONE) {
- error = allocate(descriptor, count, outStride, outBufferHandles);
- }
- return error;
- }
-
- Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
- uint32_t* outStride, buffer_handle_t* outBufferHandle) const
- {
- return allocate(descriptorInfo, 1, outStride, outBufferHandle);
- }
+ status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles) const override;
private:
- const Mapper& mMapper;
- sp<IAllocator> mAllocator;
+ const Gralloc2Mapper& mMapper;
+ sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator;
};
-} // namespace Gralloc2
-
} // namespace android
#endif // ANDROID_UI_GRALLOC2_H
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
new file mode 100644
index 0000000..0965f52
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GRALLOC3_H
+#define ANDROID_UI_GRALLOC3_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Gralloc3Mapper : public GrallocMapper {
+public:
+ static void preload();
+
+ Gralloc3Mapper();
+
+ bool isLoaded() const override;
+
+ status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+
+ status_t importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const override;
+
+ void freeBuffer(buffer_handle_t bufferHandle) const override;
+
+ status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t stride) const override;
+
+ void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+ uint32_t* outNumInts) const override;
+
+ status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, void** outData, int32_t* outBytesPerPixel,
+ int32_t* outBytesPerStride) const override;
+
+ status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* ycbcr) const override;
+
+ int unlock(buffer_handle_t bufferHandle) const override;
+
+ status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
+
+private:
+ // Determines whether the passed info is compatible with the mapper.
+ status_t validateBufferDescriptorInfo(
+ hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+
+ sp<hardware::graphics::mapper::V3_0::IMapper> mMapper;
+};
+
+class Gralloc3Allocator : public GrallocAllocator {
+public:
+ // An allocator relies on a mapper, and that mapper must be alive at all
+ // time.
+ Gralloc3Allocator(const Gralloc3Mapper& mapper);
+
+ bool isLoaded() const override;
+
+ std::string dumpDebugInfo() const override;
+
+ status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles) const override;
+
+private:
+ const Gralloc3Mapper& mMapper;
+ sp<hardware::graphics::allocator::V3_0::IAllocator> mAllocator;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC3_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 315db11..1c88777 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -22,7 +22,9 @@
#include <string>
+#include <android/hardware_buffer.h>
#include <ui/ANativeObjectBase.h>
+#include <ui/GraphicBufferMapper.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/Flattenable.h>
@@ -34,7 +36,10 @@
namespace android {
-class DetachedBufferHandle;
+#ifndef LIBUI_IN_VNDK
+class BufferHubBuffer;
+#endif // LIBUI_IN_VNDK
+
class GraphicBufferMapper;
// ===========================================================================
@@ -75,6 +80,10 @@
static sp<GraphicBuffer> from(ANativeWindowBuffer *);
+ static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*);
+ static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*);
+ AHardwareBuffer* toAHardwareBuffer();
+ AHardwareBuffer const* toAHardwareBuffer() const;
// Create a GraphicBuffer to be unflatten'ed into or be reallocated.
GraphicBuffer();
@@ -134,6 +143,11 @@
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage, std::string requestorName = "<Unknown>");
+#ifndef LIBUI_IN_VNDK
+ // Create a GraphicBuffer from an existing BufferHubBuffer.
+ GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer);
+#endif // LIBUI_IN_VNDK
+
// return status
status_t initCheck() const;
@@ -145,6 +159,7 @@
uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); }
Rect getBounds() const { return Rect(width, height); }
uint64_t getId() const { return mId; }
+ int32_t getBufferId() const { return mBufferId; }
uint32_t getGenerationNumber() const { return mGenerationNumber; }
void setGenerationNumber(uint32_t generation) {
@@ -160,24 +175,35 @@
bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage);
- status_t lock(uint32_t inUsage, void** vaddr);
- status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
+ // For the following two lock functions, if bytesPerStride or bytesPerPixel
+ // are unknown or variable, -1 will be returned
+ status_t lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel = nullptr,
+ int32_t* outBytesPerStride = nullptr);
+ status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
// For HAL_PIXEL_FORMAT_YCbCr_420_888
status_t lockYCbCr(uint32_t inUsage, android_ycbcr *ycbcr);
status_t lockYCbCr(uint32_t inUsage, const Rect& rect,
android_ycbcr *ycbcr);
status_t unlock();
- status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd);
- status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr,
- int fenceFd);
- status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
- const Rect& rect, void** vaddr, int fenceFd);
+ // For the following three lockAsync functions, if bytesPerStride or bytesPerPixel
+ // are unknown or variable, -1 will be returned
+ status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
+ status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
+ status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect,
+ void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr,
+ int32_t* outBytesPerStride = nullptr);
status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr,
int fenceFd);
status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
android_ycbcr *ycbcr, int fenceFd);
status_t unlockAsync(int *fenceFd);
+ status_t isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+ uint32_t inLayerCount, uint64_t inUsage, bool* outSupported) const;
+
ANativeWindowBuffer* getNativeBuffer() const;
// for debugging
@@ -189,10 +215,14 @@
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
- // Sets and takes DetachedBuffer. Should only be called from BufferHub.
- bool isDetachedBuffer() const;
- status_t setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> detachedBuffer);
- std::unique_ptr<DetachedBufferHandle> takeDetachedBufferHandle();
+ GraphicBufferMapper::Version getBufferMapperVersion() const {
+ return mBufferMapper.getMapperVersion();
+ }
+
+#ifndef LIBUI_IN_VNDK
+ // Returns whether this GraphicBuffer is backed by BufferHubBuffer.
+ bool isBufferHubBuffer() const;
+#endif // LIBUI_IN_VNDK
private:
~GraphicBuffer();
@@ -239,21 +269,21 @@
uint64_t mId;
+ // System unique buffer ID. Note that this is different from mId, which is process unique. For
+ // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the
+ // same cross process for the same chunck of underlying memory. Also note that this only applies
+ // to GraphicBuffers that are backed by BufferHub.
+ int32_t mBufferId = -1;
+
// Stores the generation number of this buffer. If this number does not
// match the BufferQueue's internal generation number (set through
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
uint32_t mGenerationNumber;
- // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a
- // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub,
- // there are three different modes:
- // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be
- // invalid.
- // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer
- // set. In this mode, mDetachedBufferHandle must be valid.
- // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer
- // set. In this mode, mDetachedBufferHandle must be invalid.
- std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle;
+#ifndef LIBUI_IN_VNDK
+ // Stores a BufferHubBuffer that handles buffer signaling, identification.
+ std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
+#endif // LIBUI_IN_VNDK
};
}; // namespace android
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 14a865e..3a547b6 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -34,12 +34,8 @@
namespace android {
-namespace Gralloc2 {
-class Allocator;
-}
-
+class GrallocAllocator;
class GraphicBufferMapper;
-class String8;
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
{
@@ -53,7 +49,7 @@
status_t free(buffer_handle_t handle);
- void dump(String8& res) const;
+ void dump(std::string& res) const;
static void dumpToSystemLog();
private:
@@ -76,7 +72,7 @@
~GraphicBufferAllocator();
GraphicBufferMapper& mMapper;
- const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+ std::unique_ptr<const GrallocAllocator> mAllocator;
};
// ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 7cf003d..2461454 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -35,15 +35,16 @@
// ---------------------------------------------------------------------------
-namespace Gralloc2 {
-class Mapper;
-}
-
+class GrallocMapper;
class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
{
public:
+ enum Version {
+ GRALLOC_2,
+ GRALLOC_3,
+ };
static void preloadHal();
static inline GraphicBufferMapper& get() { return getInstance(); }
@@ -59,20 +60,21 @@
void getTransportSize(buffer_handle_t handle,
uint32_t* outTransportNumFds, uint32_t* outTransportNumInts);
- status_t lock(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, void** vaddr);
+ status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
status_t lockYCbCr(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr);
status_t unlock(buffer_handle_t handle);
- status_t lockAsync(buffer_handle_t handle,
- uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
+ status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+ int fenceFd, int32_t* outBytesPerPixel = nullptr,
+ int32_t* outBytesPerStride = nullptr);
- status_t lockAsync(buffer_handle_t handle,
- uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
- void** vaddr, int fenceFd);
+ status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage,
+ const Rect& bounds, void** vaddr, int fenceFd,
+ int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
status_t lockAsyncYCbCr(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
@@ -80,17 +82,23 @@
status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
- const Gralloc2::Mapper& getGrallocMapper() const
- {
- return *mMapper;
+ status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+ uint32_t layerCount, uint64_t usage, bool* outSupported);
+
+ const GrallocMapper& getGrallocMapper() const {
+ return reinterpret_cast<const GrallocMapper&>(*mMapper);
}
+ Version getMapperVersion() const { return mMapperVersion; }
+
private:
friend class Singleton<GraphicBufferMapper>;
GraphicBufferMapper();
- const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+ std::unique_ptr<const GrallocMapper> mMapper;
+
+ Version mMapperVersion;
};
// ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 0fa819d..5dc56c8 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,19 +16,28 @@
#pragma once
+#include <cinttypes>
+#include <cstdint>
+
#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
+#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
+
+namespace android {
+
+using PhysicalDisplayId = uint64_t;
+
// android::ui::* in this header file will alias different types as
// the HIDL interface is updated.
-namespace android {
namespace ui {
-using android::hardware::graphics::common::V1_0::Hdr;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
-using android::hardware::graphics::common::V1_1::PixelFormat;
using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::hardware::graphics::common::V1_2::ColorMode;
+using android::hardware::graphics::common::V1_2::Dataspace;
+using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
} // namespace ui
} // namespace android
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index 75b6705..1152cc5 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -51,9 +51,11 @@
DEPTH_POINT_CLOUD = 0x101,
RAW_DEPTH = 0x1002, // @hide
YV12 = 0x32315659,
- Y8 = 0x20203859, // @hide
+ Y8 = 0x20203859,
Y16 = 0x20363159, // @hide
- DEPTH16 = 0x44363159
+ DEPTH16 = 0x44363159,
+ DEPTH_JPEG = 0x69656963,
+ HEIC = 0x48454946,
};
/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 0bec0b7..1768805 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@
#include <ui/FloatRect.h>
#include <ui/Point.h>
+#include <ui/Size.h>
#include <android/rect.h>
@@ -78,6 +79,13 @@
bottom = static_cast<int32_t>(floatRect.bottom + 0.5f);
}
+ inline explicit Rect(const ui::Size& size) {
+ left = 0;
+ top = 0;
+ right = size.width;
+ bottom = size.height;
+ }
+
void makeInvalid();
inline void clear() {
@@ -106,6 +114,8 @@
return bottom - top;
}
+ ui::Size getSize() const { return ui::Size(getWidth(), getHeight()); }
+
__attribute__((no_sanitize("signed-integer-overflow")))
inline Rect getBounds() const {
return Rect(right - left, bottom - top);
@@ -120,7 +130,7 @@
right = rb.x;
bottom = rb.y;
}
-
+
// the following 4 functions return the 4 corners of the rect as Point
Point leftTop() const {
return Point(left, top);
@@ -175,6 +185,11 @@
Rect& offsetTo(int32_t x, int32_t y);
Rect& offsetBy(int32_t x, int32_t y);
+ /**
+ * Insets the rectangle on all sides specified by the insets.
+ */
+ Rect& inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom);
+
bool intersect(const Rect& with, Rect* result) const;
// Create a new Rect by transforming this one using a graphics HAL
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 7788452..79642ae 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -25,12 +25,13 @@
#include <ui/Rect.h>
#include <utils/Flattenable.h>
+#include <android-base/macros.h>
+
+#include <string>
+
namespace android {
// ---------------------------------------------------------------------------
-class String8;
-
-// ---------------------------------------------------------------------------
class Region : public LightFlattenable<Region>
{
public:
@@ -87,17 +88,19 @@
// these translate rhs first
Region& translateSelf(int dx, int dy);
+ Region& scaleSelf(float sx, float sy);
Region& orSelf(const Region& rhs, int dx, int dy);
Region& xorSelf(const Region& rhs, int dx, int dy);
Region& andSelf(const Region& rhs, int dx, int dy);
Region& subtractSelf(const Region& rhs, int dx, int dy);
+
// these translate rhs first
- const Region translate(int dx, int dy) const;
- const Region merge(const Region& rhs, int dx, int dy) const;
- const Region mergeExclusive(const Region& rhs, int dx, int dy) const;
- const Region intersect(const Region& rhs, int dx, int dy) const;
- const Region subtract(const Region& rhs, int dx, int dy) const;
+ const Region translate(int dx, int dy) const WARN_UNUSED;
+ const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED;
+ const Region mergeExclusive(const Region& rhs, int dx, int dy) const WARN_UNUSED;
+ const Region intersect(const Region& rhs, int dx, int dy) const WARN_UNUSED;
+ const Region subtract(const Region& rhs, int dx, int dy) const WARN_UNUSED;
// convenience operators overloads
inline const Region operator | (const Region& rhs) const;
@@ -140,8 +143,8 @@
status_t flatten(void* buffer, size_t size) const;
status_t unflatten(void const* buffer, size_t size);
- void dump(String8& out, const char* what, uint32_t flags=0) const;
- void dump(const char* what, uint32_t flags=0) const;
+ void dump(std::string& out, const char* what, uint32_t flags=0) const;
+ void dump(const char* what, uint32_t flags=0) const;
private:
class rasterizer;
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
new file mode 100644
index 0000000..c39d8af
--- /dev/null
+++ b/libs/ui/include/ui/Size.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 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 <algorithm>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+namespace android {
+namespace ui {
+
+// Forward declare a few things.
+struct Size;
+bool operator==(const Size& lhs, const Size& rhs);
+
+/**
+ * A simple value type representing a two-dimensional size
+ */
+struct Size {
+ int32_t width;
+ int32_t height;
+
+ // Special values
+ static const Size INVALID;
+ static const Size EMPTY;
+
+ // ------------------------------------------------------------------------
+ // Construction
+ // ------------------------------------------------------------------------
+
+ Size() : Size(INVALID) {}
+ template <typename T>
+ Size(T&& w, T&& h)
+ : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
+ height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
+
+ // ------------------------------------------------------------------------
+ // Accessors
+ // ------------------------------------------------------------------------
+
+ int32_t getWidth() const { return width; }
+ int32_t getHeight() const { return height; }
+
+ template <typename T>
+ void setWidth(T&& v) {
+ width = Size::clamp<int32_t, T>(std::forward<T>(v));
+ }
+ template <typename T>
+ void setHeight(T&& v) {
+ height = Size::clamp<int32_t, T>(std::forward<T>(v));
+ }
+
+ // ------------------------------------------------------------------------
+ // Assignment
+ // ------------------------------------------------------------------------
+
+ void set(const Size& size) { *this = size; }
+ template <typename T>
+ void set(T&& w, T&& h) {
+ set(Size(std::forward<T>(w), std::forward<T>(h)));
+ }
+
+ // Sets the value to INVALID
+ void makeInvalid() { set(INVALID); }
+
+ // Sets the value to EMPTY
+ void clear() { set(EMPTY); }
+
+ // ------------------------------------------------------------------------
+ // Semantic checks
+ // ------------------------------------------------------------------------
+
+ // Valid means non-negative width and height
+ bool isValid() const { return width >= 0 && height >= 0; }
+
+ // Empty means zero width and height
+ bool isEmpty() const { return *this == EMPTY; }
+
+ // ------------------------------------------------------------------------
+ // Clamp Helpers
+ // ------------------------------------------------------------------------
+
+ // Note: We use only features available in C++11 here for compatibility with
+ // external targets which include this file directly or indirectly and which
+ // themselves use C++11.
+
+ // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
+ template <typename T>
+ using remove_cv_reference_t =
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+ // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
+ // clamping the input value to the output range if necessary.
+ template <typename ToType, typename FromType>
+ static Size::remove_cv_reference_t<ToType> clamp(
+ typename std::enable_if<
+ std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
+ std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
+ FromType&&>::type v) {
+ static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
+ static constexpr auto toLowest =
+ std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
+ static constexpr auto fromHighest =
+ std::numeric_limits<remove_cv_reference_t<FromType>>::max();
+ static constexpr auto fromLowest =
+ std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+
+ // A clamp is needed if the range of FromType is not a subset of the range of ToType
+ static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+
+ // If a clamp is not needed, the conversion is just a trivial cast.
+ if (!isClampNeeded) {
+ return static_cast<ToType>(v);
+ }
+
+ // Otherwise we leverage implicit conversion to safely compare values of
+ // different types, to ensure we return a value clamped to the range of
+ // ToType.
+ return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v));
+ }
+};
+
+// ------------------------------------------------------------------------
+// Comparisons
+// ------------------------------------------------------------------------
+
+inline bool operator==(const Size& lhs, const Size& rhs) {
+ return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Size& lhs, const Size& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+inline bool operator<(const Size& lhs, const Size& rhs) {
+ // Orders by increasing width, then height.
+ if (lhs.width != rhs.width) return lhs.width < rhs.width;
+ return lhs.height < rhs.height;
+}
+
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
new file mode 100644
index 0000000..f29a370
--- /dev/null
+++ b/libs/ui/include/ui/Transform.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TRANSFORM_H
+#define ANDROID_TRANSFORM_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <string>
+
+#include <hardware/hardware.h>
+#include <math/mat4.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+
+namespace android {
+
+class Region;
+
+namespace ui {
+
+class Transform {
+public:
+ Transform();
+ Transform(const Transform& other);
+ explicit Transform(uint32_t orientation);
+ ~Transform();
+
+ enum orientation_flags {
+ ROT_0 = 0x00000000,
+ FLIP_H = HAL_TRANSFORM_FLIP_H,
+ FLIP_V = HAL_TRANSFORM_FLIP_V,
+ ROT_90 = HAL_TRANSFORM_ROT_90,
+ ROT_180 = FLIP_H|FLIP_V,
+ ROT_270 = ROT_180|ROT_90,
+ ROT_INVALID = 0x80
+ };
+
+ enum type_mask : uint32_t {
+ IDENTITY = 0,
+ TRANSLATE = 0x1,
+ ROTATE = 0x2,
+ SCALE = 0x4,
+ UNKNOWN = 0x8
+ };
+
+ // query the transform
+ bool preserveRects() const;
+ uint32_t getType() const;
+ uint32_t getOrientation() const;
+
+ const vec3& operator [] (size_t i) const; // returns column i
+ float tx() const;
+ float ty() const;
+ float sx() const;
+ float sy() 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);
+
+ // transform data
+ Rect makeBounds(int w, int h) const;
+ vec2 transform(int x, int 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;
+ // assumes the last row is < 0 , 0 , 1 >
+ vec2 transform(const vec2& v) const;
+ vec3 transform(const vec3& v) const;
+
+ // Expands from the internal 3x3 matrix to an equivalent 4x4 matrix
+ mat4 asMatrix4() const;
+
+ Transform inverse() const;
+
+ // for debugging
+ void dump(std::string& result, const char* name) const;
+ void dump(const char* name) const;
+
+private:
+ struct mat33 {
+ vec3 v[3];
+ inline const vec3& operator [] (size_t i) const { return v[i]; }
+ inline vec3& operator [] (size_t i) { return v[i]; }
+ };
+
+ enum { UNKNOWN_TYPE = 0x80000000 };
+
+ uint32_t type() const;
+ static bool absIsOne(float f);
+ static bool isZero(float f);
+
+ mat33 mMatrix;
+ mutable uint32_t mType;
+};
+
+} // namespace ui
+} // namespace android
+
+#endif /* ANDROID_TRANSFORM_H */
diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h
index fcf8ed5..d1d6014 100644
--- a/libs/ui/include/ui/UiConfig.h
+++ b/libs/ui/include/ui/UiConfig.h
@@ -17,12 +17,12 @@
#ifndef ANDROID_UI_CONFIG_H
#define ANDROID_UI_CONFIG_H
-#include <utils/String8.h>
+#include <string>
namespace android {
// Append the libui configuration details to configStr.
-void appendUiConfigString(String8& configStr);
+void appendUiConfigString(std::string& configStr);
}; // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/include_vndk/ui/ConfigStoreTypes.h
index e6ac6bf..4445ae9 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,24 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+// android::ui::* in this header file will alias different types as
+// the HIDL interface is updated.
namespace android {
-namespace mock {
+namespace ui {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+struct CieXyz {
+ float X;
+ float Y;
+ float Z;
+};
+struct DisplayPrimaries {
+ CieXyz red;
+ CieXyz green;
+ CieXyz blue;
+ CieXyz white;
+};
-} // namespace mock
-} // namespace android
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include_vndk/ui/DisplayedFrameStats.h b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
new file mode 120000
index 0000000..6014e19
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
@@ -0,0 +1 @@
+../../include/ui/DisplayedFrameStats.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Size.h b/libs/ui/include_vndk/ui/Size.h
new file mode 120000
index 0000000..fd2b21b
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Size.h
@@ -0,0 +1 @@
+../../include/ui/Size.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Transform.h b/libs/ui/include_vndk/ui/Transform.h
new file mode 120000
index 0000000..60633c2
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Transform.h
@@ -0,0 +1 @@
+../../include/ui/Transform.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index aef6428..373fa4f 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -30,7 +30,51 @@
cc_test {
name: "GraphicBuffer_test",
- shared_libs: ["libpdx_default_transport", "libui", "libutils"],
+ header_libs: [
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libcutils",
+ "libhidlbase",
+ "libhwbinder",
+ "libui",
+ "libutils",
+ ],
srcs: ["GraphicBuffer_test.cpp"],
cflags: ["-Wall", "-Werror"],
}
+
+cc_test {
+ name: "BufferHub_test",
+ header_libs: [
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+ shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libcutils",
+ "libhidlbase",
+ "libhwbinder",
+ "liblog",
+ "libui",
+ "libutils"
+ ],
+ srcs: [
+ "BufferHubBuffer_test.cpp",
+ "BufferHubEventFd_test.cpp",
+ "BufferHubMetadata_test.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
+ name: "Size_test",
+ shared_libs: ["libui"],
+ srcs: ["Size_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
new file mode 100644
index 0000000..efc1a80
--- /dev/null
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2018 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 "BufferHubBufferTest"
+
+#include <errno.h>
+#include <sys/epoll.h>
+
+#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/BufferHubBuffer.h>
+#include <ui/BufferHubEventFd.h>
+
+namespace android {
+
+namespace {
+
+using ::android::BufferHubDefs::isAnyClientAcquired;
+using ::android::BufferHubDefs::isAnyClientGained;
+using ::android::BufferHubDefs::isAnyClientPosted;
+using ::android::BufferHubDefs::isClientAcquired;
+using ::android::BufferHubDefs::isClientGained;
+using ::android::BufferHubDefs::isClientPosted;
+using ::android::BufferHubDefs::isClientReleased;
+using ::android::BufferHubDefs::kMetadataHeaderSize;
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kLayerCount = 1;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const AHardwareBuffer_Desc kDesc = {kWidth, kHeight, kLayerCount, kFormat,
+ kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+const size_t kUserMetadataSize = 1;
+
+class BufferHubBufferTest : public ::testing::Test {
+protected:
+ void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); }
+};
+
+bool cmpAHardwareBufferDesc(const AHardwareBuffer_Desc& desc, const AHardwareBuffer_Desc& other) {
+ // Not comparing stride because it's unknown before allocation
+ return desc.format == other.format && desc.height == other.height &&
+ desc.layers == other.layers && desc.usage == other.usage && desc.width == other.width;
+}
+
+class BufferHubBufferStateTransitionTest : public BufferHubBufferTest {
+protected:
+ void SetUp() override {
+ BufferHubBufferTest::SetUp();
+ CreateTwoClientsOfABuffer();
+ }
+
+ std::unique_ptr<BufferHubBuffer> b1;
+ uint32_t b1ClientMask = 0U;
+ std::unique_ptr<BufferHubBuffer> b2;
+ uint32_t b2ClientMask = 0U;
+
+private:
+ // Creates b1 and b2 as the clients of the same buffer for testing.
+ void CreateTwoClientsOfABuffer();
+};
+
+void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() {
+ b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+ ASSERT_THAT(b1, NotNull());
+ b1ClientMask = b1->clientStateMask();
+ ASSERT_NE(b1ClientMask, 0U);
+
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
+
+ b2 = BufferHubBuffer::import(token);
+ ASSERT_THAT(b2, NotNull());
+
+ b2ClientMask = b2->clientStateMask();
+ ASSERT_NE(b2ClientMask, 0U);
+ ASSERT_NE(b2ClientMask, b1ClientMask);
+}
+
+TEST_F(BufferHubBufferTest, CreateBufferFails) {
+ // Buffer Creation will fail: BLOB format requires height to be 1.
+ auto b1 = BufferHubBuffer::create(kWidth, /*height=*/2, kLayerCount,
+ /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize);
+
+ EXPECT_THAT(b1, IsNull());
+
+ // Buffer Creation will fail: user metadata size too large.
+ auto b2 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ /*userMetadataSize=*/std::numeric_limits<size_t>::max());
+
+ EXPECT_THAT(b2, IsNull());
+
+ // Buffer Creation will fail: user metadata size too large.
+ const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize;
+ auto b3 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ userMetadataSize);
+
+ EXPECT_THAT(b3, IsNull());
+}
+
+TEST_F(BufferHubBufferTest, CreateBuffer) {
+ auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ kUserMetadataSize);
+ ASSERT_THAT(b1, NotNull());
+ EXPECT_TRUE(b1->isConnected());
+ EXPECT_TRUE(b1->isValid());
+ EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), kDesc));
+ EXPECT_EQ(b1->userMetadataSize(), kUserMetadataSize);
+}
+
+TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
+ auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ kUserMetadataSize);
+ ASSERT_THAT(b1, NotNull());
+ EXPECT_TRUE(b1->isValid());
+
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
+
+ // The detached buffer should still be valid.
+ EXPECT_TRUE(b1->isConnected());
+ EXPECT_TRUE(b1->isValid());
+
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
+
+ ASSERT_THAT(b2, NotNull());
+ EXPECT_TRUE(b2->isValid());
+
+ EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), b2->desc()));
+ EXPECT_EQ(b1->userMetadataSize(), b2->userMetadataSize());
+
+ // These two buffer instances are based on the same physical buffer under the
+ // hood, so they should share the same id.
+ EXPECT_EQ(b1->id(), b2->id());
+ // We use clientStateMask() to tell those two instances apart.
+ EXPECT_NE(b1->clientStateMask(), b2->clientStateMask());
+
+ // Both buffer instances should be in released state currently.
+ EXPECT_TRUE(b1->isReleased());
+ EXPECT_TRUE(b2->isReleased());
+
+ // The event fd should behave like duped event fds.
+ const BufferHubEventFd& eventFd1 = b1->eventFd();
+ ASSERT_GE(eventFd1.get(), 0);
+ const BufferHubEventFd& eventFd2 = b2->eventFd();
+ ASSERT_GE(eventFd2.get(), 0);
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ // Add eventFd1 to epoll set, and signal eventFd2.
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ eventFd2.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ eventFd2.signal();
+ eventFd2.clear();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubBufferTest, ImportFreedBuffer) {
+ auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ kUserMetadataSize);
+ ASSERT_THAT(b1, NotNull());
+ EXPECT_TRUE(b1->isValid());
+
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
+
+ // Explicitly destroy b1. Backend buffer should be freed and token becomes invalid
+ b1.reset();
+
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
+
+ // Import should fail with INVALID_TOKEN
+ EXPECT_THAT(b2, IsNull());
+}
+
+// nullptr must not crash the service
+TEST_F(BufferHubBufferTest, ImportNullToken) {
+ auto b1 = BufferHubBuffer::import(nullptr);
+ EXPECT_THAT(b1, IsNull());
+}
+
+TEST_F(BufferHubBufferTest, ImportInvalidToken) {
+ native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+ token->data[0] = 0;
+
+ sp<NativeHandle> tokenHandle = NativeHandle::create(token, /*ownHandle=*/true);
+ auto b1 = BufferHubBuffer::import(tokenHandle);
+
+ EXPECT_THAT(b1, IsNull());
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
+ ASSERT_TRUE(b1->isReleased());
+
+ // Successful gaining the buffer should change the buffer state bit of b1 to
+ // gained state, other client state bits to released state.
+ EXPECT_EQ(b1->gain(), 0);
+ EXPECT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ auto currentBufferState = b1->bufferState();
+ ASSERT_TRUE(isClientGained(currentBufferState, b1ClientMask));
+
+ // Gaining from gained state by the same client should not return error.
+ EXPECT_EQ(b1->gain(), 0);
+
+ // Gaining from gained state by another client should return error.
+ EXPECT_EQ(b2->gain(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
+
+ // Gaining from acquired state should fail.
+ EXPECT_EQ(b1->gain(), -EBUSY);
+ EXPECT_EQ(b2->gain(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
+
+ // Gaining a buffer who has other posted client should succeed.
+ EXPECT_EQ(b1->gain(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
+
+ // A posted client should be able to gain the buffer when there is no other clients in
+ // acquired state.
+ EXPECT_EQ(b2->gain(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
+
+ EXPECT_EQ(b2->post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask));
+
+ EXPECT_EQ(b1->post(), 0);
+ auto currentBufferState = b1->bufferState();
+ EXPECT_TRUE(isClientReleased(currentBufferState, b1ClientMask));
+ EXPECT_TRUE(isClientPosted(currentBufferState, b2ClientMask));
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
+
+ // Post from posted state should fail.
+ EXPECT_EQ(b1->post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
+
+ // Posting from acquired state should fail.
+ EXPECT_EQ(b1->post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) {
+ ASSERT_TRUE(b1->isReleased());
+
+ // Posting from released state should fail.
+ EXPECT_EQ(b1->post(), -EBUSY);
+ EXPECT_EQ(b2->post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask));
+
+ // Acquire from posted state should pass.
+ EXPECT_EQ(b2->acquire(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask));
+
+ // Acquire from released state should fail, although there are other clients
+ // in posted state.
+ EXPECT_EQ(b1->acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ auto currentBufferState = b1->bufferState();
+ ASSERT_TRUE(isClientAcquired(currentBufferState, b2ClientMask));
+
+ // Acquiring from acquired state by the same client should not error out.
+ EXPECT_EQ(b2->acquire(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) {
+ ASSERT_TRUE(b1->isReleased());
+
+ // Acquiring form released state should fail.
+ EXPECT_EQ(b1->acquire(), -EBUSY);
+ EXPECT_EQ(b2->acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isAnyClientGained(b1->bufferState()));
+
+ // Acquiring from gained state should fail.
+ EXPECT_EQ(b1->acquire(), -EBUSY);
+ EXPECT_EQ(b2->acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) {
+ ASSERT_TRUE(b1->isReleased());
+
+ EXPECT_EQ(b1->release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) {
+ ASSERT_TRUE(b1->isReleased());
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_TRUE(isAnyClientGained(b1->bufferState()));
+
+ EXPECT_EQ(b1->release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_TRUE(isAnyClientPosted(b1->bufferState()));
+
+ EXPECT_EQ(b2->release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_TRUE(isAnyClientAcquired(b1->bufferState()));
+
+ EXPECT_EQ(b2->release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, BasicUsage) {
+ // 1 producer buffer and 1 consumer buffer initialised in testcase setup.
+ // Test if this set of basic operation succeed:
+ // Producer post three times to the consumer, and released by consumer.
+ for (int i = 0; i < 3; ++i) {
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+ ASSERT_EQ(b2->acquire(), 0);
+ ASSERT_EQ(b2->release(), 0);
+ }
+}
+
+TEST_F(BufferHubBufferTest, createNewConsumerAfterGain) {
+ // Create a poducer buffer and gain.
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ kUserMetadataSize);
+ ASSERT_THAT(b1, NotNull());
+ ASSERT_EQ(b1->gain(), 0);
+
+ // Create a consumer of the buffer and test if the consumer can acquire the
+ // buffer if producer posts.
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
+
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
+
+ ASSERT_THAT(b2, NotNull());
+ ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
+
+ ASSERT_EQ(b1->post(), 0);
+ EXPECT_EQ(b2->acquire(), 0);
+}
+
+TEST_F(BufferHubBufferTest, createNewConsumerAfterPost) {
+ // Create a poducer buffer and post.
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ kUserMetadataSize);
+ ASSERT_EQ(b1->gain(), 0);
+ ASSERT_EQ(b1->post(), 0);
+
+ // Create a consumer of the buffer and test if the consumer can acquire the
+ // buffer if producer posts.
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
+
+ std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
+
+ ASSERT_THAT(b2, NotNull());
+ ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
+
+ EXPECT_EQ(b2->acquire(), 0);
+}
+
+} // namespace
+
+} // namespace android
diff --git a/libs/ui/tests/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp
new file mode 100644
index 0000000..ef1781f
--- /dev/null
+++ b/libs/ui/tests/BufferHubEventFd_test.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2018 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 "BufferHubEventFdTest"
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <array>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <ui/BufferHubEventFd.h>
+
+namespace android {
+
+namespace {
+
+const int kTimeout = 100;
+const std::chrono::milliseconds kTimeoutMs(kTimeout);
+const int kTestRuns = 5;
+
+using ::testing::Contains;
+using BufferHubEventFdTest = ::testing::Test;
+
+} // namespace
+
+TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Check that it can receive consecutive signal.
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Check that it can receive consecutive signal from a duplicated eventfd.
+ BufferHubEventFd dupEventFd(dup(eventFd.get()));
+ ASSERT_TRUE(dupEventFd.isValid());
+ dupEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+ dupEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testCreateEpollFdAndAddSignaledEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+ eventFd.signal();
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ // Make sure that the epoll set has not been signal yet.
+ std::array<epoll_event, 1> events;
+ ASSERT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Check that adding an signaled fd into this epoll set will trigger the epoll set.
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testAddSignaledEventFdToEpollFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+
+ eventFd.signal();
+
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromAEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ std::array<epoll_event, 1> events;
+ for (int i = 0; i < kTestRuns; ++i) {
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+ }
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromADuplicatedEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+ base::unique_fd epollFd(epoll_create(64));
+ ASSERT_GE(epollFd.get(), 0);
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ BufferHubEventFd dupEventFd(dup(eventFd.get()));
+ ASSERT_TRUE(dupEventFd.isValid());
+
+ std::array<epoll_event, 1> events;
+ for (int i = 0; i < kTestRuns; ++i) {
+ dupEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+ }
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testClear) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+ ASSERT_GE(epollFd.get(), 0);
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ eventFd.signal();
+ eventFd.clear();
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+ ASSERT_GE(epollFd.get(), 0);
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ // Technically, the dupliated eventFd and the original eventFd are pointing
+ // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl
+ // eventFd.
+ BufferHubEventFd dupedEventFd(dup(eventFd.get()));
+ ASSERT_GE(dupedEventFd.get(), 0);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ dupedEventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ dupedEventFd.signal();
+
+ dupedEventFd.clear();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd1(epoll_create(64));
+ base::unique_fd epollFd2(epoll_create(64));
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+ ASSERT_GE(epollFd1.get(), 0);
+ ASSERT_GE(epollFd2.get(), 0);
+
+ // Register the same eventFd to two EpollFds.
+ ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+ ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ std::array<epoll_event, 1> events;
+ EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
+ EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
+
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1);
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
+ EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
+
+ eventFd.signal();
+ EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1);
+
+ eventFd.clear();
+ EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0);
+ EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) {
+ BufferHubEventFd eventFd1;
+ BufferHubEventFd eventFd2;
+
+ ASSERT_TRUE(eventFd1.isValid());
+ ASSERT_TRUE(eventFd2.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}};
+ epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}};
+
+ ASSERT_GE(epollFd.get(), 0);
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0);
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0);
+
+ std::array<epoll_event, 2> events;
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ // Signal one by one.
+ eventFd1.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(events[0].data.u32, e1.data.u32);
+
+ eventFd2.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+ EXPECT_EQ(events[0].data.u32, e2.data.u32);
+
+ // Signal both.
+ eventFd1.signal();
+ eventFd2.signal();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2);
+
+ uint32_t u32s[] = {events[0].data.u32, events[1].data.u32};
+ EXPECT_THAT(u32s, Contains(e1.data.u32));
+ EXPECT_THAT(u32s, Contains(e2.data.u32));
+
+ // The epoll fd is edge triggered, so it only responds to the eventFd once.
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0);
+
+ eventFd1.signal();
+ eventFd2.signal();
+ eventFd2.clear();
+ EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1);
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) {
+ BufferHubEventFd eventFd1;
+ BufferHubEventFd eventFd2;
+
+ ASSERT_TRUE(eventFd1.isValid());
+ ASSERT_TRUE(eventFd2.isValid());
+
+ base::unique_fd epollFd(epoll_create(64));
+ epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}};
+ epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}};
+
+ ASSERT_GE(epollFd.get(), 0);
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0);
+ ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0);
+
+ int countEvent1 = 0;
+ int countEvent2 = 0;
+ std::atomic<bool> stop{false};
+ std::mutex mx;
+ std::condition_variable cv;
+
+ std::thread pollingThread([&] {
+ std::array<epoll_event, 2> events;
+ while (true) {
+ if (stop.load()) {
+ break;
+ }
+ int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout);
+ ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
+
+ std::lock_guard<std::mutex> lock(mx);
+ for (int i = 0; i < ret; i++) {
+ if (events[i].data.u32 == e1.data.u32) {
+ countEvent1++;
+ cv.notify_one();
+ } else if (events[i].data.u32 == e2.data.u32) {
+ countEvent2++;
+ cv.notify_one();
+ }
+ }
+ }
+ });
+
+ {
+ std::unique_lock<std::mutex> lock(mx);
+
+ eventFd1.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; }));
+
+ eventFd1.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; }));
+
+ eventFd2.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; }));
+
+ eventFd1.clear();
+ eventFd2.clear();
+ EXPECT_EQ(countEvent1, 2);
+ EXPECT_EQ(countEvent2, 1);
+
+ eventFd1.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; }));
+
+ eventFd2.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; }));
+ }
+
+ stop.store(true);
+ pollingThread.join();
+}
+
+TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) {
+ BufferHubEventFd eventFd;
+ ASSERT_TRUE(eventFd.isValid());
+
+ base::unique_fd epollFd1(epoll_create(64));
+ base::unique_fd epollFd2(epoll_create(64));
+ epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}};
+
+ ASSERT_GE(epollFd1.get(), 0);
+ ASSERT_GE(epollFd2.get(), 0);
+
+ // Register the same eventFd to two EpollFds.
+ ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+ ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0);
+
+ int countEpoll1 = 0;
+ int countEpoll2 = 0;
+ std::atomic<bool> stop{false};
+ std::mutex mx;
+ std::condition_variable cv;
+
+ std::thread pollingThread1([&] {
+ std::array<epoll_event, 1> events;
+ while (!stop.load()) {
+ int ret = epoll_wait(epollFd1.get(), events.data(), events.size(), kTimeout);
+ ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
+
+ if (ret > 0) {
+ std::lock_guard<std::mutex> lock(mx);
+ countEpoll1++;
+ cv.notify_one();
+ }
+ }
+ });
+
+ std::thread pollingThread2([&] {
+ std::array<epoll_event, 1> events;
+ while (!stop.load()) {
+ int ret = epoll_wait(epollFd2.get(), events.data(), events.size(), kTimeout);
+ ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed.");
+
+ if (ret > 0) {
+ std::lock_guard<std::mutex> lock(mx);
+ countEpoll2++;
+ cv.notify_one();
+ }
+ }
+ });
+
+ {
+ std::unique_lock<std::mutex> lock(mx);
+
+ eventFd.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 1; }));
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 1; }));
+
+ eventFd.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 2; }));
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 2; }));
+
+ eventFd.clear();
+ EXPECT_EQ(countEpoll1, 2);
+ EXPECT_EQ(countEpoll2, 2);
+
+ eventFd.signal();
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 3; }));
+ EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 3; }));
+ }
+
+ stop.store(true);
+ pollingThread1.join();
+ pollingThread2.join();
+}
+
+} // namespace android
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
new file mode 100644
index 0000000..eb978ca
--- /dev/null
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 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 <ui/BufferHubMetadata.h>
+
+namespace android {
+namespace dvr {
+
+constexpr size_t kEmptyUserMetadataSize = 0;
+
+class BufferHubMetadataTest : public ::testing::Test {};
+
+TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) {
+ BufferHubMetadata m1 = BufferHubMetadata::create(std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(m1.isValid());
+}
+
+TEST_F(BufferHubMetadataTest, Create_Success) {
+ BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize);
+ EXPECT_TRUE(m1.isValid());
+ EXPECT_NE(m1.metadataHeader(), nullptr);
+}
+
+TEST_F(BufferHubMetadataTest, Import_Success) {
+ BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize);
+ EXPECT_TRUE(m1.isValid());
+ EXPECT_NE(m1.metadataHeader(), nullptr);
+
+ unique_fd h2 = unique_fd(dup(m1.ashmemFd().get()));
+ EXPECT_NE(h2.get(), -1);
+
+ BufferHubMetadata m2 = BufferHubMetadata::import(std::move(h2));
+ EXPECT_EQ(h2.get(), -1);
+ EXPECT_TRUE(m1.isValid());
+ BufferHubDefs::MetadataHeader* mh1 = m1.metadataHeader();
+ EXPECT_NE(mh1, nullptr);
+
+ // Check if the newly allocated buffer is initialized in released state (i.e.
+ // state equals to 0U).
+ EXPECT_TRUE(mh1->bufferState.load() == 0U);
+
+ EXPECT_TRUE(m2.isValid());
+ BufferHubDefs::MetadataHeader* mh2 = m2.metadataHeader();
+ EXPECT_NE(mh2, nullptr);
+
+ // Check if the newly allocated buffer is initialized in released state (i.e.
+ // state equals to 0U).
+ EXPECT_TRUE(mh2->bufferState.load() == 0U);
+}
+
+TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
+ BufferHubMetadata m1 = BufferHubMetadata::create(sizeof(int));
+ EXPECT_TRUE(m1.isValid());
+ EXPECT_NE(m1.metadataHeader(), nullptr);
+ EXPECT_NE(m1.ashmemFd().get(), -1);
+ EXPECT_EQ(m1.userMetadataSize(), sizeof(int));
+
+ BufferHubMetadata m2 = std::move(m1);
+
+ // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
+ EXPECT_EQ(m1.metadataHeader(), nullptr);
+ EXPECT_NE(m2.metadataHeader(), nullptr);
+
+ EXPECT_EQ(m1.ashmemFd().get(), -1);
+ EXPECT_NE(m2.ashmemFd().get(), -1);
+
+ EXPECT_EQ(m1.userMetadataSize(), 0U);
+ EXPECT_EQ(m2.userMetadataSize(), sizeof(int));
+
+ BufferHubMetadata m3{std::move(m2)};
+
+ // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
+ EXPECT_EQ(m2.metadataHeader(), nullptr);
+ EXPECT_NE(m3.metadataHeader(), nullptr);
+
+ EXPECT_EQ(m2.ashmemFd().get(), -1);
+ EXPECT_NE(m3.ashmemFd().get(), -1);
+
+ EXPECT_EQ(m2.userMetadataSize(), 0U);
+ EXPECT_EQ(m3.userMetadataSize(), sizeof(int));
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index eb679ac..c767ce0 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "GraphicBufferTest"
-#include <ui/DetachedBufferHandle.h>
+#include <ui/BufferHubBuffer.h>
#include <ui/GraphicBuffer.h>
#include <gtest/gtest.h>
@@ -35,30 +35,43 @@
class GraphicBufferTest : public testing::Test {};
-TEST_F(GraphicBufferTest, DetachedBuffer) {
- sp<GraphicBuffer> buffer(
+TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ kTestUsage, /*userMetadataSize=*/0);
+ ASSERT_NE(b1, nullptr);
+ EXPECT_TRUE(b1->isValid());
+
+ sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+ EXPECT_TRUE(gb->isBufferHubBuffer());
+
+ EXPECT_EQ(gb->getWidth(), kTestWidth);
+ EXPECT_EQ(gb->getHeight(), kTestHeight);
+ EXPECT_EQ(static_cast<uint32_t>(gb->getPixelFormat()), kTestFormat);
+ EXPECT_EQ(gb->getUsage(), kTestUsage);
+ EXPECT_EQ(gb->getLayerCount(), kTestLayerCount);
+}
+
+TEST_F(GraphicBufferTest, InvalidBufferIdForNoneBufferHubBuffer) {
+ sp<GraphicBuffer> gb(
new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage));
+ EXPECT_FALSE(gb->isBufferHubBuffer());
+ EXPECT_EQ(gb->getBufferId(), -1);
+}
- // Currently a newly allocated GraphicBuffer is in legacy mode, i.e. not associated with
- // BufferHub. But this may change in the future.
- EXPECT_FALSE(buffer->isDetachedBuffer());
+TEST_F(GraphicBufferTest, BufferIdMatchesBufferHubBufferId) {
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ kTestUsage, /*userMetadataSize=*/0);
+ EXPECT_NE(b1, nullptr);
+ EXPECT_TRUE(b1->isValid());
- pdx::LocalChannelHandle channel{nullptr, 1234};
- EXPECT_TRUE(channel.valid());
+ int b1_id = b1->id();
+ EXPECT_GE(b1_id, 0);
- std::unique_ptr<DetachedBufferHandle> handle = DetachedBufferHandle::Create(std::move(channel));
- EXPECT_FALSE(channel.valid());
- EXPECT_TRUE(handle->isValid());
- EXPECT_TRUE(handle->handle().valid());
-
- buffer->setDetachedBufferHandle(std::move(handle));
- EXPECT_TRUE(handle == nullptr);
- EXPECT_TRUE(buffer->isDetachedBuffer());
-
- handle = buffer->takeDetachedBufferHandle();
- EXPECT_TRUE(handle != nullptr);
- EXPECT_TRUE(handle->isValid());
- EXPECT_FALSE(buffer->isDetachedBuffer());
+ sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+ EXPECT_TRUE(gb->isBufferHubBuffer());
+ EXPECT_EQ(gb->getBufferId(), b1_id);
}
} // namespace android
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
new file mode 100644
index 0000000..69e1ac8
--- /dev/null
+++ b/libs/ui/tests/Size_test.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "SizeTest"
+
+#include <cmath>
+#include <cstdlib>
+
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace ui {
+
+TEST(SizeTest, BasicConstructionAndEqualityComparison) {
+ Size s(123, 456);
+
+ EXPECT_EQ(123, s.width);
+ EXPECT_EQ(123, s.getWidth());
+
+ EXPECT_EQ(456, s.height);
+ EXPECT_EQ(456, s.getHeight());
+
+ EXPECT_EQ(Size(123, 456), s);
+ EXPECT_NE(Size(456, 123), s);
+}
+
+TEST(SizeTest, BasicLessThanComparison) {
+ EXPECT_TRUE(Size(0, 1) < Size(2, 3));
+ EXPECT_FALSE(Size(2, 3) < Size(0, 1));
+
+ EXPECT_TRUE(Size(0, 3) < Size(2, 1));
+ EXPECT_FALSE(Size(2, 1) < Size(0, 3));
+
+ EXPECT_TRUE(Size(0, 1) < Size(0, 3));
+ EXPECT_FALSE(Size(0, 3) < Size(0, 1));
+
+ EXPECT_FALSE(Size(1, 1) < Size(1, 1));
+}
+
+TEST(SizeTest, ValidAndEmpty) {
+ {
+ Size s;
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ Size s(-1, -1);
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ Size s(1, -1000);
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ Size s(-1000, 1);
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ Size s(-1000, -1000);
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ const auto& s = Size::INVALID;
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ Size s(123, 456);
+ s.makeInvalid();
+ EXPECT_FALSE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+
+ {
+ Size s(0, 0);
+ EXPECT_TRUE(s.isValid());
+ EXPECT_TRUE(s.isEmpty());
+ }
+
+ {
+ const auto& s = Size::EMPTY;
+ EXPECT_TRUE(s.isValid());
+ EXPECT_TRUE(s.isEmpty());
+ }
+
+ {
+ Size s(123, 456);
+ s.clear();
+ EXPECT_TRUE(s.isValid());
+ EXPECT_TRUE(s.isEmpty());
+ }
+
+ {
+ Size s(123, 456);
+ EXPECT_TRUE(s.isValid());
+ EXPECT_FALSE(s.isEmpty());
+ }
+}
+
+TEST(SizeTest, Set) {
+ {
+ Size s;
+ s.setWidth(0);
+ EXPECT_EQ(Size(0, -1), s);
+ }
+
+ {
+ Size s;
+ s.setHeight(0);
+ EXPECT_EQ(Size(-1, 0), s);
+ }
+
+ {
+ Size s;
+ s.set(123, 456);
+ EXPECT_EQ(Size(123, 456), s);
+ }
+}
+
+template <typename T, typename U>
+void ClampTest(T input, U expected) {
+ // The constructor, set(), setWidth() and setHeight() all allow arbitrary
+ // conversions from other numeric types, and implement clamping if necessary.
+
+ EXPECT_EQ(Size(expected, expected), Size(input, input));
+
+ {
+ Size s;
+ s.set(input, input);
+ EXPECT_EQ(Size(expected, expected), s);
+ }
+
+ {
+ Size s;
+ s.setWidth(input);
+ EXPECT_EQ(expected, s.width);
+ }
+
+ {
+ Size s;
+ s.setHeight(input);
+ EXPECT_EQ(expected, s.height);
+ }
+}
+
+TEST(SizeTest, Int8RangeIsNotClamped) {
+ ClampTest(std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::max());
+ ClampTest(int8_t(0), int8_t(0));
+ ClampTest(std::numeric_limits<int8_t>::lowest(), std::numeric_limits<int8_t>::lowest());
+}
+
+TEST(SizeTest, FloatRangeIsClamped) {
+ ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(float(0), int32_t(0));
+ ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
+}
+
+} // namespace ui
+} // namespace android
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
new file mode 100644
index 0000000..e95a080
--- /dev/null
+++ b/libs/vibrator/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2018 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: "libvibrator",
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libaudio_system_headers",
+ ],
+
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ local_include_dirs: ["include/"],
+ export_aidl_headers: true,
+ },
+
+ srcs: [
+ ":libvibrator_aidl",
+ "*.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wno-unused-parameter",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
new file mode 100644
index 0000000..f6fc19e
--- /dev/null
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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 <vibrator/ExternalVibration.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) {
+ out->writeInt32(attrs.usage);
+ out->writeInt32(attrs.content_type);
+ out->writeInt32(attrs.source);
+ out->writeInt32(attrs.flags);
+}
+
+void readAudioAttributes(audio_attributes_t* attrs, const android::Parcel* in) {
+ attrs->usage = static_cast<audio_usage_t>(in->readInt32());
+ attrs->content_type = static_cast<audio_content_type_t>(in->readInt32());
+ attrs->source = static_cast<audio_source_t>(in->readInt32());
+ attrs->flags = static_cast<audio_flags_mask_t>(in->readInt32());
+}
+
+namespace android {
+namespace os {
+
+ExternalVibration::ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+ sp<IExternalVibrationController> controller) :
+ mUid(uid), mPkg(pkg), mAttrs(attrs), mController(controller) { }
+
+status_t ExternalVibration::writeToParcel(Parcel* parcel) const {
+ parcel->writeInt32(mUid);
+ parcel->writeString16(String16(mPkg.c_str()));
+ writeAudioAttributes(mAttrs, parcel);
+ parcel->writeStrongBinder(IInterface::asBinder(mController));
+ parcel->writeStrongBinder(mToken);
+ return OK;
+}
+status_t ExternalVibration::readFromParcel(const Parcel* parcel) {
+ mUid = parcel->readInt32();
+ String8 pkgStr8 = String8(parcel->readString16());
+ mPkg = pkgStr8.c_str();
+ readAudioAttributes(&mAttrs, parcel);
+ mController = IExternalVibrationController::asInterface(parcel->readStrongBinder());
+ mToken = parcel->readStrongBinder();
+ return OK;
+}
+
+inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const {
+ return mToken == rhs.mToken;
+}
+
+} // namespace os
+} // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
new file mode 100644
index 0000000..c6eb3d1
--- /dev/null
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_EXTERNAL_VIBRATION_H
+#define ANDROID_EXTERNAL_VIBRATION_H
+
+#include <android/os/IExternalVibrationController.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcelable.h>
+#include <system/audio.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+class ExternalVibration : public Parcelable, public virtual RefBase {
+public :
+ ExternalVibration() = default;
+ ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
+ sp<IExternalVibrationController> controller);
+ virtual ~ExternalVibration() = default;
+ ExternalVibration(const ExternalVibration&) = default;
+
+ bool operator==(const ExternalVibration&) const;
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ int32_t getUid() const { return mUid; }
+ std::string getPackage() const { return mPkg; }
+ audio_attributes_t getAudioAttributes() const { return mAttrs; }
+ sp<IExternalVibrationController> getController() { return mController; }
+
+
+private:
+ int32_t mUid;
+ std::string mPkg;
+ audio_attributes_t mAttrs;
+ sp<IExternalVibrationController> mController;
+ sp<IBinder> mToken = new BBinder();
+};
+
+} // namespace android
+} // namespace os
+
+#endif // ANDROID_EXTERNAL_VIBRATION_H
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 69b6422..fa92830 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -12,20 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-sourceFiles = [
- "buffer_hub_client.cpp",
- "buffer_hub_rpc.cpp",
- "detached_buffer.cpp",
- "ion_buffer.cpp",
-]
+cc_library_headers {
+ name: "libbufferhub_headers",
+ export_include_dirs: ["include"],
+ vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor.
+}
-localIncludeFiles = [
- "include",
+sourceFiles = [
+ "buffer_hub_base.cpp",
+ "buffer_hub_rpc.cpp",
+ "consumer_buffer.cpp",
+ "ion_buffer.cpp",
+ "producer_buffer.cpp",
]
sharedLibraries = [
"libbase",
- "libbinder",
"libcutils",
"libhardware",
"liblog",
@@ -36,6 +38,7 @@
]
headerLibraries = [
+ "libbufferhub_headers",
"libdvr_headers",
"libnativebase_headers",
]
@@ -49,13 +52,16 @@
"-Wall",
"-Werror",
],
- export_include_dirs: localIncludeFiles,
shared_libs: sharedLibraries,
header_libs: headerLibraries,
name: "libbufferhub",
export_header_lib_headers: [
+ "libbufferhub_headers",
"libnativebase_headers",
],
+
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
}
cc_test {
@@ -64,5 +70,7 @@
shared_libs: sharedLibraries,
header_libs: headerLibraries,
name: "buffer_hub-test",
-}
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
+}
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index e247398..27ab024 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -1,15 +1,16 @@
#include <gtest/gtest.h>
#include <poll.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/detached_buffer.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
-#include <ui/DetachedBufferHandle.h>
+#include <ui/BufferHubDefs.h>
#include <mutex>
#include <thread>
+namespace {
#define RETRY_EINTR(fnc_call) \
([&]() -> decltype(fnc_call) { \
decltype(fnc_call) result; \
@@ -19,102 +20,95 @@
return result; \
})()
-using android::GraphicBuffer;
-using android::sp;
-using android::dvr::BufferConsumer;
-using android::dvr::BufferProducer;
-using android::dvr::DetachedBuffer;
-using android::dvr::BufferHubDefs::IsBufferAcquired;
-using android::dvr::BufferHubDefs::IsBufferGained;
-using android::dvr::BufferHubDefs::IsBufferPosted;
-using android::dvr::BufferHubDefs::IsBufferReleased;
-using android::dvr::BufferHubDefs::kConsumerStateMask;
-using android::dvr::BufferHubDefs::kMetadataHeaderSize;
-using android::dvr::BufferHubDefs::kProducerStateBit;
-using android::pdx::LocalChannelHandle;
+using android::BufferHubDefs::isAnyClientAcquired;
+using android::BufferHubDefs::isAnyClientGained;
+using android::BufferHubDefs::isAnyClientPosted;
+using android::BufferHubDefs::isClientAcquired;
+using android::BufferHubDefs::isClientPosted;
+using android::BufferHubDefs::isClientReleased;
+using android::BufferHubDefs::kFirstClientBitMask;
+using android::dvr::ConsumerBuffer;
+using android::dvr::ProducerBuffer;
using android::pdx::LocalHandle;
using android::pdx::Status;
+using LibBufferHubTest = ::testing::Test;
const int kWidth = 640;
const int kHeight = 480;
-const int kLayerCount = 1;
const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
const int kUsage = 0;
-const size_t kUserMetadataSize = 0;
-const uint64_t kContext = 42;
-const size_t kMaxConsumerCount = 63;
+// Maximum number of consumers for the buffer that only has one producer in the
+// test.
+const size_t kMaxConsumerCount =
+ android::BufferHubDefs::kMaxNumberOfClients - 1;
const int kPollTimeoutMs = 100;
-using LibBufferHubTest = ::testing::Test;
+// Helper function to poll the eventfd in BufferHubBase.
+template <class BufferHubBase>
+int PollBufferEvent(const std::unique_ptr<BufferHubBase>& buffer,
+ int timeout_ms = kPollTimeoutMs) {
+ pollfd p = {buffer->event_fd(), POLLIN, 0};
+ return poll(&p, 1, timeout_ms);
+}
+
+} // namespace
TEST_F(LibBufferHubTest, TestBasicUsage) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
- ASSERT_TRUE(c.get() != nullptr);
+ std::unique_ptr<ConsumerBuffer> c1 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c1.get() != nullptr);
// Check that consumers can spawn other consumers.
- std::unique_ptr<BufferConsumer> c2 =
- BufferConsumer::Import(c->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c2 =
+ ConsumerBuffer::Import(c1->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
- // Producer state mask is unique, i.e. 1.
- EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit);
- // Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0U);
- // Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c->buffer_state_bit(), 0U);
- EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0U);
- // Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0U);
- // Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c2->buffer_state_bit(), 0U);
- EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0U);
- // Each consumer should have unique bit.
- EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0U);
+ // Checks the state masks of client p, c1 and c2.
+ EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask);
+ EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1);
+ EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2);
// Initial state: producer not available, consumers not available.
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
- EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_EQ(0, p->Post(LocalHandle()));
// New state: producer not available, consumers available.
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c2)));
- uint64_t context;
LocalHandle fence;
- EXPECT_EQ(0, c->Acquire(&fence, &context));
- EXPECT_EQ(kContext, context);
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->Acquire(&fence));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c2)));
- EXPECT_EQ(0, c2->Acquire(&fence, &context));
- EXPECT_EQ(kContext, context);
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c2->Acquire(&fence));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
- EXPECT_EQ(0, c->Release(LocalHandle()));
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->Release(LocalHandle()));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, c2->Discard());
+ EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(p)));
- EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, p->Gain(&fence));
- EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
}
TEST_F(LibBufferHubTest, TestEpoll) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)};
@@ -146,8 +140,9 @@
// No events should be signaled initially.
ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0));
- // Post the producer and check for consumer signal.
- EXPECT_EQ(0, p->Post({}, kContext));
+ // Gain and post the producer and check for consumer signal.
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_EQ(0, p->Post({}));
ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(),
kPollTimeoutMs));
ASSERT_TRUE(events[0].events & EPOLLIN);
@@ -177,21 +172,21 @@
}
TEST_F(LibBufferHubTest, TestStateMask) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
// It's ok to create up to kMaxConsumerCount consumer buffers.
- uint64_t buffer_state_bits = p->buffer_state_bit();
- std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+ uint32_t client_state_masks = p->client_state_mask();
+ std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
for (size_t i = 0; i < kMaxConsumerCount; i++) {
- cs[i] = BufferConsumer::Import(p->CreateConsumer());
+ cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
// Expect all buffers have unique state mask.
- EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U);
- buffer_state_bits |= cs[i]->buffer_state_bit();
+ EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
+ client_state_masks |= cs[i]->client_state_mask();
}
- EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
+ EXPECT_EQ(client_state_masks, ~0U);
// The 64th creation will fail with out-of-memory error.
auto state = p->CreateConsumer();
@@ -199,106 +194,93 @@
// Release any consumer should allow us to re-create.
for (size_t i = 0; i < kMaxConsumerCount; i++) {
- buffer_state_bits &= ~cs[i]->buffer_state_bit();
+ client_state_masks &= ~cs[i]->client_state_mask();
cs[i] = nullptr;
- cs[i] = BufferConsumer::Import(p->CreateConsumer());
+ cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
// The released state mask will be reused.
- EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U);
- buffer_state_bits |= cs[i]->buffer_state_bit();
- EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask);
+ EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
+ client_state_masks |= cs[i]->client_state_mask();
}
}
TEST_F(LibBufferHubTest, TestStateTransitions) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- uint64_t context;
LocalHandle fence;
+ EXPECT_EQ(0, p->GainAsync());
- // The producer buffer starts in gained state.
-
- // Acquire, release, and gain in gained state should fail.
- EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EALREADY, p->Gain(&fence));
+ // Acquire in gained state should fail.
+ EXPECT_EQ(-EBUSY, c->Acquire(&fence));
// Post in gained state should succeed.
- EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+ EXPECT_EQ(0, p->Post(LocalHandle()));
- // Post, release, and gain in posted state should fail.
- EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
+ // Post and gain in posted state should fail.
+ EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
EXPECT_EQ(-EBUSY, p->Gain(&fence));
// Acquire in posted state should succeed.
- EXPECT_LE(0, c->Acquire(&fence, &context));
+ EXPECT_EQ(0, c->Acquire(&fence));
// Acquire, post, and gain in acquired state should fail.
- EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
- EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext));
+ EXPECT_EQ(-EBUSY, c->Acquire(&fence));
+ EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
EXPECT_EQ(-EBUSY, p->Gain(&fence));
// Release in acquired state should succeed.
EXPECT_EQ(0, c->Release(LocalHandle()));
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
- // Release, acquire, and post in released state should fail.
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
- EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext));
+ // Acquire and post in released state should fail.
+ EXPECT_EQ(-EBUSY, c->Acquire(&fence));
+ EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
// Gain in released state should succeed.
EXPECT_EQ(0, p->Gain(&fence));
- // Acquire, release, and gain in gained state should fail.
- EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EALREADY, p->Gain(&fence));
+ // Acquire in gained state should fail.
+ EXPECT_EQ(-EBUSY, c->Acquire(&fence));
}
TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
+ EXPECT_EQ(0, p->GainAsync());
- // The producer buffer starts in gained state.
-
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
// Post in gained state should succeed.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientPosted(p->buffer_state()));
- // Post, release, and gain in posted state should fail.
+ // Post and gain in posted state should fail.
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
// Acquire in posted state should succeed.
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientAcquired(p->buffer_state()));
// Acquire, post, and gain in acquired state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -309,12 +291,11 @@
// Release in acquired state should succeed.
EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_TRUE(p->is_released());
- // Release, acquire, and post in released state should fail.
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ // Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
@@ -323,75 +304,114 @@
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
- // Acquire, release, and gain in gained state should fail.
+ // Acquire and gain in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_FALSE(invalid_fence.IsValid());
}
-TEST_F(LibBufferHubTest, TestZeroConsumer) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->GainAsync());
+}
+
+TEST_F(LibBufferHubTest, TestGainPostedBuffer) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->Post(LocalHandle()));
+ ASSERT_TRUE(isAnyClientPosted(p->buffer_state()));
+
+ // Gain in posted state should only succeed with gain_posted_buffer = true.
+ LocalHandle invalid_fence;
+ EXPECT_EQ(-EBUSY, p->Gain(&invalid_fence, false));
+ EXPECT_EQ(0, p->Gain(&invalid_fence, true));
+}
+
+TEST_F(LibBufferHubTest, TestGainPostedBufferAsync) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->Post(LocalHandle()));
+ ASSERT_TRUE(isAnyClientPosted(p->buffer_state()));
+
+ // GainAsync in posted state should only succeed with gain_posted_buffer
+ // equals true.
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
+ EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence, false));
+ EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true));
+}
- // Newly created.
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
- EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->Post(LocalHandle()));
+ // Producer state bit is in released state after post, other clients shall be
+ // in posted state although there is no consumer of this buffer yet.
+ ASSERT_TRUE(isClientReleased(p->buffer_state(), p->client_state_mask()));
+ ASSERT_TRUE(p->is_released());
+ ASSERT_TRUE(isAnyClientPosted(p->buffer_state()));
- // The buffer should stay in posted stay until a consumer picks it up.
- EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
- // A new consumer should still be able to acquire the buffer immediately.
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
- ASSERT_TRUE(c.get() != nullptr);
- EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ // Gain in released state should succeed.
+ LocalHandle invalid_fence;
+ EXPECT_EQ(0, p->Gain(&invalid_fence, false));
}
TEST_F(LibBufferHubTest, TestMaxConsumers) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
+ uint32_t producer_state_mask = p->client_state_mask();
- std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
- for (size_t i = 0; i < kMaxConsumerCount; i++) {
- cs[i] = BufferConsumer::Import(p->CreateConsumer());
+ std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
+ for (size_t i = 0; i < kMaxConsumerCount; ++i) {
+ cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
- EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+ EXPECT_TRUE(cs[i]->is_released());
+ EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
}
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the producer should trigger all consumers to be available.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
- for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_TRUE(isClientReleased(p->buffer_state(), p->client_state_mask()));
+ for (size_t i = 0; i < kMaxConsumerCount; ++i) {
EXPECT_TRUE(
- IsBufferPosted(cs[i]->buffer_state(), cs[i]->buffer_state_bit()));
- EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs)));
+ isClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(cs[i])));
EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ EXPECT_TRUE(
+ isClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
}
// All consumers have to release before the buffer is considered to be
// released.
for (size_t i = 0; i < kMaxConsumerCount; i++) {
- EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+ EXPECT_FALSE(p->is_released());
EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
}
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_TRUE(p->is_released());
// Buffer state cross all clients must be consistent.
for (size_t i = 0; i < kMaxConsumerCount; i++) {
@@ -400,64 +420,68 @@
}
TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(c->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer should signal already created consumer.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_TRUE(isAnyClientPosted(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_TRUE(isAnyClientAcquired(c->buffer_state()));
}
-TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer before any consumer gets created.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(p->is_released());
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p)));
- // Newly created consumer should be automatically sigalled.
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ // Newly created consumer will be signalled for the posted buffer although it
+ // is created after producer posting.
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+ EXPECT_TRUE(isClientPosted(c->buffer_state(), c->client_state_mask()));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
}
TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c1 =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c1 =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c1.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post, acquire, and release the buffer..
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1)));
EXPECT_EQ(0, c1->AcquireAsync(&metadata, &invalid_fence));
EXPECT_EQ(0, c1->ReleaseAsync(&metadata, invalid_fence));
@@ -465,18 +489,18 @@
// executed before Release impulse gets executed by bufferhubd. Thus, here we
// need to wait until the releasd is confirmed before creating another
// consumer.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
+ EXPECT_TRUE(p->is_released());
// Create another consumer immediately after the release, should not make the
// buffer un-released.
- std::unique_ptr<BufferConsumer> c2 =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c2 =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
- EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_TRUE(p->is_released());
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_TRUE(isAnyClientGained(p->buffer_state()));
}
TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -484,25 +508,23 @@
int64_t field1;
int64_t field2;
};
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
-
+ EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
- EXPECT_EQ(0, p->Post(LocalHandle(), m));
- EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
-
+ EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata)));
+ EXPECT_LE(0, RETRY_EINTR(PollBufferEvent(c)));
LocalHandle fence;
Metadata m2 = {};
- EXPECT_EQ(0, c->Acquire(&fence, &m2));
+ EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2)));
EXPECT_EQ(m.field1, m2.field1);
EXPECT_EQ(m.field2, m2.field2);
-
EXPECT_EQ(0, c->Release(LocalHandle()));
- EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p, /*timeout_ms=*/0)));
}
TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
@@ -515,23 +537,23 @@
int64_t field2;
int64_t field3;
};
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
// It is illegal to post metadata larger than originally requested during
// buffer allocation.
OverSizedMetadata evil_meta = {};
- EXPECT_NE(0, p->Post(LocalHandle(), evil_meta));
- EXPECT_GE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata)));
+ EXPECT_GE(0, RETRY_EINTR(PollBufferEvent(c)));
// It is ok to post metadata smaller than originally requested during
// buffer allocation.
- int64_t sequence = 42;
- EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+ EXPECT_EQ(0, p->Post(LocalHandle()));
}
TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
@@ -544,15 +566,16 @@
int64_t field2;
int64_t field3;
};
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
- EXPECT_EQ(0, p->Post(LocalHandle(), m));
+ EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m)));
LocalHandle fence;
int64_t sequence;
@@ -560,53 +583,56 @@
// It is illegal to acquire metadata larger than originally requested during
// buffer allocation.
- EXPECT_NE(0, c->Acquire(&fence, &e));
+ EXPECT_NE(0, c->Acquire(&fence, &e, sizeof(e)));
// It is ok to acquire metadata smaller than originally requested during
// buffer allocation.
- EXPECT_EQ(0, c->Acquire(&fence, &sequence));
+ EXPECT_EQ(0, c->Acquire(&fence, &sequence, sizeof(sequence)));
EXPECT_EQ(m.field1, sequence);
}
TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
int64_t sequence = 3;
- EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+ EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
LocalHandle fence;
EXPECT_EQ(0, c->Acquire(&fence));
}
TEST_F(LibBufferHubTest, TestWithNoMeta) {
- std::unique_ptr<BufferProducer> p =
- BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ std::unique_ptr<ProducerBuffer> p =
+ ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage);
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
LocalHandle fence;
- EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+ EXPECT_EQ(0, p->Post(LocalHandle()));
EXPECT_EQ(0, c->Acquire(&fence));
}
TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
- std::unique_ptr<BufferProducer> p =
- BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ std::unique_ptr<ProducerBuffer> p =
+ ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage);
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
int64_t sequence = 3;
- EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+ EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
}
namespace {
@@ -619,12 +645,13 @@
} // namespace
TEST_F(LibBufferHubTest, TestAcquireFence) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0);
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
@@ -634,7 +661,7 @@
// Should acquire a valid fence.
LocalHandle f2;
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&meta, &f2));
EXPECT_TRUE(f2.IsValid());
// The original fence and acquired fence should have different fd number.
@@ -651,7 +678,7 @@
// Should gain an invalid fence.
LocalHandle f3;
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, p->GainAsync(&meta, &f3));
EXPECT_FALSE(f3.IsValid());
@@ -660,7 +687,7 @@
// Should acquire a valid fence and it's already signalled.
LocalHandle f4;
- EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c)));
EXPECT_EQ(0, c->AcquireAsync(&meta, &f4));
EXPECT_TRUE(f4.IsValid());
EXPECT_LT(0, PollFd(f4.Get(), kPollTimeoutMs));
@@ -673,66 +700,119 @@
// Should gain a valid fence, which is already signaled.
LocalHandle f6;
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
EXPECT_EQ(0, p->GainAsync(&meta, &f6));
EXPECT_TRUE(f6.IsValid());
EXPECT_LT(0, PollFd(f6.Get(), kPollTimeoutMs));
}
TEST_F(LibBufferHubTest, TestOrphanedAcquire) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<BufferConsumer> c1 =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c1 =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c1.get() != nullptr);
- const uint64_t consumer_state_bit1 = c1->buffer_state_bit();
+ const uint32_t client_state_mask1 = c1->client_state_mask();
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
LocalHandle fence;
- EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_LE(0, c1->AcquireAsync(&meta, &fence));
- // Destroy the consumer now will make it orphaned and the buffer is still
- // acquired.
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1)));
+ EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence));
+
+ // Destroy the consumer who has acquired but not released the buffer.
c1 = nullptr;
- EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
- std::unique_ptr<BufferConsumer> c2 =
- BufferConsumer::Import(p->CreateConsumer());
- ASSERT_TRUE(c2.get() != nullptr);
- const uint64_t consumer_state_bit2 = c2->buffer_state_bit();
- EXPECT_NE(consumer_state_bit1, consumer_state_bit2);
-
- // The new consumer is available for acquire.
- EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_LE(0, c2->AcquireAsync(&meta, &fence));
- // Releasing the consumer makes the buffer gainable.
- EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle()));
// The buffer is now available for the producer to gain.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
- // But if another consumer is created in released state.
- std::unique_ptr<BufferConsumer> c3 =
- BufferConsumer::Import(p->CreateConsumer());
+ // Newly added consumer is not able to acquire the buffer.
+ std::unique_ptr<ConsumerBuffer> c2 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+ const uint32_t client_state_mask2 = c2->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask2);
+ EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2)));
+ EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence));
+
+ // Producer should be able to gain.
+ EXPECT_EQ(0, p->GainAsync(&meta, &fence, false));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireLastPosted) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<ConsumerBuffer> c1 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c1.get() != nullptr);
+ const uint32_t client_state_mask1 = c1->client_state_mask();
+
+ EXPECT_EQ(0, p->GainAsync());
+ DvrNativeBufferMetadata meta;
+ EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1)));
+
+ // c2 is created when the buffer is in posted state. buffer state for c1 is
+ // posted. Thus, c2 should be automatically set to posted and able to acquire.
+ std::unique_ptr<ConsumerBuffer> c2 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+ const uint32_t client_state_mask2 = c2->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask2);
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c2)));
+ LocalHandle invalid_fence;
+ EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence));
+
+ EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence));
+
+ // c3 is created when the buffer is in acquired state. buffer state for c1 and
+ // c2 are acquired. Thus, c3 should be automatically set to posted and able to
+ // acquire.
+ std::unique_ptr<ConsumerBuffer> c3 =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c3.get() != nullptr);
- const uint64_t consumer_state_bit3 = c3->buffer_state_bit();
- EXPECT_NE(consumer_state_bit2, consumer_state_bit3);
- // The consumer buffer is not acquirable.
- EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
- EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence));
+ const uint32_t client_state_mask3 = c3->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask3);
+ EXPECT_NE(client_state_mask2, client_state_mask3);
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c3)));
+ EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence));
- // Producer should be able to gain no matter what.
- EXPECT_EQ(0, p->GainAsync(&meta, &fence));
+ // Releasing c2 and c3 in normal ways.
+ EXPECT_EQ(0, c2->Release(LocalHandle()));
+ EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle()));
+
+ // Destroy the c1 who has not released the buffer.
+ c1 = nullptr;
+
+ // The buffer is now available for the producer to gain.
+ EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p)));
+
+ // C4 is created in released state. Thus, it cannot gain the just posted
+ // buffer.
+ std::unique_ptr<ConsumerBuffer> c4 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c4.get() != nullptr);
+ const uint32_t client_state_mask4 = c4->client_state_mask();
+ EXPECT_NE(client_state_mask3, client_state_mask4);
+ EXPECT_GE(0, RETRY_EINTR(PollBufferEvent(c3)));
+ EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence));
+
+ // Producer should be able to gain.
+ EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence));
}
TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
- std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ // TODO(b/112338294) rewrite test after migration
+ return;
+
+ /* std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
- std::unique_ptr<BufferConsumer> c =
- BufferConsumer::Import(p->CreateConsumer());
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(p.get() != nullptr);
ASSERT_TRUE(c.get() != nullptr);
@@ -741,8 +821,9 @@
int p_id = p->id();
// Detach in posted state should fail.
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(c)), 0);
auto s1 = p->Detach();
EXPECT_FALSE(s1);
@@ -753,7 +834,7 @@
// Detach in released state should fail.
EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(p)), 0);
s1 = p->Detach();
EXPECT_FALSE(s1);
@@ -766,12 +847,12 @@
EXPECT_TRUE(handle.valid());
// Both producer and consumer should have hangup.
- EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(p)), 0);
auto s2 = p->GetEventMask(POLLHUP);
EXPECT_TRUE(s2);
EXPECT_EQ(s2.get(), POLLHUP);
- EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+ EXPECT_GT(RETRY_EINTR(PollBufferEvent(c)), 0);
s2 = p->GetEventMask(POLLHUP);
EXPECT_TRUE(s2);
EXPECT_EQ(s2.get(), POLLHUP);
@@ -789,159 +870,37 @@
// is gone.
EXPECT_EQ(s3.error(), EPIPE);
- // Detached buffer handle can be use to construct a new DetachedBuffer object.
- auto d = DetachedBuffer::Import(std::move(handle));
+ // Detached buffer handle can be use to construct a new BufferHubBuffer
+ // object.
+ auto d = BufferHubBuffer::Import(std::move(handle));
EXPECT_FALSE(handle.valid());
EXPECT_TRUE(d->IsConnected());
EXPECT_TRUE(d->IsValid());
- ASSERT_TRUE(d->buffer() != nullptr);
- EXPECT_EQ(d->buffer()->initCheck(), 0);
- EXPECT_EQ(d->id(), p_id);
+ EXPECT_EQ(d->id(), p_id); */
}
-TEST_F(LibBufferHubTest, TestCreateDetachedBufferFails) {
- // Buffer Creation will fail: BLOB format requires height to be 1.
- auto b1 = DetachedBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount,
- /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage,
- kUserMetadataSize);
+TEST_F(LibBufferHubTest, TestDetach) {
+ // TODO(b/112338294) rewrite test after migration
+ return;
- EXPECT_FALSE(b1->IsConnected());
- EXPECT_FALSE(b1->IsValid());
- EXPECT_TRUE(b1->buffer() == nullptr);
-
- // Buffer Creation will fail: user metadata size too large.
- auto b2 = DetachedBuffer::Create(
- kWidth, kHeight, kLayerCount, kFormat, kUsage,
- /*user_metadata_size=*/std::numeric_limits<size_t>::max());
-
- EXPECT_FALSE(b2->IsConnected());
- EXPECT_FALSE(b2->IsValid());
- EXPECT_TRUE(b2->buffer() == nullptr);
-
- // Buffer Creation will fail: user metadata size too large.
- auto b3 = DetachedBuffer::Create(
- kWidth, kHeight, kLayerCount, kFormat, kUsage,
- /*user_metadata_size=*/std::numeric_limits<size_t>::max() -
- kMetadataHeaderSize);
-
- EXPECT_FALSE(b3->IsConnected());
- EXPECT_FALSE(b3->IsValid());
- EXPECT_TRUE(b3->buffer() == nullptr);
-}
-
-TEST_F(LibBufferHubTest, TestCreateDetachedBuffer) {
- auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat,
- kUsage, kUserMetadataSize);
- int b1_id = b1->id();
-
- EXPECT_TRUE(b1->IsConnected());
- EXPECT_TRUE(b1->IsValid());
- ASSERT_TRUE(b1->buffer() != nullptr);
- EXPECT_NE(b1->id(), 0);
- EXPECT_EQ(b1->buffer()->initCheck(), 0);
- EXPECT_FALSE(b1->buffer()->isDetachedBuffer());
-
- // Takes a standalone GraphicBuffer which still holds on an
- // PDX::LocalChannelHandle towards BufferHub.
- sp<GraphicBuffer> g1 = b1->TakeGraphicBuffer();
- ASSERT_TRUE(g1 != nullptr);
- EXPECT_TRUE(g1->isDetachedBuffer());
-
- EXPECT_FALSE(b1->IsConnected());
- EXPECT_FALSE(b1->IsValid());
- EXPECT_TRUE(b1->buffer() == nullptr);
-
- sp<GraphicBuffer> g2 = b1->TakeGraphicBuffer();
- ASSERT_TRUE(g2 == nullptr);
-
- auto h1 = g1->takeDetachedBufferHandle();
- ASSERT_TRUE(h1 != nullptr);
- ASSERT_TRUE(h1->isValid());
- EXPECT_FALSE(g1->isDetachedBuffer());
-
- auto b2 = DetachedBuffer::Import(std::move(h1->handle()));
- ASSERT_FALSE(h1->isValid());
- EXPECT_TRUE(b2->IsConnected());
- EXPECT_TRUE(b2->IsValid());
-
- ASSERT_TRUE(b2->buffer() != nullptr);
- EXPECT_EQ(b2->buffer()->initCheck(), 0);
-
- // The newly created DetachedBuffer should share the original buffer_id.
- EXPECT_EQ(b2->id(), b1_id);
- EXPECT_FALSE(b2->buffer()->isDetachedBuffer());
-}
-
-TEST_F(LibBufferHubTest, TestPromoteDetachedBuffer) {
- auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat,
- kUsage, kUserMetadataSize);
- int b1_id = b1->id();
- EXPECT_TRUE(b1->IsValid());
-
- auto status_or_handle = b1->Promote();
- EXPECT_TRUE(status_or_handle);
-
- // The detached buffer should have hangup.
- EXPECT_GT(RETRY_EINTR(b1->Poll(kPollTimeoutMs)), 0);
- auto status_or_int = b1->GetEventMask(POLLHUP);
- EXPECT_TRUE(status_or_int.ok());
- EXPECT_EQ(status_or_int.get(), POLLHUP);
-
- // The buffer client is still considered as connected but invalid.
- EXPECT_TRUE(b1->IsConnected());
- EXPECT_FALSE(b1->IsValid());
-
- // Gets the channel handle for the producer.
- LocalChannelHandle h1 = status_or_handle.take();
- EXPECT_TRUE(h1.valid());
-
- std::unique_ptr<BufferProducer> p1 = BufferProducer::Import(std::move(h1));
- EXPECT_FALSE(h1.valid());
- ASSERT_TRUE(p1 != nullptr);
- int p1_id = p1->id();
-
- // A newly promoted ProducerBuffer should inherit the same buffer id.
- EXPECT_EQ(b1_id, p1_id);
- EXPECT_TRUE(IsBufferGained(p1->buffer_state()));
-}
-
-TEST_F(LibBufferHubTest, TestDetachThenPromote) {
- std::unique_ptr<BufferProducer> p1 = BufferProducer::Create(
+ /* std::unique_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p1.get() != nullptr);
int p1_id = p1->id();
- // Detached the producer.
+ // Detached the producer from gained state.
+ EXPECT_EQ(0, p1->GainAsync());
auto status_or_handle = p1->Detach();
EXPECT_TRUE(status_or_handle.ok());
LocalChannelHandle h1 = status_or_handle.take();
EXPECT_TRUE(h1.valid());
- // Detached buffer handle can be use to construct a new DetachedBuffer object.
- auto b1 = DetachedBuffer::Import(std::move(h1));
+ // Detached buffer handle can be use to construct a new BufferHubBuffer
+ // object.
+ auto b1 = BufferHubBuffer::Import(std::move(h1));
EXPECT_FALSE(h1.valid());
EXPECT_TRUE(b1->IsValid());
int b1_id = b1->id();
- EXPECT_EQ(b1_id, p1_id);
-
- // Promote the detached buffer.
- status_or_handle = b1->Promote();
- // The buffer client is still considered as connected but invalid.
- EXPECT_TRUE(b1->IsConnected());
- EXPECT_FALSE(b1->IsValid());
- EXPECT_TRUE(status_or_handle.ok());
-
- // Gets the channel handle for the producer.
- LocalChannelHandle h2 = status_or_handle.take();
- EXPECT_TRUE(h2.valid());
-
- std::unique_ptr<BufferProducer> p2 = BufferProducer::Import(std::move(h2));
- EXPECT_FALSE(h2.valid());
- ASSERT_TRUE(p2 != nullptr);
- int p2_id = p2->id();
-
- // A newly promoted ProducerBuffer should inherit the same buffer id.
- EXPECT_EQ(b1_id, p2_id);
- EXPECT_TRUE(IsBufferGained(p2->buffer_state()));
+ EXPECT_EQ(b1_id, p1_id); */
}
diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp
new file mode 100644
index 0000000..17930b4
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_base.cpp
@@ -0,0 +1,216 @@
+#include <poll.h>
+#include <sys/epoll.h>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/buffer_hub_base.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+using android::pdx::default_transport::ClientChannel;
+using android::pdx::default_transport::ClientChannelFactory;
+
+namespace android {
+namespace dvr {
+
+BufferHubBase::BufferHubBase(LocalChannelHandle channel_handle)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ id_(-1),
+ cid_(-1) {}
+BufferHubBase::BufferHubBase(const std::string& endpoint_path)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path)},
+ id_(-1),
+ cid_(-1) {}
+
+BufferHubBase::~BufferHubBase() {
+ // buffer_state and fence_state are not reset here. They will be used to
+ // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method.
+ if (metadata_header_ != nullptr) {
+ metadata_buffer_.Unlock();
+ }
+}
+
+Status<LocalChannelHandle> BufferHubBase::CreateConsumer() {
+ Status<LocalChannelHandle> status =
+ InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+ ALOGE_IF(!status,
+ "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+ status.GetErrorMessage().c_str());
+ return status;
+}
+
+int BufferHubBase::ImportBuffer() {
+ ATRACE_NAME("BufferHubBase::ImportBuffer");
+
+ Status<BufferDescription<LocalHandle>> status =
+ InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
+ if (!status) {
+ ALOGE("BufferHubBase::ImportBuffer: Failed to get buffer: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ } else if (status.get().id() < 0) {
+ ALOGE("BufferHubBase::ImportBuffer: Received an invalid id!");
+ return -EIO;
+ }
+
+ auto buffer_desc = status.take();
+
+ // Stash the buffer id to replace the value in id_.
+ const int new_id = buffer_desc.id();
+
+ // Import the buffer.
+ IonBuffer ion_buffer;
+ ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d.", buffer_desc.id());
+
+ if (const int ret = buffer_desc.ImportBuffer(&ion_buffer))
+ return ret;
+
+ // Import the metadata.
+ IonBuffer metadata_buffer;
+ if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) {
+ ALOGE("Failed to import metadata buffer, error=%d", ret);
+ return ret;
+ }
+ size_t metadata_buf_size = metadata_buffer.width();
+ if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) {
+ ALOGE("BufferHubBase::ImportBuffer: metadata buffer too small: %zu",
+ metadata_buf_size);
+ return -ENOMEM;
+ }
+
+ // If all imports succee, replace the previous buffer and id.
+ buffer_ = std::move(ion_buffer);
+ metadata_buffer_ = std::move(metadata_buffer);
+ metadata_buf_size_ = metadata_buf_size;
+ user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize;
+
+ void* metadata_ptr = nullptr;
+ if (const int ret =
+ metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
+ /*y=*/0, metadata_buf_size_,
+ /*height=*/1, &metadata_ptr)) {
+ ALOGE("BufferHubBase::ImportBuffer: Failed to lock metadata.");
+ return ret;
+ }
+
+ // Set up shared fences.
+ shared_acquire_fence_ = buffer_desc.take_acquire_fence();
+ shared_release_fence_ = buffer_desc.take_release_fence();
+ if (!shared_acquire_fence_ || !shared_release_fence_) {
+ ALOGE("BufferHubBase::ImportBuffer: Failed to import shared fences.");
+ return -EIO;
+ }
+
+ metadata_header_ =
+ reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
+ if (user_metadata_size_) {
+ user_metadata_ptr_ =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) +
+ BufferHubDefs::kMetadataHeaderSize);
+ } else {
+ user_metadata_ptr_ = nullptr;
+ }
+
+ id_ = new_id;
+ cid_ = buffer_desc.buffer_cid();
+ client_state_mask_ = buffer_desc.client_state_mask();
+
+ // Note that here the buffer_state, fence_state and active_clients_bit_mask
+ // are mapped from shared memory as an atomic object. The std::atomic's
+ // constructor will not be called so that the original value stored in the
+ // memory region will be preserved.
+ buffer_state_ = &metadata_header_->bufferState;
+ ALOGD_IF(TRACE,
+ "BufferHubBase::ImportBuffer: id=%d, buffer_state=%" PRIx32 ".",
+ id(), buffer_state_->load(std::memory_order_acquire));
+ fence_state_ = &metadata_header_->fenceState;
+ ALOGD_IF(TRACE,
+ "BufferHubBase::ImportBuffer: id=%d, fence_state=%" PRIx32 ".", id(),
+ fence_state_->load(std::memory_order_acquire));
+ active_clients_bit_mask_ = &metadata_header_->activeClientsBitMask;
+ ALOGD_IF(
+ TRACE,
+ "BufferHubBase::ImportBuffer: id=%d, active_clients_bit_mask=%" PRIx32
+ ".",
+ id(), active_clients_bit_mask_->load(std::memory_order_acquire));
+
+ return 0;
+}
+
+int BufferHubBase::CheckMetadata(size_t user_metadata_size) const {
+ if (user_metadata_size && !user_metadata_ptr_) {
+ ALOGE("BufferHubBase::CheckMetadata: doesn't support custom metadata.");
+ return -EINVAL;
+ }
+ if (user_metadata_size > user_metadata_size_) {
+ ALOGE("BufferHubBase::CheckMetadata: too big: %zu, maximum: %zu.",
+ user_metadata_size, user_metadata_size_);
+ return -E2BIG;
+ }
+ return 0;
+}
+
+int BufferHubBase::UpdateSharedFence(const LocalHandle& new_fence,
+ const LocalHandle& shared_fence) {
+ if (pending_fence_fd_.Get() != new_fence.Get()) {
+ // First, replace the old fd if there was already one. Skipping if the new
+ // one is the same as the old.
+ if (pending_fence_fd_.IsValid()) {
+ const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL,
+ pending_fence_fd_.Get(), nullptr);
+ ALOGW_IF(ret,
+ "BufferHubBase::UpdateSharedFence: failed to remove old fence "
+ "fd from epoll set, error: %s.",
+ strerror(errno));
+ }
+
+ if (new_fence.IsValid()) {
+ // If ready fence is valid, we put that into the epoll set.
+ epoll_event event;
+ event.events = EPOLLIN;
+ event.data.u32 = client_state_mask();
+ pending_fence_fd_ = new_fence.Duplicate();
+ if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(),
+ &event) < 0) {
+ const int error = errno;
+ ALOGE(
+ "BufferHubBase::UpdateSharedFence: failed to add new fence fd "
+ "into epoll set, error: %s.",
+ strerror(error));
+ return -error;
+ }
+ // Set bit in fence state to indicate that there is a fence from this
+ // producer or consumer.
+ fence_state_->fetch_or(client_state_mask());
+ } else {
+ // Unset bit in fence state to indicate that there is no fence, so that
+ // when consumer to acquire or producer to acquire, it knows no need to
+ // check fence for this buffer.
+ fence_state_->fetch_and(~client_state_mask());
+ }
+ }
+
+ return 0;
+}
+
+int BufferHubBase::Lock(int usage, int x, int y, int width, int height,
+ void** address) {
+ return buffer_.Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBase::Unlock() { return buffer_.Unlock(); }
+
+int BufferHubBase::GetBlobReadWritePointer(size_t size, void** addr) {
+ int width = static_cast<int>(size);
+ int height = 1;
+ int ret = Lock(usage(), 0, 0, width, height, addr);
+ if (ret == 0)
+ Unlock();
+ return ret;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
deleted file mode 100644
index 159f2bd..0000000
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ /dev/null
@@ -1,650 +0,0 @@
-#include <private/dvr/buffer_hub_client.h>
-
-#include <log/log.h>
-#include <poll.h>
-#include <sys/epoll.h>
-#include <utils/Trace.h>
-
-#include <mutex>
-
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-
-#include "include/private/dvr/bufferhub_rpc.h"
-
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-using android::pdx::default_transport::ClientChannel;
-using android::pdx::default_transport::ClientChannelFactory;
-
-namespace android {
-namespace dvr {
-
-BufferHubClient::BufferHubClient()
- : Client(ClientChannelFactory::Create(BufferHubRPC::kClientPath)) {}
-
-BufferHubClient::BufferHubClient(LocalChannelHandle channel_handle)
- : Client(ClientChannel::Create(std::move(channel_handle))) {}
-
-bool BufferHubClient::IsValid() const {
- return IsConnected() && GetChannelHandle().valid();
-}
-
-LocalChannelHandle BufferHubClient::TakeChannelHandle() {
- if (IsConnected()) {
- return std::move(GetChannelHandle());
- } else {
- return {};
- }
-}
-
-BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
- : Client{pdx::default_transport::ClientChannel::Create(
- std::move(channel_handle))},
- id_(-1) {}
-BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
- : Client{pdx::default_transport::ClientChannelFactory::Create(
- endpoint_path)},
- id_(-1) {}
-
-BufferHubBuffer::~BufferHubBuffer() {
- if (metadata_header_ != nullptr) {
- metadata_buffer_.Unlock();
- }
-}
-
-Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
- Status<LocalChannelHandle> status =
- InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
- ALOGE_IF(!status,
- "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
- status.GetErrorMessage().c_str());
- return status;
-}
-
-int BufferHubBuffer::ImportBuffer() {
- ATRACE_NAME("BufferHubBuffer::ImportBuffer");
-
- Status<BufferDescription<LocalHandle>> status =
- InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
- if (!status) {
- ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- } else if (status.get().id() < 0) {
- ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!");
- return -EIO;
- }
-
- auto buffer_desc = status.take();
-
- // Stash the buffer id to replace the value in id_.
- const int new_id = buffer_desc.id();
-
- // Import the buffer.
- IonBuffer ion_buffer;
- ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id());
-
- if (const int ret = buffer_desc.ImportBuffer(&ion_buffer))
- return ret;
-
- // Import the metadata.
- IonBuffer metadata_buffer;
- if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) {
- ALOGE("Failed to import metadata buffer, error=%d", ret);
- return ret;
- }
- size_t metadata_buf_size = metadata_buffer.width();
- if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) {
- ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu",
- metadata_buf_size);
- return -ENOMEM;
- }
-
- // If all imports succee, replace the previous buffer and id.
- buffer_ = std::move(ion_buffer);
- metadata_buffer_ = std::move(metadata_buffer);
- metadata_buf_size_ = metadata_buf_size;
- user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize;
-
- void* metadata_ptr = nullptr;
- if (const int ret =
- metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
- /*y=*/0, metadata_buf_size_,
- /*height=*/1, &metadata_ptr)) {
- ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata.");
- return ret;
- }
-
- // Set up shared fences.
- shared_acquire_fence_ = buffer_desc.take_acquire_fence();
- shared_release_fence_ = buffer_desc.take_release_fence();
- if (!shared_acquire_fence_ || !shared_release_fence_) {
- ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences.");
- return -EIO;
- }
-
- metadata_header_ =
- reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
- if (user_metadata_size_) {
- user_metadata_ptr_ =
- reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) +
- BufferHubDefs::kMetadataHeaderSize);
- } else {
- user_metadata_ptr_ = nullptr;
- }
-
- id_ = new_id;
- buffer_state_bit_ = buffer_desc.buffer_state_bit();
-
- // Note that here the buffer state is mapped from shared memory as an atomic
- // object. The std::atomic's constructor will not be called so that the
- // original value stored in the memory region will be preserved.
- buffer_state_ = &metadata_header_->buffer_state;
- ALOGD_IF(TRACE,
- "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".",
- id(), buffer_state_->load());
- fence_state_ = &metadata_header_->fence_state;
- ALOGD_IF(TRACE,
- "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".",
- id(), fence_state_->load());
-
- return 0;
-}
-
-inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const {
- if (user_metadata_size && !user_metadata_ptr_) {
- ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata.");
- return -EINVAL;
- }
- if (user_metadata_size > user_metadata_size_) {
- ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.",
- user_metadata_size, user_metadata_size_);
- return -E2BIG;
- }
- return 0;
-}
-
-int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence,
- const LocalHandle& shared_fence) {
- if (pending_fence_fd_.Get() != new_fence.Get()) {
- // First, replace the old fd if there was already one. Skipping if the new
- // one is the same as the old.
- if (pending_fence_fd_.IsValid()) {
- const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL,
- pending_fence_fd_.Get(), nullptr);
- ALOGW_IF(ret,
- "BufferHubBuffer::UpdateSharedFence: failed to remove old fence "
- "fd from epoll set, error: %s.",
- strerror(errno));
- }
-
- if (new_fence.IsValid()) {
- // If ready fence is valid, we put that into the epoll set.
- epoll_event event;
- event.events = EPOLLIN;
- event.data.u64 = buffer_state_bit();
- pending_fence_fd_ = new_fence.Duplicate();
- if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(),
- &event) < 0) {
- const int error = errno;
- ALOGE(
- "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd "
- "into epoll set, error: %s.",
- strerror(error));
- return -error;
- }
- // Set bit in fence state to indicate that there is a fence from this
- // producer or consumer.
- fence_state_->fetch_or(buffer_state_bit());
- } else {
- // Unset bit in fence state to indicate that there is no fence, so that
- // when consumer to acquire or producer to acquire, it knows no need to
- // check fence for this buffer.
- fence_state_->fetch_and(~buffer_state_bit());
- }
- }
-
- return 0;
-}
-
-int BufferHubBuffer::Poll(int timeout_ms) {
- ATRACE_NAME("BufferHubBuffer::Poll");
- pollfd p = {event_fd(), POLLIN, 0};
- return poll(&p, 1, timeout_ms);
-}
-
-int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
- void** address) {
- return buffer_.Lock(usage, x, y, width, height, address);
-}
-
-int BufferHubBuffer::Unlock() { return buffer_.Unlock(); }
-
-int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
- int width = static_cast<int>(size);
- int height = 1;
- int ret = Lock(usage(), 0, 0, width, height, addr);
- if (ret == 0)
- Unlock();
- return ret;
-}
-
-int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
- return GetBlobReadWritePointer(size, addr);
-}
-
-void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count,
- size_t max_fds_count) const {
- size_t numFds = static_cast<size_t>(native_handle()->numFds);
- *fds_count = std::min(max_fds_count, numFds);
- std::copy(native_handle()->data, native_handle()->data + *fds_count, fds);
-}
-
-BufferConsumer::BufferConsumer(LocalChannelHandle channel)
- : BASE(std::move(channel)) {
- const int ret = ImportBuffer();
- if (ret < 0) {
- ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
- strerror(-ret));
- Close(ret);
- }
-}
-
-std::unique_ptr<BufferConsumer> BufferConsumer::Import(
- LocalChannelHandle channel) {
- ATRACE_NAME("BufferConsumer::Import");
- ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
- return BufferConsumer::Create(std::move(channel));
-}
-
-std::unique_ptr<BufferConsumer> BufferConsumer::Import(
- Status<LocalChannelHandle> status) {
- return Import(status ? status.take()
- : LocalChannelHandle{nullptr, -status.error()});
-}
-
-int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta,
- LocalHandle* out_fence) {
- if (!out_meta)
- return -EINVAL;
-
- // Only check producer bit and this consumer buffer's particular consumer bit.
- // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
- // is not set.
- uint64_t buffer_state = buffer_state_->load();
- if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) {
- ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64
- " buffer_state_bit=%" PRIx64 ".",
- id(), buffer_state, buffer_state_bit());
- return -EBUSY;
- }
-
- // Copy the canonical metadata.
- void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
- memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
- // Fill in the user_metadata_ptr in address space of the local process.
- if (out_meta->user_metadata_size) {
- out_meta->user_metadata_ptr =
- reinterpret_cast<uint64_t>(user_metadata_ptr_);
- } else {
- out_meta->user_metadata_ptr = 0;
- }
-
- uint64_t fence_state = fence_state_->load();
- // If there is an acquire fence from producer, we need to return it.
- if (fence_state & BufferHubDefs::kProducerStateBit) {
- *out_fence = shared_acquire_fence_.Duplicate();
- }
-
- // Set the consumer bit unique to this consumer.
- BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit());
- return 0;
-}
-
-int BufferConsumer::Acquire(LocalHandle* ready_fence) {
- return Acquire(ready_fence, nullptr, 0);
-}
-
-int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
- size_t user_metadata_size) {
- ATRACE_NAME("BufferConsumer::Acquire");
-
- if (const int error = CheckMetadata(user_metadata_size))
- return error;
-
- DvrNativeBufferMetadata canonical_meta;
- if (const int error = LocalAcquire(&canonical_meta, ready_fence))
- return error;
-
- if (meta && user_metadata_size) {
- void* metadata_src =
- reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
- if (metadata_src) {
- memcpy(meta, metadata_src, user_metadata_size);
- } else {
- ALOGW("BufferConsumer::Acquire: no user-defined metadata.");
- }
- }
-
- auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>();
- if (!status)
- return -status.error();
- return 0;
-}
-
-int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta,
- LocalHandle* out_fence) {
- ATRACE_NAME("BufferConsumer::AcquireAsync");
-
- if (const int error = LocalAcquire(out_meta, out_fence))
- return error;
-
- auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode);
- if (!status)
- return -status.error();
- return 0;
-}
-
-int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta,
- const LocalHandle& release_fence) {
- if (const int error = CheckMetadata(meta->user_metadata_size))
- return error;
-
- // Check invalid state transition.
- uint64_t buffer_state = buffer_state_->load();
- if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
- ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
- id(), buffer_state);
- return -EBUSY;
- }
-
- // On release, only the user requested metadata is copied back into the shared
- // memory for metadata. Since there are multiple consumers, it doesn't make
- // sense to send the canonical metadata back to the producer. However, one of
- // the consumer can still choose to write up to user_metadata_size bytes of
- // data into user_metadata_ptr.
- if (meta->user_metadata_ptr && meta->user_metadata_size) {
- void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
- memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
- }
-
- // Send out the release fence through the shared epoll fd. Note that during
- // releasing the producer is not expected to be polling on the fence.
- if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
- return error;
-
- // For release operation, the client don't need to change the state as it's
- // bufferhubd's job to flip the produer bit once all consumers are released.
- return 0;
-}
-
-int BufferConsumer::Release(const LocalHandle& release_fence) {
- ATRACE_NAME("BufferConsumer::Release");
-
- DvrNativeBufferMetadata meta;
- if (const int error = LocalRelease(&meta, release_fence))
- return error;
-
- return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
- BorrowedFence(release_fence.Borrow())));
-}
-
-int BufferConsumer::ReleaseAsync() {
- DvrNativeBufferMetadata meta;
- return ReleaseAsync(&meta, LocalHandle());
-}
-
-int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta,
- const LocalHandle& release_fence) {
- ATRACE_NAME("BufferConsumer::ReleaseAsync");
-
- if (const int error = LocalRelease(meta, release_fence))
- return error;
-
- return ReturnStatusOrError(
- SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
-}
-
-int BufferConsumer::Discard() { return Release(LocalHandle()); }
-
-int BufferConsumer::SetIgnore(bool ignore) {
- return ReturnStatusOrError(
- InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
-}
-
-BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
- uint64_t usage, size_t user_metadata_size)
- : BASE(BufferHubRPC::kClientPath) {
- ATRACE_NAME("BufferProducer::BufferProducer");
- ALOGD_IF(TRACE,
- "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u "
- "usage=%" PRIx64 " user_metadata_size=%zu",
- event_fd(), width, height, format, usage, user_metadata_size);
-
- auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
- width, height, format, usage, user_metadata_size);
- if (!status) {
- ALOGE(
- "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
- status.GetErrorMessage().c_str());
- Close(-status.error());
- return;
- }
-
- const int ret = ImportBuffer();
- if (ret < 0) {
- ALOGE(
- "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
- strerror(-ret));
- Close(ret);
- }
-}
-
-BufferProducer::BufferProducer(uint64_t usage, size_t size)
- : BASE(BufferHubRPC::kClientPath) {
- ATRACE_NAME("BufferProducer::BufferProducer");
- ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%" PRIx64 " size=%zu",
- usage, size);
- const int width = static_cast<int>(size);
- const int height = 1;
- const int format = HAL_PIXEL_FORMAT_BLOB;
- const size_t user_metadata_size = 0;
-
- auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
- width, height, format, usage, user_metadata_size);
- if (!status) {
- ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
- status.GetErrorMessage().c_str());
- Close(-status.error());
- return;
- }
-
- const int ret = ImportBuffer();
- if (ret < 0) {
- ALOGE(
- "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
- strerror(-ret));
- Close(ret);
- }
-}
-
-BufferProducer::BufferProducer(LocalChannelHandle channel)
- : BASE(std::move(channel)) {
- const int ret = ImportBuffer();
- if (ret < 0) {
- ALOGE(
- "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
- strerror(-ret));
- Close(ret);
- }
-}
-
-int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta,
- const LocalHandle& ready_fence) {
- if (const int error = CheckMetadata(meta->user_metadata_size))
- return error;
-
- // Check invalid state transition.
- uint64_t buffer_state = buffer_state_->load();
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
- ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
- id(), buffer_state);
- return -EBUSY;
- }
-
- // Copy the canonical metadata.
- void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
- memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
- // Copy extra user requested metadata.
- if (meta->user_metadata_ptr && meta->user_metadata_size) {
- void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
- memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
- }
-
- // Send out the acquire fence through the shared epoll fd. Note that during
- // posting no consumer is not expected to be polling on the fence.
- if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
- return error;
-
- // Set the producer bit atomically to transit into posted state.
- BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
- BufferHubDefs::kProducerStateBit);
- return 0;
-}
-
-int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
- size_t user_metadata_size) {
- ATRACE_NAME("BufferProducer::Post");
-
- // Populate cononical metadata for posting.
- DvrNativeBufferMetadata canonical_meta;
- canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta);
- canonical_meta.user_metadata_size = user_metadata_size;
-
- if (const int error = LocalPost(&canonical_meta, ready_fence))
- return error;
-
- return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
- BorrowedFence(ready_fence.Borrow())));
-}
-
-int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta,
- const LocalHandle& ready_fence) {
- ATRACE_NAME("BufferProducer::PostAsync");
-
- if (const int error = LocalPost(meta, ready_fence))
- return error;
-
- return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode));
-}
-
-int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta,
- LocalHandle* out_fence) {
- uint64_t buffer_state = buffer_state_->load();
- ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".",
- id(), buffer_state);
-
- if (!out_meta)
- return -EINVAL;
-
- if (!BufferHubDefs::IsBufferReleased(buffer_state)) {
- if (BufferHubDefs::IsBufferGained(buffer_state)) {
- // We don't want to log error when gaining a newly allocated
- // buffer.
- ALOGI("BufferProducer::LocalGain: already gained id=%d.", id());
- return -EALREADY;
- }
- ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".",
- id(), buffer_state);
- return -EBUSY;
- }
-
- // Canonical metadata is undefined on Gain. Except for user_metadata and
- // release_fence_mask. Fill in the user_metadata_ptr in address space of the
- // local process.
- if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) {
- out_meta->user_metadata_size =
- metadata_header_->metadata.user_metadata_size;
- out_meta->user_metadata_ptr =
- reinterpret_cast<uint64_t>(user_metadata_ptr_);
- } else {
- out_meta->user_metadata_size = 0;
- out_meta->user_metadata_ptr = 0;
- }
-
- uint64_t fence_state = fence_state_->load();
- // If there is an release fence from consumer, we need to return it.
- if (fence_state & BufferHubDefs::kConsumerStateMask) {
- *out_fence = shared_release_fence_.Duplicate();
- out_meta->release_fence_mask =
- fence_state & BufferHubDefs::kConsumerStateMask;
- }
-
- // Clear out all bits and the buffer is now back to gained state.
- buffer_state_->store(0ULL);
- return 0;
-}
-
-int BufferProducer::Gain(LocalHandle* release_fence) {
- ATRACE_NAME("BufferProducer::Gain");
-
- DvrNativeBufferMetadata meta;
- if (const int error = LocalGain(&meta, release_fence))
- return error;
-
- auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
- if (!status)
- return -status.error();
- return 0;
-}
-
-int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta,
- LocalHandle* release_fence) {
- ATRACE_NAME("BufferProducer::GainAsync");
-
- if (const int error = LocalGain(out_meta, release_fence))
- return error;
-
- return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
-}
-
-int BufferProducer::GainAsync() {
- DvrNativeBufferMetadata meta;
- LocalHandle fence;
- return GainAsync(&meta, &fence);
-}
-
-std::unique_ptr<BufferProducer> BufferProducer::Import(
- LocalChannelHandle channel) {
- ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
- return BufferProducer::Create(std::move(channel));
-}
-
-std::unique_ptr<BufferProducer> BufferProducer::Import(
- Status<LocalChannelHandle> status) {
- return Import(status ? status.take()
- : LocalChannelHandle{nullptr, -status.error()});
-}
-
-Status<LocalChannelHandle> BufferProducer::Detach() {
- uint64_t buffer_state = buffer_state_->load();
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
- // Can only detach a BufferProducer when it's in gained state.
- ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64
- ") is not in gained state.",
- id(), buffer_state);
- return {};
- }
-
- Status<LocalChannelHandle> status =
- InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>();
- ALOGE_IF(!status,
- "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(),
- status.GetErrorMessage().c_str());
- return status;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
new file mode 100644
index 0000000..115e866
--- /dev/null
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -0,0 +1,213 @@
+#include <private/dvr/consumer_buffer.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+ConsumerBuffer::ConsumerBuffer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE("ConsumerBuffer::ConsumerBuffer: Failed to import buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import(
+ LocalChannelHandle channel) {
+ ATRACE_NAME("ConsumerBuffer::Import");
+ ALOGD_IF(TRACE, "ConsumerBuffer::Import: channel=%d", channel.value());
+ return ConsumerBuffer::Create(std::move(channel));
+}
+
+std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta,
+ LocalHandle* out_fence) {
+ if (!out_meta)
+ return -EINVAL;
+
+ // The buffer can be acquired iff the buffer state for this client is posted.
+ uint32_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::isClientPosted(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to acquire the buffer. The buffer is not posted, id=%d "
+ "state=%" PRIx32 " client_state_mask=%" PRIx32 ".",
+ __FUNCTION__, id(), current_buffer_state, client_state_mask());
+ return -EBUSY;
+ }
+
+ // Change the buffer state for this consumer from posted to acquired.
+ uint32_t updated_buffer_state = current_buffer_state ^ client_state_mask();
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s Failed to acquire the buffer. Current buffer state was changed to "
+ "%" PRIx32
+ " when trying to acquire the buffer and modify the buffer state to "
+ "%" PRIx32 ". About to try again if the buffer is still posted.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (!BufferHubDefs::isClientPosted(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to acquire the buffer. The buffer is no longer posted, "
+ "id=%d state=%" PRIx32 " client_state_mask=%" PRIx32 ".",
+ __FUNCTION__, id(), current_buffer_state, client_state_mask());
+ return -EBUSY;
+ }
+ // The failure of compare_exchange_weak updates current_buffer_state.
+ updated_buffer_state = current_buffer_state ^ client_state_mask();
+ }
+
+ // Copy the canonical metadata.
+ void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
+ memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
+ // Fill in the user_metadata_ptr in address space of the local process.
+ if (out_meta->user_metadata_size) {
+ out_meta->user_metadata_ptr =
+ reinterpret_cast<uint64_t>(user_metadata_ptr_);
+ } else {
+ out_meta->user_metadata_ptr = 0;
+ }
+
+ uint32_t fence_state = fence_state_->load(std::memory_order_acquire);
+ // If there is an acquire fence from producer, we need to return it.
+ // The producer state bit mask is kFirstClientBitMask for now.
+ if (fence_state & BufferHubDefs::kFirstClientBitMask) {
+ *out_fence = shared_acquire_fence_.Duplicate();
+ }
+
+ return 0;
+}
+
+int ConsumerBuffer::Acquire(LocalHandle* ready_fence) {
+ return Acquire(ready_fence, nullptr, 0);
+}
+
+int ConsumerBuffer::Acquire(LocalHandle* ready_fence, void* meta,
+ size_t user_metadata_size) {
+ ATRACE_NAME("ConsumerBuffer::Acquire");
+
+ if (const int error = CheckMetadata(user_metadata_size))
+ return error;
+
+ DvrNativeBufferMetadata canonical_meta;
+ if (const int error = LocalAcquire(&canonical_meta, ready_fence))
+ return error;
+
+ if (meta && user_metadata_size) {
+ void* metadata_src =
+ reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
+ if (metadata_src) {
+ memcpy(meta, metadata_src, user_metadata_size);
+ } else {
+ ALOGW("ConsumerBuffer::Acquire: no user-defined metadata.");
+ }
+ }
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>();
+ if (!status)
+ return -status.error();
+ return 0;
+}
+
+int ConsumerBuffer::AcquireAsync(DvrNativeBufferMetadata* out_meta,
+ LocalHandle* out_fence) {
+ ATRACE_NAME("ConsumerBuffer::AcquireAsync");
+
+ if (const int error = LocalAcquire(out_meta, out_fence))
+ return error;
+
+ auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode);
+ if (!status)
+ return -status.error();
+ return 0;
+}
+
+int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& release_fence) {
+ if (const int error = CheckMetadata(meta->user_metadata_size))
+ return error;
+
+ // Set the buffer state of this client to released if it is not already in
+ // released state.
+ uint32_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::isClientReleased(current_buffer_state,
+ client_state_mask())) {
+ return 0;
+ }
+ uint32_t updated_buffer_state = current_buffer_state & (~client_state_mask());
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to release the buffer. Current buffer state was changed to "
+ "%" PRIx32
+ " when trying to release the buffer and modify the buffer state to "
+ "%" PRIx32 ". About to try again.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ // The failure of compare_exchange_weak updates current_buffer_state.
+ updated_buffer_state = current_buffer_state & (~client_state_mask());
+ }
+
+ // On release, only the user requested metadata is copied back into the shared
+ // memory for metadata. Since there are multiple consumers, it doesn't make
+ // sense to send the canonical metadata back to the producer. However, one of
+ // the consumer can still choose to write up to user_metadata_size bytes of
+ // data into user_metadata_ptr.
+ if (meta->user_metadata_ptr && meta->user_metadata_size) {
+ void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
+ memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
+ }
+
+ // Send out the release fence through the shared epoll fd. Note that during
+ // releasing the producer is not expected to be polling on the fence.
+ if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
+ return error;
+
+ return 0;
+}
+
+int ConsumerBuffer::Release(const LocalHandle& release_fence) {
+ ATRACE_NAME("ConsumerBuffer::Release");
+
+ DvrNativeBufferMetadata meta;
+ if (const int error = LocalRelease(&meta, release_fence))
+ return error;
+
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+ BorrowedFence(release_fence.Borrow())));
+}
+
+int ConsumerBuffer::ReleaseAsync() {
+ DvrNativeBufferMetadata meta;
+ return ReleaseAsync(&meta, LocalHandle());
+}
+
+int ConsumerBuffer::ReleaseAsync(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& release_fence) {
+ ATRACE_NAME("ConsumerBuffer::ReleaseAsync");
+
+ if (const int error = LocalRelease(meta, release_fence))
+ return error;
+
+ return ReturnStatusOrError(
+ SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int ConsumerBuffer::Discard() { return Release(LocalHandle()); }
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/detached_buffer.cpp b/libs/vr/libbufferhub/detached_buffer.cpp
deleted file mode 100644
index 6fae16d..0000000
--- a/libs/vr/libbufferhub/detached_buffer.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#include <private/dvr/detached_buffer.h>
-
-#include <pdx/file_handle.h>
-#include <ui/DetachedBufferHandle.h>
-
-#include <poll.h>
-
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-DetachedBuffer::DetachedBuffer(uint32_t width, uint32_t height,
- uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t user_metadata_size) {
- ATRACE_NAME("DetachedBuffer::DetachedBuffer");
- ALOGD_IF(TRACE,
- "DetachedBuffer::DetachedBuffer: width=%u height=%u layer_count=%u, "
- "format=%u usage=%" PRIx64 " user_metadata_size=%zu",
- width, height, layer_count, format, usage, user_metadata_size);
-
- auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Create>(
- width, height, layer_count, format, usage, user_metadata_size);
- if (!status) {
- ALOGE(
- "DetachedBuffer::DetachedBuffer: Failed to create detached buffer: %s",
- status.GetErrorMessage().c_str());
- client_.Close(-status.error());
- }
-
- const int ret = ImportGraphicBuffer();
- if (ret < 0) {
- ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s",
- strerror(-ret));
- client_.Close(ret);
- }
-}
-
-DetachedBuffer::DetachedBuffer(LocalChannelHandle channel_handle)
- : client_(std::move(channel_handle)) {
- const int ret = ImportGraphicBuffer();
- if (ret < 0) {
- ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s",
- strerror(-ret));
- client_.Close(ret);
- }
-}
-
-int DetachedBuffer::ImportGraphicBuffer() {
- ATRACE_NAME("DetachedBuffer::DetachedBuffer");
-
- auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Import>();
- if (!status) {
- ALOGE("DetachedBuffer::DetachedBuffer: Failed to import GraphicBuffer: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- BufferDescription<LocalHandle> buffer_desc = status.take();
- if (buffer_desc.id() < 0) {
- ALOGE("DetachedBuffer::DetachedBuffer: Received an invalid id!");
- return -EIO;
- }
-
- // Stash the buffer id to replace the value in id_.
- const int buffer_id = buffer_desc.id();
-
- // Import the buffer.
- IonBuffer ion_buffer;
- ALOGD_IF(TRACE, "DetachedBuffer::DetachedBuffer: id=%d.", buffer_id);
-
- if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) {
- ALOGE("Failed to import GraphicBuffer, error=%d", ret);
- return ret;
- }
-
- // If all imports succeed, replace the previous buffer and id.
- id_ = buffer_id;
- buffer_ = std::move(ion_buffer);
- return 0;
-}
-
-int DetachedBuffer::Poll(int timeout_ms) {
- ATRACE_NAME("DetachedBuffer::Poll");
- pollfd p = {client_.event_fd(), POLLIN, 0};
- return poll(&p, 1, timeout_ms);
-}
-
-Status<LocalChannelHandle> DetachedBuffer::Promote() {
- ATRACE_NAME("DetachedBuffer::Promote");
- ALOGD_IF(TRACE, "DetachedBuffer::Promote: id=%d.", id_);
-
- auto status_or_handle =
- client_.InvokeRemoteMethod<DetachedBufferRPC::Promote>();
- if (status_or_handle.ok()) {
- // Invalidate the buffer.
- buffer_ = {};
- } else {
- ALOGE("DetachedBuffer::Promote: Failed to promote buffer (id=%d): %s.", id_,
- status_or_handle.GetErrorMessage().c_str());
- }
- return status_or_handle;
-}
-
-sp<GraphicBuffer> DetachedBuffer::TakeGraphicBuffer() {
- if (!client_.IsValid() || !buffer_.buffer()) {
- ALOGE("DetachedBuffer::TakeGraphicBuffer: Invalid buffer.");
- return nullptr;
- }
-
- // Technically this should never happen.
- LOG_FATAL_IF(
- buffer_.buffer()->isDetachedBuffer(),
- "DetachedBuffer::TakeGraphicBuffer: GraphicBuffer is already detached.");
-
- sp<GraphicBuffer> buffer = std::move(buffer_.buffer());
- buffer->setDetachedBufferHandle(
- DetachedBufferHandle::Create(client_.TakeChannelHandle()));
- return buffer;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
new file mode 100644
index 0000000..8a490d9
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h
@@ -0,0 +1,169 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_BASE_H_
+#define ANDROID_DVR_BUFFER_HUB_BASE_H_
+
+#include <vector>
+
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+// Base class of two types of BufferHub clients: dvr::ProducerBuffer and
+// dvr::ConsumerBuffer.
+class BufferHubBase : public pdx::Client {
+ public:
+ using LocalHandle = pdx::LocalHandle;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ template <typename T>
+ using Status = pdx::Status<T>;
+
+ // Create a new consumer channel that is attached to the producer. Returns
+ // a file descriptor for the new channel or a negative error code.
+ Status<LocalChannelHandle> CreateConsumer();
+
+ // Gets a blob buffer that was created with ProducerBuffer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadWritePointer(size_t size, void** addr);
+
+ // Returns a dup'd file descriptor for accessing the blob shared memory. The
+ // caller takes ownership of the file descriptor and must close it or pass on
+ // ownership. Some GPU API extensions can take file descriptors to bind shared
+ // memory gralloc buffers to GPU buffer objects.
+ LocalHandle GetBlobFd() const {
+ // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+ // vendors and this is the wrong fd, late-latching and EDS will very clearly
+ // stop working and we will need to correct this. The alternative is to use
+ // a GL context in the pose service to allocate this buffer or to use the
+ // ION API directly instead of gralloc.
+ return LocalHandle(dup(native_handle()->data[0]));
+ }
+
+ using Client::event_fd;
+
+ Status<int> GetEventMask(int events) {
+ if (auto* client_channel = GetChannel()) {
+ return client_channel->GetEventMask(events);
+ } else {
+ return pdx::ErrorStatus(EINVAL);
+ }
+ }
+
+ std::vector<pdx::ClientChannel::EventSource> GetEventSources() const {
+ if (auto* client_channel = GetChannel()) {
+ return client_channel->GetEventSources();
+ } else {
+ return {};
+ }
+ }
+
+ native_handle_t* native_handle() const {
+ return const_cast<native_handle_t*>(buffer_.handle());
+ }
+
+ IonBuffer* buffer() { return &buffer_; }
+ const IonBuffer* buffer() const { return &buffer_; }
+
+ // Gets ID of the buffer client. All BufferHub clients derived from the same
+ // buffer in bufferhubd share the same buffer id.
+ int id() const { return id_; }
+
+ // Gets the channel id of the buffer client. Each BufferHub client has its
+ // system unique channel id.
+ int cid() const { return cid_; }
+
+ // Returns the buffer buffer state.
+ uint32_t buffer_state() {
+ return buffer_state_->load(std::memory_order_acquire);
+ };
+
+ // Returns whether the buffer is already released by all current clients.
+ bool is_released() {
+ return (buffer_state() &
+ active_clients_bit_mask_->load(std::memory_order_acquire)) == 0;
+ }
+
+ // A state mask which is unique to a buffer hub client among all its siblings
+ // sharing the same concrete graphic buffer.
+ uint32_t client_state_mask() const { return client_state_mask_; }
+
+ // The following methods return settings of the first buffer. Currently,
+ // it is only possible to create multi-buffer BufferHubBases with the same
+ // settings.
+ uint32_t width() const { return buffer_.width(); }
+ uint32_t height() const { return buffer_.height(); }
+ uint32_t stride() const { return buffer_.stride(); }
+ uint32_t format() const { return buffer_.format(); }
+ uint32_t usage() const { return buffer_.usage(); }
+ uint32_t layer_count() const { return buffer_.layer_count(); }
+
+ uint64_t GetQueueIndex() const { return metadata_header_->queueIndex; }
+ void SetQueueIndex(uint64_t index) { metadata_header_->queueIndex = index; }
+
+ protected:
+ explicit BufferHubBase(LocalChannelHandle channel);
+ explicit BufferHubBase(const std::string& endpoint_path);
+ virtual ~BufferHubBase();
+
+ // Initialization helper.
+ int ImportBuffer();
+
+ // Check invalid metadata operation. Returns 0 if requested metadata is valid.
+ int CheckMetadata(size_t user_metadata_size) const;
+
+ // Send out the new fence by updating the shared fence (shared_release_fence
+ // for producer and shared_acquire_fence for consumer). Note that during this
+ // should only be used in LocalPost() or LocalRelease, and the shared fence
+ // shouldn't be poll'ed by the other end.
+ int UpdateSharedFence(const LocalHandle& new_fence,
+ const LocalHandle& shared_fence);
+
+ // Locks the area specified by (x, y, width, height) for a specific usage. If
+ // the usage is software then |addr| will be updated to point to the address
+ // of the buffer in virtual memory. The caller should only access/modify the
+ // pixels in the specified area. anything else is undefined behavior.
+ int Lock(int usage, int x, int y, int width, int height, void** addr);
+
+ // Must be called after Lock() when the caller has finished changing the
+ // buffer.
+ int Unlock();
+
+ // IonBuffer that is shared between bufferhubd, producer, and consumers.
+ size_t metadata_buf_size_{0};
+ size_t user_metadata_size_{0};
+ BufferHubDefs::MetadataHeader* metadata_header_ = nullptr;
+ void* user_metadata_ptr_ = nullptr;
+ std::atomic<uint32_t>* buffer_state_ = nullptr;
+ std::atomic<uint32_t>* fence_state_ = nullptr;
+ std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
+
+ LocalHandle shared_acquire_fence_;
+ LocalHandle shared_release_fence_;
+
+ // A local fence fd that holds the ownership of the fence fd on Post (for
+ // producer) and Release (for consumer).
+ LocalHandle pending_fence_fd_;
+
+ private:
+ BufferHubBase(const BufferHubBase&) = delete;
+ void operator=(const BufferHubBase&) = delete;
+
+ // Global id for the buffer that is consistent across processes. It is meant
+ // for logging and debugging purposes only and should not be used for lookup
+ // or any other functional purpose as a security precaution.
+ int id_;
+
+ // Channel id.
+ int cid_;
+
+ // Client bit mask which indicates the locations of this client object in the
+ // buffer_state_.
+ uint32_t client_state_mask_{0U};
+ IonBuffer buffer_;
+ IonBuffer metadata_buffer_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_BASE_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
deleted file mode 100644
index 0ea77c8..0000000
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ /dev/null
@@ -1,347 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
-
-#include <hardware/gralloc.h>
-#include <pdx/channel_handle.h>
-#include <pdx/client.h>
-#include <pdx/file_handle.h>
-#include <pdx/status.h>
-
-#include <vector>
-
-#include <private/dvr/ion_buffer.h>
-
-#include "bufferhub_rpc.h"
-
-namespace android {
-namespace dvr {
-
-class BufferHubClient : public pdx::Client {
- public:
- BufferHubClient();
- explicit BufferHubClient(pdx::LocalChannelHandle channel_handle);
-
- bool IsValid() const;
- pdx::LocalChannelHandle TakeChannelHandle();
-
- using pdx::Client::Close;
- using pdx::Client::GetChannel;
- using pdx::Client::InvokeRemoteMethod;
- using pdx::Client::IsConnected;
- using pdx::Client::event_fd;
-};
-
-class BufferHubBuffer : public pdx::Client {
- public:
- using LocalHandle = pdx::LocalHandle;
- using LocalChannelHandle = pdx::LocalChannelHandle;
- template <typename T>
- using Status = pdx::Status<T>;
-
- // Create a new consumer channel that is attached to the producer. Returns
- // a file descriptor for the new channel or a negative error code.
- Status<LocalChannelHandle> CreateConsumer();
-
- // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
- int Poll(int timeout_ms);
-
- // Locks the area specified by (x, y, width, height) for a specific usage. If
- // the usage is software then |addr| will be updated to point to the address
- // of the buffer in virtual memory. The caller should only access/modify the
- // pixels in the specified area. anything else is undefined behavior.
- int Lock(int usage, int x, int y, int width, int height, void** addr);
-
- // Must be called after Lock() when the caller has finished changing the
- // buffer.
- int Unlock();
-
- // Gets a blob buffer that was created with BufferProducer::CreateBlob.
- // Locking and Unlocking is handled internally. There's no need to Unlock
- // after calling this method.
- int GetBlobReadWritePointer(size_t size, void** addr);
-
- // Gets a blob buffer that was created with BufferProducer::CreateBlob.
- // Locking and Unlocking is handled internally. There's no need to Unlock
- // after calling this method.
- int GetBlobReadOnlyPointer(size_t size, void** addr);
-
- // Returns a dup'd file descriptor for accessing the blob shared memory. The
- // caller takes ownership of the file descriptor and must close it or pass on
- // ownership. Some GPU API extensions can take file descriptors to bind shared
- // memory gralloc buffers to GPU buffer objects.
- LocalHandle GetBlobFd() const {
- // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
- // vendors and this is the wrong fd, late-latching and EDS will very clearly
- // stop working and we will need to correct this. The alternative is to use
- // a GL context in the pose service to allocate this buffer or to use the
- // ION API directly instead of gralloc.
- return LocalHandle(dup(native_handle()->data[0]));
- }
-
- // Get up to |max_fds_count| file descriptors for accessing the blob shared
- // memory. |fds_count| will contain the actual number of file descriptors.
- void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const;
-
- using Client::event_fd;
-
- Status<int> GetEventMask(int events) {
- if (auto* client_channel = GetChannel()) {
- return client_channel->GetEventMask(events);
- } else {
- return pdx::ErrorStatus(EINVAL);
- }
- }
-
- std::vector<pdx::ClientChannel::EventSource> GetEventSources() const {
- if (auto* client_channel = GetChannel()) {
- return client_channel->GetEventSources();
- } else {
- return {};
- }
- }
-
- native_handle_t* native_handle() const {
- return const_cast<native_handle_t*>(buffer_.handle());
- }
-
- IonBuffer* buffer() { return &buffer_; }
- const IonBuffer* buffer() const { return &buffer_; }
-
- int id() const { return id_; }
-
- // Returns the buffer buffer state.
- uint64_t buffer_state() { return buffer_state_->load(); };
-
- // A state mask which is unique to a buffer hub client among all its siblings
- // sharing the same concrete graphic buffer.
- uint64_t buffer_state_bit() const { return buffer_state_bit_; }
-
- // The following methods return settings of the first buffer. Currently,
- // it is only possible to create multi-buffer BufferHubBuffers with the same
- // settings.
- uint32_t width() const { return buffer_.width(); }
- uint32_t height() const { return buffer_.height(); }
- uint32_t stride() const { return buffer_.stride(); }
- uint32_t format() const { return buffer_.format(); }
- uint32_t usage() const { return buffer_.usage(); }
- uint32_t layer_count() const { return buffer_.layer_count(); }
-
- uint64_t GetQueueIndex() const { return metadata_header_->queue_index; }
- void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; }
-
- protected:
- explicit BufferHubBuffer(LocalChannelHandle channel);
- explicit BufferHubBuffer(const std::string& endpoint_path);
- virtual ~BufferHubBuffer();
-
- // Initialization helper.
- int ImportBuffer();
-
- // Check invalid metadata operation. Returns 0 if requested metadata is valid.
- int CheckMetadata(size_t user_metadata_size) const;
-
- // Send out the new fence by updating the shared fence (shared_release_fence
- // for producer and shared_acquire_fence for consumer). Note that during this
- // should only be used in LocalPost() or LocalRelease, and the shared fence
- // shouldn't be poll'ed by the other end.
- int UpdateSharedFence(const LocalHandle& new_fence,
- const LocalHandle& shared_fence);
-
- // IonBuffer that is shared between bufferhubd, producer, and consumers.
- size_t metadata_buf_size_{0};
- size_t user_metadata_size_{0};
- BufferHubDefs::MetadataHeader* metadata_header_{nullptr};
- void* user_metadata_ptr_{nullptr};
- std::atomic<uint64_t>* buffer_state_{nullptr};
- std::atomic<uint64_t>* fence_state_{nullptr};
-
- LocalHandle shared_acquire_fence_;
- LocalHandle shared_release_fence_;
-
- // A local fence fd that holds the ownership of the fence fd on Post (for
- // producer) and Release (for consumer).
- LocalHandle pending_fence_fd_;
-
- private:
- BufferHubBuffer(const BufferHubBuffer&) = delete;
- void operator=(const BufferHubBuffer&) = delete;
-
- // Global id for the buffer that is consistent across processes. It is meant
- // for logging and debugging purposes only and should not be used for lookup
- // or any other functional purpose as a security precaution.
- int id_;
- uint64_t buffer_state_bit_{0ULL};
- IonBuffer buffer_;
- IonBuffer metadata_buffer_;
-};
-
-// This represents a writable buffer. Calling Post notifies all clients and
-// makes the buffer read-only. Call Gain to acquire write access. A buffer
-// may have many consumers.
-//
-// The user of BufferProducer is responsible with making sure that the Post() is
-// done with the correct metadata type and size. The user is also responsible
-// for making sure that remote ends (BufferConsumers) are also using the correct
-// metadata when acquiring the buffer. The API guarantees that a Post() with a
-// metadata of wrong size will fail. However, it currently does not do any
-// type checking.
-// The API also assumes that metadata is a serializable type (plain old data).
-class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
- public:
- // Imports a bufferhub producer channel, assuming ownership of its handle.
- static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
- static std::unique_ptr<BufferProducer> Import(
- Status<LocalChannelHandle> status);
-
- // Asynchronously posts a buffer. The fence and metadata are passed to
- // consumer via shared fd and shared memory.
- int PostAsync(const DvrNativeBufferMetadata* meta,
- const LocalHandle& ready_fence);
-
- // Post this buffer, passing |ready_fence| to the consumers. The bytes in
- // |meta| are passed unaltered to the consumers. The producer must not modify
- // the buffer until it is re-gained.
- // This returns zero or a negative unix error code.
- int Post(const LocalHandle& ready_fence, const void* meta,
- size_t user_metadata_size);
-
- template <typename Meta,
- typename = typename std::enable_if<std::is_void<Meta>::value>::type>
- int Post(const LocalHandle& ready_fence) {
- return Post(ready_fence, nullptr, 0);
- }
- template <typename Meta, typename = typename std::enable_if<
- !std::is_void<Meta>::value>::type>
- int Post(const LocalHandle& ready_fence, const Meta& meta) {
- return Post(ready_fence, &meta, sizeof(meta));
- }
-
- // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
- // must be waited on before using the buffer. If it is not valid then the
- // buffer is free for immediate use. This call will only succeed if the buffer
- // is in the released state.
- // This returns zero or a negative unix error code.
- int Gain(LocalHandle* release_fence);
- int GainAsync();
-
- // Asynchronously marks a released buffer as gained. This method is similar to
- // the synchronous version above, except that it does not wait for BufferHub
- // to acknowledge success or failure. Because of the asynchronous nature of
- // the underlying message, no error is returned if this method is called when
- // the buffer is in an incorrect state. Returns zero if sending the message
- // succeeded, or a negative errno code if local error check fails.
- int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
-
- // Detaches a ProducerBuffer from an existing producer/consumer set. Can only
- // be called when a producer buffer has exclusive access to the buffer (i.e.
- // in the gain'ed state). On the successful return of the IPC call, a new
- // LocalChannelHandle representing a detached buffer will be returned and all
- // existing producer and consumer channels will be closed. Further IPCs
- // towards those channels will return error.
- Status<LocalChannelHandle> Detach();
-
- private:
- friend BASE;
-
- // Constructors are automatically exposed through BufferProducer::Create(...)
- // static template methods inherited from ClientBase, which take the same
- // arguments as the constructors.
-
- // Constructs a buffer with the given geometry and parameters.
- BufferProducer(uint32_t width, uint32_t height, uint32_t format,
- uint64_t usage, size_t metadata_size = 0);
-
- // Constructs a blob (flat) buffer with the given usage flags.
- BufferProducer(uint64_t usage, size_t size);
-
- // Imports the given file handle to a producer channel, taking ownership.
- explicit BufferProducer(LocalChannelHandle channel);
-
- // Local state transition helpers.
- int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
- int LocalPost(const DvrNativeBufferMetadata* meta,
- const LocalHandle& ready_fence);
-};
-
-// This is a connection to a producer buffer, which can be located in another
-// application. When that buffer is Post()ed, this fd will be signaled and
-// Acquire allows read access. The user is responsible for making sure that
-// Acquire is called with the correct metadata structure. The only guarantee the
-// API currently provides is that an Acquire() with metadata of the wrong size
-// will fail.
-class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
- public:
- // This call assumes ownership of |fd|.
- static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
- static std::unique_ptr<BufferConsumer> Import(
- Status<LocalChannelHandle> status);
-
- // Attempt to retrieve a post event from buffer hub. If successful,
- // |ready_fence| will be set to a fence to wait on until the buffer is ready.
- // This call will only succeed after the fd is signalled. This call may be
- // performed as an alternative to the Acquire() with metadata. In such cases
- // the metadata is not read.
- //
- // This returns zero or negative unix error code.
- int Acquire(LocalHandle* ready_fence);
-
- // Attempt to retrieve a post event from buffer hub. If successful,
- // |ready_fence| is set to a fence signaling that the contents of the buffer
- // are available. This call will only succeed if the buffer is in the posted
- // state.
- // Returns zero on success, or a negative errno code otherwise.
- int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size);
-
- // Attempt to retrieve a post event from buffer hub. If successful,
- // |ready_fence| is set to a fence to wait on until the buffer is ready. This
- // call will only succeed after the fd is signaled. This returns zero or a
- // negative unix error code.
- template <typename Meta>
- int Acquire(LocalHandle* ready_fence, Meta* meta) {
- return Acquire(ready_fence, meta, sizeof(*meta));
- }
-
- // Asynchronously acquires a bufer.
- int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
-
- // This should be called after a successful Acquire call. If the fence is
- // valid the fence determines the buffer usage, otherwise the buffer is
- // released immediately.
- // This returns zero or a negative unix error code.
- int Release(const LocalHandle& release_fence);
- int ReleaseAsync();
-
- // Asynchronously releases a buffer. Similar to the synchronous version above,
- // except that it does not wait for BufferHub to reply with success or error.
- // The fence and metadata are passed to consumer via shared fd and shared
- // memory.
- int ReleaseAsync(const DvrNativeBufferMetadata* meta,
- const LocalHandle& release_fence);
-
- // May be called after or instead of Acquire to indicate that the consumer
- // does not need to access the buffer this cycle. This returns zero or a
- // negative unix error code.
- int Discard();
-
- // When set, this consumer is no longer notified when this buffer is
- // available. The system behaves as if Discard() is immediately called
- // whenever the buffer is posted. If ignore is set to true while a buffer is
- // pending, it will act as if Discard() was also called.
- // This returns zero or a negative unix error code.
- int SetIgnore(bool ignore);
-
- private:
- friend BASE;
-
- explicit BufferConsumer(LocalChannelHandle channel);
-
- // Local state transition helpers.
- int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
- int LocalRelease(const DvrNativeBufferMetadata* meta,
- const LocalHandle& release_fence);
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
new file mode 100644
index 0000000..e610e18
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -0,0 +1,164 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_DEFS_H_
+#define ANDROID_DVR_BUFFER_HUB_DEFS_H_
+
+#include <dvr/dvr_api.h>
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/native_handle_wrapper.h>
+#include <ui/BufferHubDefs.h>
+
+namespace android {
+namespace dvr {
+
+namespace BufferHubDefs {
+
+static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB;
+static constexpr uint32_t kMetadataUsage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+
+// See more details in libs/ui/include/ui/BufferHubDefs.h
+static constexpr int kMaxNumberOfClients =
+ android::BufferHubDefs::kMaxNumberOfClients;
+static constexpr uint32_t kLowbitsMask = android::BufferHubDefs::kLowbitsMask;
+static constexpr uint32_t kHighBitsMask = android::BufferHubDefs::kHighBitsMask;
+static constexpr uint32_t kFirstClientBitMask =
+ android::BufferHubDefs::kFirstClientBitMask;
+
+static inline bool isAnyClientGained(uint32_t state) {
+ return android::BufferHubDefs::isAnyClientGained(state);
+}
+
+static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientGained(state, client_bit_mask);
+}
+
+static inline bool isAnyClientPosted(uint32_t state) {
+ return android::BufferHubDefs::isAnyClientPosted(state);
+}
+
+static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientPosted(state, client_bit_mask);
+}
+
+static inline bool isAnyClientAcquired(uint32_t state) {
+ return android::BufferHubDefs::isAnyClientAcquired(state);
+}
+
+static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientAcquired(state, client_bit_mask);
+}
+
+static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) {
+ return android::BufferHubDefs::isClientReleased(state, client_bit_mask);
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) {
+ return android::BufferHubDefs::findNextAvailableClientStateMask(union_bits);
+}
+
+using MetadataHeader = android::BufferHubDefs::MetadataHeader;
+static constexpr size_t kMetadataHeaderSize =
+ android::BufferHubDefs::kMetadataHeaderSize;
+
+} // namespace BufferHubDefs
+
+template <typename FileHandleType>
+class BufferTraits {
+ public:
+ BufferTraits() = default;
+ BufferTraits(const native_handle_t* buffer_handle,
+ const FileHandleType& metadata_handle, int id,
+ uint32_t client_state_mask, uint64_t metadata_size,
+ uint32_t width, uint32_t height, uint32_t layer_count,
+ uint32_t format, uint64_t usage, uint32_t stride,
+ const FileHandleType& acquire_fence_fd,
+ const FileHandleType& release_fence_fd)
+ : id_(id),
+ client_state_mask_(client_state_mask),
+ metadata_size_(metadata_size),
+ width_(width),
+ height_(height),
+ layer_count_(layer_count),
+ format_(format),
+ usage_(usage),
+ stride_(stride),
+ buffer_handle_(buffer_handle),
+ metadata_handle_(metadata_handle.Borrow()),
+ acquire_fence_fd_(acquire_fence_fd.Borrow()),
+ release_fence_fd_(release_fence_fd.Borrow()) {}
+
+ BufferTraits(BufferTraits&& other) = default;
+ BufferTraits& operator=(BufferTraits&& other) = default;
+
+ // ID of the buffer client. All BufferHubBuffer clients derived from the same
+ // buffer in bufferhubd share the same buffer id.
+ int id() const { return id_; }
+
+ // State mask of the buffer client. Each BufferHubBuffer client backed by the
+ // same buffer channel has uniqued state bit among its siblings. For a
+ // producer buffer the bit must be kFirstClientBitMask; for a consumer the bit
+ // must be one of the kConsumerStateMask.
+ uint32_t client_state_mask() const { return client_state_mask_; }
+ uint64_t metadata_size() const { return metadata_size_; }
+
+ uint32_t width() { return width_; }
+ uint32_t height() { return height_; }
+ uint32_t layer_count() { return layer_count_; }
+ uint32_t format() { return format_; }
+ uint64_t usage() { return usage_; }
+ uint32_t stride() { return stride_; }
+
+ const NativeHandleWrapper<FileHandleType>& buffer_handle() const {
+ return buffer_handle_;
+ }
+
+ NativeHandleWrapper<FileHandleType> take_buffer_handle() {
+ return std::move(buffer_handle_);
+ }
+ FileHandleType take_metadata_handle() { return std::move(metadata_handle_); }
+ FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); }
+ FileHandleType take_release_fence() { return std::move(release_fence_fd_); }
+
+ private:
+ // BufferHub specific traits.
+ int id_ = -1;
+ uint32_t client_state_mask_;
+ uint64_t metadata_size_;
+
+ // Traits for a GraphicBuffer.
+ uint32_t width_;
+ uint32_t height_;
+ uint32_t layer_count_;
+ uint32_t format_;
+ uint64_t usage_;
+ uint32_t stride_;
+
+ // Native handle for the graphic buffer.
+ NativeHandleWrapper<FileHandleType> buffer_handle_;
+
+ // File handle of an ashmem that holds buffer metadata.
+ FileHandleType metadata_handle_;
+
+ // Pamameters for shared fences.
+ FileHandleType acquire_fence_fd_;
+ FileHandleType release_fence_fd_;
+
+ PDX_SERIALIZABLE_MEMBERS(BufferTraits<FileHandleType>, id_,
+ client_state_mask_, metadata_size_, stride_, width_,
+ height_, layer_count_, format_, usage_,
+ buffer_handle_, metadata_handle_, acquire_fence_fd_,
+ release_fence_fd_);
+
+ BufferTraits(const BufferTraits&) = delete;
+ void operator=(const BufferTraits&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_DEFS_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index 04f4fb4..f1cd0b4 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -1,11 +1,11 @@
#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
#define ANDROID_DVR_BUFFERHUB_RPC_H_
+#include "buffer_hub_defs.h"
+
#include <cutils/native_handle.h>
-#include <sys/types.h>
#include <ui/BufferQueueDefs.h>
-#include <dvr/dvr_api.h>
#include <pdx/channel_handle.h>
#include <pdx/file_handle.h>
#include <pdx/rpc/remote_method.h>
@@ -15,71 +15,6 @@
namespace android {
namespace dvr {
-namespace BufferHubDefs {
-
-static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB;
-static constexpr uint32_t kMetadataUsage =
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
-
-// Single producuer multiple (up to 63) consumers ownership signal.
-// 64-bit atomic unsigned int.
-//
-// MSB LSB
-// | |
-// v v
-// [P|C62|...|C1|C0]
-// Gain'ed state: [0|..|0|0] -> Exclusively Writable.
-// Post'ed state: [1|..|0|0]
-// Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits
-// Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits
-static constexpr uint64_t kProducerStateBit = 1ULL << 63;
-static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1;
-
-static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state,
- uint64_t clear_mask, uint64_t set_mask) {
- uint64_t old_state;
- uint64_t new_state;
- do {
- old_state = buffer_state->load();
- new_state = (old_state & ~clear_mask) | set_mask;
- } while (!buffer_state->compare_exchange_weak(old_state, new_state));
-}
-
-static inline bool IsBufferGained(uint64_t state) { return state == 0; }
-
-static inline bool IsBufferPosted(uint64_t state,
- uint64_t consumer_bit = kConsumerStateMask) {
- return (state & kProducerStateBit) && !(state & consumer_bit);
-}
-
-static inline bool IsBufferAcquired(uint64_t state) {
- return (state & kProducerStateBit) && (state & kConsumerStateMask);
-}
-
-static inline bool IsBufferReleased(uint64_t state) {
- return !(state & kProducerStateBit) && (state & kConsumerStateMask);
-}
-
-struct __attribute__((packed, aligned(8))) MetadataHeader {
- // Internal data format, which can be updated as long as the size, padding and
- // field alignment of the struct is consistent within the same ABI. As this
- // part is subject for future updates, it's not stable cross Android version,
- // so don't have it visible from outside of the Android platform (include Apps
- // and vendor HAL).
- std::atomic<uint64_t> buffer_state;
- std::atomic<uint64_t> fence_state;
- uint64_t queue_index;
-
- // Public data format, which should be updated with caution. See more details
- // in dvr_api.h
- DvrNativeBufferMetadata metadata;
-};
-
-static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size");
-static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader);
-
-} // namespace BufferHubDefs
-
template <typename FileHandleType>
class NativeBufferHandle {
public:
@@ -164,11 +99,12 @@
public:
BufferDescription() = default;
BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id,
- uint64_t buffer_state_bit,
+ int buffer_cid, uint32_t client_state_mask,
const FileHandleType& acquire_fence_fd,
const FileHandleType& release_fence_fd)
: id_(id),
- buffer_state_bit_(buffer_state_bit),
+ buffer_cid_(buffer_cid),
+ client_state_mask_(client_state_mask),
buffer_(buffer, id),
metadata_(metadata, id),
acquire_fence_fd_(acquire_fence_fd.Borrow()),
@@ -177,14 +113,17 @@
BufferDescription(BufferDescription&& other) noexcept = default;
BufferDescription& operator=(BufferDescription&& other) noexcept = default;
- // ID of the buffer client. All BufferHubBuffer clients derived from the same
- // buffer in bufferhubd share the same buffer id.
+ // ID of the buffer client. All BufferHub clients derived from the same buffer
+ // in bufferhubd share the same buffer id.
int id() const { return id_; }
- // State mask of the buffer client. Each BufferHubBuffer client backed by the
- // same buffer channel has uniqued state bit among its siblings. For a
- // producer buffer the bit must be kProducerStateBit; for a consumer the bit
- // must be one of the kConsumerStateMask.
- uint64_t buffer_state_bit() const { return buffer_state_bit_; }
+
+ // Channel ID of the buffer client. Each BufferHub client has its system
+ // unique channel id.
+ int buffer_cid() const { return buffer_cid_; }
+
+ // State mask of the buffer client. Each BufferHub client backed by the
+ // same buffer channel has uniqued state bit among its siblings.
+ uint32_t client_state_mask() const { return client_state_mask_; }
FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); }
FileHandleType take_release_fence() { return std::move(release_fence_fd_); }
@@ -193,7 +132,8 @@
private:
int id_{-1};
- uint64_t buffer_state_bit_{0};
+ int buffer_cid_{-1};
+ uint32_t client_state_mask_{0U};
// Two IonBuffers: one for the graphic buffer and one for metadata.
NativeBufferHandle<FileHandleType> buffer_;
NativeBufferHandle<FileHandleType> metadata_;
@@ -202,8 +142,8 @@
FileHandleType acquire_fence_fd_;
FileHandleType release_fence_fd_;
- PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_,
- buffer_state_bit_, buffer_, metadata_,
+ PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, buffer_cid_,
+ client_state_mask_, buffer_, metadata_,
acquire_fence_fd_, release_fence_fd_);
BufferDescription(const BufferDescription&) = delete;
@@ -372,19 +312,15 @@
kOpProducerGain,
kOpConsumerAcquire,
kOpConsumerRelease,
- kOpConsumerSetIgnore,
- kOpProducerBufferDetach,
kOpConsumerBufferDetach,
- kOpDetachedBufferCreate,
- kOpDetachedBufferPromote,
kOpCreateProducerQueue,
kOpCreateConsumerQueue,
kOpGetQueueInfo,
kOpProducerQueueAllocateBuffers,
+ kOpProducerQueueInsertBuffer,
kOpProducerQueueRemoveBuffer,
kOpConsumerQueueImportBuffers,
// TODO(b/77153033): Separate all those RPC operations into subclasses.
- kOpDetachedBufferBase = 1000,
};
// Aliases.
@@ -405,9 +341,6 @@
PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void));
PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
void(LocalFence release_fence));
- PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
- PDX_REMOTE_METHOD(ProducerBufferDetach, kOpProducerBufferDetach,
- LocalChannelHandle(Void));
// Detaches a ConsumerBuffer from an existing producer/consumer set. Can only
// be called when the consumer is the only consumer and it has exclusive
@@ -430,31 +363,14 @@
std::vector<std::pair<LocalChannelHandle, size_t>>(
uint32_t width, uint32_t height, uint32_t layer_count,
uint32_t format, uint64_t usage, size_t buffer_count));
+ PDX_REMOTE_METHOD(ProducerQueueInsertBuffer, kOpProducerQueueInsertBuffer,
+ size_t(int buffer_cid));
PDX_REMOTE_METHOD(ProducerQueueRemoveBuffer, kOpProducerQueueRemoveBuffer,
void(size_t slot));
PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
};
-struct DetachedBufferRPC final : public BufferHubRPC {
- private:
- enum {
- kOpCreate = kOpDetachedBufferBase,
- kOpImport,
- kOpPromote,
- };
-
- public:
- PDX_REMOTE_METHOD(Create, kOpCreate,
- void(uint32_t width, uint32_t height, uint32_t layer_count,
- uint32_t format, uint64_t usage,
- size_t user_metadata_size));
- PDX_REMOTE_METHOD(Import, kOpImport, BufferDescription<LocalHandle>(Void));
- PDX_REMOTE_METHOD(Promote, kOpPromote, LocalChannelHandle(Void));
-
- PDX_REMOTE_API(API, Create, Promote);
-};
-
} // namespace dvr
} // namespace android
diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
new file mode 100644
index 0000000..726f035
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_DVR_CONSUMER_BUFFER_H_
+#define ANDROID_DVR_CONSUMER_BUFFER_H_
+
+#include <private/dvr/buffer_hub_base.h>
+
+namespace android {
+namespace dvr {
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class ConsumerBuffer : public pdx::ClientBase<ConsumerBuffer, BufferHubBase> {
+ public:
+ // This call assumes ownership of |fd|.
+ static std::unique_ptr<ConsumerBuffer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<ConsumerBuffer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+ // This call will only succeed after the fd is signalled. This call may be
+ // performed as an alternative to the Acquire() with metadata. In such cases
+ // the metadata is not read.
+ //
+ // This returns zero or negative unix error code.
+ int Acquire(LocalHandle* ready_fence);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence signaling that the contents of the buffer
+ // are available. This call will only succeed if the buffer is in the posted
+ // state.
+ // Returns zero on success, or a negative errno code otherwise.
+ int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size);
+
+ // Asynchronously acquires a bufer.
+ int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+
+ // Releases the buffer from any buffer state. If the fence is valid the fence
+ // determines the buffer usage, otherwise the buffer is released immediately.
+ // This returns zero or a negative unix error code.
+ int Release(const LocalHandle& release_fence);
+ int ReleaseAsync();
+
+ // Asynchronously releases a buffer. Similar to the synchronous version above,
+ // except that it does not wait for BufferHub to reply with success or error.
+ // The fence and metadata are passed to consumer via shared fd and shared
+ // memory.
+ int ReleaseAsync(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& release_fence);
+
+ // May be called after or instead of Acquire to indicate that the consumer
+ // does not need to access the buffer this cycle. This returns zero or a
+ // negative unix error code.
+ int Discard();
+
+ private:
+ friend BASE;
+
+ explicit ConsumerBuffer(LocalChannelHandle channel);
+
+ // Local state transition helpers.
+ int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
+ int LocalRelease(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& release_fence);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_CONSUMER_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h b/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h
deleted file mode 100644
index 6d0b502..0000000
--- a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#ifndef ANDROID_DVR_DETACHED_BUFFER_H_
-#define ANDROID_DVR_DETACHED_BUFFER_H_
-
-#include <private/dvr/buffer_hub_client.h>
-
-namespace android {
-namespace dvr {
-
-class DetachedBuffer {
- public:
- // Allocates a standalone DetachedBuffer not associated with any producer
- // consumer set.
- static std::unique_ptr<DetachedBuffer> Create(uint32_t width, uint32_t height,
- uint32_t layer_count,
- uint32_t format, uint64_t usage,
- size_t user_metadata_size) {
- return std::unique_ptr<DetachedBuffer>(new DetachedBuffer(
- width, height, layer_count, format, usage, user_metadata_size));
- }
-
- // Imports the given channel handle to a DetachedBuffer, taking ownership.
- static std::unique_ptr<DetachedBuffer> Import(
- pdx::LocalChannelHandle channel_handle) {
- return std::unique_ptr<DetachedBuffer>(
- new DetachedBuffer(std::move(channel_handle)));
- }
-
- DetachedBuffer(const DetachedBuffer&) = delete;
- void operator=(const DetachedBuffer&) = delete;
-
- const sp<GraphicBuffer>& buffer() const { return buffer_.buffer(); }
-
- int id() const { return id_; }
-
- // Returns true if the buffer holds an open PDX channels towards bufferhubd.
- bool IsConnected() const { return client_.IsValid(); }
-
- // Returns true if the buffer holds an valid gralloc buffer handle that's
- // availble for the client to read from and/or write into.
- bool IsValid() const { return buffer_.IsValid(); }
-
- // Returns the event mask for all the events that are pending on this buffer
- // (see sys/poll.h for all possible bits).
- pdx::Status<int> GetEventMask(int events) {
- if (auto* channel = client_.GetChannel()) {
- return channel->GetEventMask(events);
- } else {
- return pdx::ErrorStatus(EINVAL);
- }
- }
-
- // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
- int Poll(int timeout_ms);
-
- // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the
- // DetachedBuffer channel will be closed automatically on successful IPC
- // return. Further IPCs towards this channel will return error.
- pdx::Status<pdx::LocalChannelHandle> Promote();
-
- // Takes the underlying graphic buffer out of this DetachedBuffer. This call
- // immediately invalidates this DetachedBuffer object and transfers the
- // underlying pdx::LocalChannelHandle into the GraphicBuffer.
- sp<GraphicBuffer> TakeGraphicBuffer();
-
- private:
- DetachedBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
- uint32_t format, uint64_t usage, size_t user_metadata_size);
-
- DetachedBuffer(pdx::LocalChannelHandle channel_handle);
-
- int ImportGraphicBuffer();
-
- // Global id for the buffer that is consistent across processes.
- int id_;
- IonBuffer buffer_;
- BufferHubClient client_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DETACHED_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
index 860f08a..ed38e7f 100644
--- a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -24,7 +24,7 @@
IonBuffer& operator=(IonBuffer&& other) noexcept;
// Returns check this IonBuffer holds a valid Gralloc buffer.
- bool IsValid() const { return buffer_ && buffer_->initCheck() == NO_ERROR; }
+ bool IsValid() const { return buffer_ && buffer_->initCheck() == OK; }
// Frees the underlying native handle and leaves the instance initialized to
// empty.
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
new file mode 100644
index 0000000..a5c6ca2
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
@@ -0,0 +1,102 @@
+#ifndef ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_
+#define ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <pdx/rpc/serializable.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// A PDX-friendly wrapper to maintain the life cycle of a native_handle_t
+// object.
+//
+// See https://source.android.com/devices/architecture/hidl/types#handle_t for
+// more information about native_handle_t.
+template <typename FileHandleType>
+class NativeHandleWrapper {
+ public:
+ NativeHandleWrapper() = default;
+ NativeHandleWrapper(NativeHandleWrapper&& other) = default;
+ NativeHandleWrapper& operator=(NativeHandleWrapper&& other) = default;
+
+ // Create a new NativeHandleWrapper by duplicating the handle.
+ explicit NativeHandleWrapper(const native_handle_t* handle) {
+ const int fd_count = handle->numFds;
+ const int int_count = handle->numInts;
+
+ // Populate the fd and int vectors: native_handle->data[] is an array of fds
+ // followed by an array of opaque ints.
+ for (int i = 0; i < fd_count; i++) {
+ fds_.emplace_back(FileHandleType::AsDuplicate(handle->data[i]));
+ }
+ for (int i = 0; i < int_count; i++) {
+ ints_.push_back(handle->data[fd_count + i]);
+ }
+ }
+
+ size_t int_count() const { return ints_.size(); }
+ size_t fd_count() const { return fds_.size(); }
+ bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; }
+
+ // Duplicate a native handle from the wrapper.
+ native_handle_t* DuplicateHandle() const {
+ if (!IsValid()) {
+ return nullptr;
+ }
+
+ // numFds + numInts ints.
+ std::vector<FileHandleType> fds;
+ for (const auto& fd : fds_) {
+ if (!fd.IsValid()) {
+ return nullptr;
+ }
+ fds.emplace_back(fd.Duplicate());
+ }
+
+ return FromFdsAndInts(std::move(fds), ints_);
+ }
+
+ // Takes the native handle out of the wrapper.
+ native_handle_t* TakeHandle() {
+ if (!IsValid()) {
+ return nullptr;
+ }
+
+ return FromFdsAndInts(std::move(fds_), std::move(ints_));
+ }
+
+ private:
+ NativeHandleWrapper(const NativeHandleWrapper&) = delete;
+ void operator=(const NativeHandleWrapper&) = delete;
+
+ static native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds,
+ std::vector<int> ints) {
+ native_handle_t* handle = native_handle_create(fds.size(), ints.size());
+ if (!handle) {
+ ALOGE("NativeHandleWrapper::TakeHandle: Failed to create new handle.");
+ return nullptr;
+ }
+
+ // numFds + numInts ints.
+ for (int i = 0; i < handle->numFds; i++) {
+ handle->data[i] = fds[i].Release();
+ }
+ memcpy(&handle->data[handle->numFds], ints.data(),
+ sizeof(int) * handle->numInts);
+
+ return handle;
+ }
+
+ std::vector<int> ints_;
+ std::vector<FileHandleType> fds_;
+
+ PDX_SERIALIZABLE_MEMBERS(NativeHandleWrapper<FileHandleType>, ints_, fds_);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h
new file mode 100644
index 0000000..7ec345c
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h
@@ -0,0 +1,103 @@
+#ifndef ANDROID_DVR_PRODUCER_BUFFER_H_
+#define ANDROID_DVR_PRODUCER_BUFFER_H_
+
+#include <private/dvr/buffer_hub_base.h>
+
+namespace android {
+namespace dvr {
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of ProducerBuffer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (ConsumerBuffers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class ProducerBuffer : public pdx::ClientBase<ProducerBuffer, BufferHubBase> {
+ public:
+ // Imports a bufferhub producer channel, assuming ownership of its handle.
+ static std::unique_ptr<ProducerBuffer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<ProducerBuffer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Asynchronously posts a buffer. The fence and metadata are passed to
+ // consumer via shared fd and shared memory.
+ int PostAsync(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& ready_fence);
+
+ // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+ // |meta| are passed unaltered to the consumers. The producer must not modify
+ // the buffer until it is re-gained.
+ // This returns zero or a negative unix error code.
+ int Post(const LocalHandle& ready_fence, const void* meta,
+ size_t user_metadata_size);
+
+ int Post(const LocalHandle& ready_fence) {
+ return Post(ready_fence, nullptr, 0);
+ }
+
+ // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+ // must be waited on before using the buffer. If it is not valid then the
+ // buffer is free for immediate use. This call will succeed if the buffer
+ // is in the released state, or in posted state and gain_posted_buffer is
+ // true.
+ //
+ // @param release_fence output fence.
+ // @param gain_posted_buffer whether to gain posted buffer or not.
+ // @return This returns zero or a negative unix error code.
+ int Gain(LocalHandle* release_fence, bool gain_posted_buffer = false);
+
+ // Asynchronously marks a released buffer as gained. This method is similar to
+ // the synchronous version above, except that it does not wait for BufferHub
+ // to acknowledge success or failure. Because of the asynchronous nature of
+ // the underlying message, no error is returned if this method is called when
+ // the buffer is in an incorrect state. Returns zero if sending the message
+ // succeeded, or a negative errno code if local error check fails.
+ // TODO(b/112007999): gain_posted_buffer true is only used to prevent
+ // libdvrtracking from starving when there are non-responding clients. This
+ // gain_posted_buffer param can be removed once libdvrtracking start to use
+ // the new AHardwareBuffer API.
+ int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence,
+ bool gain_posted_buffer = false);
+ int GainAsync();
+
+ // Detaches a ProducerBuffer from an existing producer/consumer set. Can only
+ // be called when a producer buffer has exclusive access to the buffer (i.e.
+ // in the gain'ed state). On the successful return of the IPC call, a new
+ // LocalChannelHandle representing a detached buffer will be returned and all
+ // existing producer and consumer channels will be closed. Further IPCs
+ // towards those channels will return error.
+ Status<LocalChannelHandle> Detach();
+
+ private:
+ friend BASE;
+
+ // Constructors are automatically exposed through ProducerBuffer::Create(...)
+ // static template methods inherited from ClientBase, which take the same
+ // arguments as the constructors.
+
+ // Constructs a buffer with the given geometry and parameters.
+ ProducerBuffer(uint32_t width, uint32_t height, uint32_t format,
+ uint64_t usage, size_t metadata_size = 0);
+
+ // Constructs a blob (flat) buffer with the given usage flags.
+ ProducerBuffer(uint64_t usage, size_t size);
+
+ // Imports the given file handle to a producer channel, taking ownership.
+ explicit ProducerBuffer(LocalChannelHandle channel);
+
+ // Local state transition helpers.
+ int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence,
+ bool gain_posted_buffer = false);
+ int LocalPost(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& ready_fence);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PRODUCER_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
index 1295531..1965410 100644
--- a/libs/vr/libbufferhub/ion_buffer.cpp
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -205,7 +205,7 @@
status_t err =
buffer_->lock(usage, Rect(x, y, x + width, y + height), address);
- if (err != NO_ERROR)
+ if (err != OK)
return -EINVAL;
else
return 0;
@@ -220,7 +220,7 @@
status_t err =
buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv);
- if (err != NO_ERROR)
+ if (err != OK)
return -EINVAL;
else
return 0;
@@ -231,7 +231,7 @@
ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle());
status_t err = buffer_->unlock();
- if (err != NO_ERROR)
+ if (err != OK)
return -EINVAL;
else
return 0;
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
new file mode 100644
index 0000000..3d88ba5
--- /dev/null
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -0,0 +1,312 @@
+#include <private/dvr/producer_buffer.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+ProducerBuffer::ProducerBuffer(uint32_t width, uint32_t height, uint32_t format,
+ uint64_t usage, size_t user_metadata_size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("ProducerBuffer::ProducerBuffer");
+ ALOGD_IF(TRACE,
+ "ProducerBuffer::ProducerBuffer: fd=%d width=%u height=%u format=%u "
+ "usage=%" PRIx64 " user_metadata_size=%zu",
+ event_fd(), width, height, format, usage, user_metadata_size);
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, usage, user_metadata_size);
+ if (!status) {
+ ALOGE(
+ "ProducerBuffer::ProducerBuffer: Failed to create producer buffer: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+ProducerBuffer::ProducerBuffer(uint64_t usage, size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("ProducerBuffer::ProducerBuffer");
+ ALOGD_IF(TRACE, "ProducerBuffer::ProducerBuffer: usage=%" PRIx64 " size=%zu",
+ usage, size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t user_metadata_size = 0;
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, usage, user_metadata_size);
+ if (!status) {
+ ALOGE("ProducerBuffer::ProducerBuffer: Failed to create blob: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+ProducerBuffer::ProducerBuffer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& ready_fence) {
+ if (const int error = CheckMetadata(meta->user_metadata_size))
+ return error;
+
+ // The buffer can be posted iff the buffer state for this client is gained.
+ uint32_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::isClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGE("%s: not gained, id=%d state=%" PRIx32 ".", __FUNCTION__, id(),
+ current_buffer_state);
+ return -EBUSY;
+ }
+
+ // Set the producer client buffer state to released, that of all other clients
+ // (both existing and non-existing clients) to posted.
+ uint32_t updated_buffer_state =
+ (~client_state_mask()) & BufferHubDefs::kHighBitsMask;
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to post the buffer. Current buffer state was changed to "
+ "%" PRIx32
+ " when trying to post the buffer and modify the buffer state to "
+ "%" PRIx32
+ ". About to try again if the buffer is still gained by this client.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (!BufferHubDefs::isClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to post the buffer. The buffer is no longer gained, "
+ "id=%d state=%" PRIx32 ".",
+ __FUNCTION__, id(), current_buffer_state);
+ return -EBUSY;
+ }
+ }
+
+ // Copy the canonical metadata.
+ void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
+ memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
+ // Copy extra user requested metadata.
+ if (meta->user_metadata_ptr && meta->user_metadata_size) {
+ void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr);
+ memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size);
+ }
+
+ // Send out the acquire fence through the shared epoll fd. Note that during
+ // posting no consumer is not expected to be polling on the fence.
+ if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
+ return error;
+
+ return 0;
+}
+
+int ProducerBuffer::Post(const LocalHandle& ready_fence, const void* meta,
+ size_t user_metadata_size) {
+ ATRACE_NAME("ProducerBuffer::Post");
+
+ // Populate cononical metadata for posting.
+ DvrNativeBufferMetadata canonical_meta;
+ canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta);
+ canonical_meta.user_metadata_size = user_metadata_size;
+
+ if (const int error = LocalPost(&canonical_meta, ready_fence))
+ return error;
+
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+ BorrowedFence(ready_fence.Borrow())));
+}
+
+int ProducerBuffer::PostAsync(const DvrNativeBufferMetadata* meta,
+ const LocalHandle& ready_fence) {
+ ATRACE_NAME("ProducerBuffer::PostAsync");
+
+ if (const int error = LocalPost(meta, ready_fence))
+ return error;
+
+ return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode));
+}
+
+int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta,
+ LocalHandle* out_fence, bool gain_posted_buffer) {
+ if (!out_meta)
+ return -EINVAL;
+
+ uint32_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx32 ".", __FUNCTION__, id(),
+ current_buffer_state);
+
+ if (BufferHubDefs::isClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGV("%s: already gained id=%d.", __FUNCTION__, id());
+ return 0;
+ }
+ if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::isAnyClientPosted(
+ current_buffer_state &
+ active_clients_bit_mask_->load(std::memory_order_acquire)) &&
+ !gain_posted_buffer)) {
+ ALOGE("%s: not released id=%d state=%" PRIx32 ".", __FUNCTION__, id(),
+ current_buffer_state);
+ return -EBUSY;
+ }
+ // Change the buffer state to gained state.
+ uint32_t updated_buffer_state = client_state_mask();
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to gain the buffer. Current buffer state was changed to "
+ "%" PRIx32
+ " when trying to gain the buffer and modify the buffer state to "
+ "%" PRIx32
+ ". About to try again if the buffer is still not read by other "
+ "clients.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+
+ if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::isAnyClientPosted(
+ current_buffer_state &
+ active_clients_bit_mask_->load(std::memory_order_acquire)) &&
+ !gain_posted_buffer)) {
+ ALOGE(
+ "%s: Failed to gain the buffer. The buffer is no longer released. "
+ "id=%d state=%" PRIx32 ".",
+ __FUNCTION__, id(), current_buffer_state);
+ return -EBUSY;
+ }
+ }
+
+ // Canonical metadata is undefined on Gain. Except for user_metadata and
+ // release_fence_mask. Fill in the user_metadata_ptr in address space of the
+ // local process.
+ if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) {
+ out_meta->user_metadata_size =
+ metadata_header_->metadata.user_metadata_size;
+ out_meta->user_metadata_ptr =
+ reinterpret_cast<uint64_t>(user_metadata_ptr_);
+ } else {
+ out_meta->user_metadata_size = 0;
+ out_meta->user_metadata_ptr = 0;
+ }
+
+ uint32_t current_fence_state = fence_state_->load(std::memory_order_acquire);
+ uint32_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ // If there are release fence(s) from consumer(s), we need to return it to the
+ // consumer(s).
+ // TODO(b/112007999) add an atomic variable in metadata header in shared
+ // memory to indicate which client is the last producer of the buffer.
+ // Currently, assume the first client is the only producer to the buffer.
+ if (current_fence_state & current_active_clients_bit_mask &
+ (~BufferHubDefs::kFirstClientBitMask)) {
+ *out_fence = shared_release_fence_.Duplicate();
+ out_meta->release_fence_mask = current_fence_state &
+ current_active_clients_bit_mask &
+ (~BufferHubDefs::kFirstClientBitMask);
+ }
+
+ return 0;
+}
+
+int ProducerBuffer::Gain(LocalHandle* release_fence, bool gain_posted_buffer) {
+ ATRACE_NAME("ProducerBuffer::Gain");
+
+ DvrNativeBufferMetadata meta;
+ if (const int error = LocalGain(&meta, release_fence, gain_posted_buffer))
+ return error;
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+ if (!status)
+ return -status.error();
+ return 0;
+}
+
+int ProducerBuffer::GainAsync(DvrNativeBufferMetadata* out_meta,
+ LocalHandle* release_fence,
+ bool gain_posted_buffer) {
+ ATRACE_NAME("ProducerBuffer::GainAsync");
+
+ if (const int error = LocalGain(out_meta, release_fence, gain_posted_buffer))
+ return error;
+
+ return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+int ProducerBuffer::GainAsync() {
+ DvrNativeBufferMetadata meta;
+ LocalHandle fence;
+ return GainAsync(&meta, &fence);
+}
+
+std::unique_ptr<ProducerBuffer> ProducerBuffer::Import(
+ LocalChannelHandle channel) {
+ ALOGD_IF(TRACE, "ProducerBuffer::Import: channel=%d", channel.value());
+ return ProducerBuffer::Create(std::move(channel));
+}
+
+std::unique_ptr<ProducerBuffer> ProducerBuffer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+Status<LocalChannelHandle> ProducerBuffer::Detach() {
+ // TODO(b/112338294) remove after migrate producer buffer to binder
+ ALOGW("ProducerBuffer::Detach: not supported operation during migration");
+ return {};
+
+ // TODO(b/112338294) Keep here for reference. Remove it after new logic is
+ // written.
+ /* uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::isClientGained(
+ buffer_state, BufferHubDefs::kFirstClientStateMask)) {
+ // Can only detach a ProducerBuffer when it's in gained state.
+ ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx32
+ ") is not in gained state.",
+ id(), buffer_state);
+ return {};
+ }
+
+ Status<LocalChannelHandle> status =
+ InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>();
+ ALOGE_IF(!status,
+ "ProducerBuffer::Detach: Failed to detach buffer (id=%d): %s.", id(),
+ status.GetErrorMessage().c_str());
+ return status; */
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 9f72c05..20894e3 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -59,6 +59,9 @@
static_libs: staticLibraries,
shared_libs: sharedLibraries,
header_libs: headerLibraries,
+
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
}
subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
index 5089b87..ef1eed6 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp
+++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
@@ -5,7 +5,7 @@
"libbase",
"libbinder",
"libcutils",
- "libdvr",
+ "libdvr.google",
"libgui",
"liblog",
"libhardware",
diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
index 4ca8671..b6813eb 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
+++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
@@ -66,7 +66,7 @@
reply->writeStrongBinder(
IGraphicBufferProducer::asBinder(new_queue->producer));
buffer_queues_.push_back(new_queue);
- return NO_ERROR;
+ return OK;
}
default:
return UNKNOWN_TRANSACTION;
@@ -89,7 +89,7 @@
/*waitForFence=*/false);
}
- if (ret != NO_ERROR) {
+ if (ret != OK) {
LOG(ERROR) << "Failed to acquire next buffer.";
return;
}
@@ -99,7 +99,7 @@
ret = buffer_item_consumer_->releaseBuffer(buffer);
}
- if (ret != NO_ERROR) {
+ if (ret != OK) {
LOG(ERROR) << "Failed to release buffer.";
return;
}
@@ -171,14 +171,14 @@
Parcel data;
Parcel reply;
int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
- if (error != NO_ERROR) {
+ if (error != OK) {
LOG(ERROR) << "Failed to get buffer queue over binder.";
return nullptr;
}
sp<IBinder> binder;
error = reply.readNullableStrongBinder(&binder);
- if (error != NO_ERROR) {
+ if (error != OK) {
LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
return nullptr;
}
@@ -206,7 +206,7 @@
class DvrApi {
public:
DvrApi() {
- handle_ = dlopen("libdvr.so", RTLD_NOW | RTLD_LOCAL);
+ handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL);
CHECK(handle_);
auto dvr_get_api =
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 8feb1cd..2d3fa4a 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -69,7 +69,7 @@
.data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}};
ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
if (ret < 0) {
- ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s",
+ ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__,
strerror(-ret));
}
}
@@ -77,7 +77,7 @@
Status<void> BufferHubQueue::ImportQueue() {
auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
if (!status) {
- ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s",
+ ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
status.GetErrorMessage().c_str());
return ErrorStatus(status.error());
} else {
@@ -136,9 +136,7 @@
consumer_queue->GetChannel()->TakeChannelParcelable());
if (!queue_parcelable.IsValid()) {
- ALOGE(
- "BufferHubQueue::CreateConsumerQueueParcelable: Failed to create "
- "consumer queue parcelable.");
+ ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__);
return ErrorStatus(EINVAL);
}
@@ -169,8 +167,7 @@
}
if (ret < 0 && ret != -EINTR) {
- ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s",
- strerror(-ret));
+ ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret));
return false;
}
@@ -264,27 +261,26 @@
// wait will be tried again to acquire the newly imported buffer.
auto buffer_status = OnBufferAllocated();
if (!buffer_status) {
- ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s",
+ ALOGE("%s: Failed to import buffer: %s", __FUNCTION__,
buffer_status.GetErrorMessage().c_str());
}
} else if (events & EPOLLHUP) {
- ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!");
+ ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__);
hung_up_ = true;
} else {
- ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events);
+ ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events);
}
return {};
}
Status<void> BufferHubQueue::AddBuffer(
- const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "BufferHubQueue::AddBuffer: buffer_id=%d slot=%zu",
- buffer->id(), slot);
+ const std::shared_ptr<BufferHubBase>& buffer, size_t slot) {
+ ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(),
+ slot);
if (is_full()) {
- ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu",
- capacity_);
+ ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_);
return ErrorStatus(E2BIG);
}
@@ -303,7 +299,7 @@
const int ret =
epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
if (ret < 0) {
- ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+ ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__,
strerror(-ret));
return ErrorStatus(-ret);
}
@@ -315,17 +311,15 @@
}
Status<void> BufferHubQueue::RemoveBuffer(size_t slot) {
- ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", slot);
+ ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot);
if (buffers_[slot]) {
for (const auto& event_source : buffers_[slot]->GetEventSources()) {
const int ret =
epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
if (ret < 0) {
- ALOGE(
- "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll "
- "set: %s",
- strerror(-ret));
+ ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__,
+ strerror(-ret));
return ErrorStatus(-ret);
}
}
@@ -343,6 +337,15 @@
Status<void> BufferHubQueue::Enqueue(Entry entry) {
if (!is_full()) {
+ // Find and remove the enqueued buffer from unavailable_buffers_slot if
+ // exist.
+ auto enqueued_buffer_iter = std::find_if(
+ unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(),
+ [&entry](size_t slot) -> bool { return slot == entry.slot; });
+ if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) {
+ unavailable_buffers_slot_.erase(enqueued_buffer_iter);
+ }
+
available_buffers_.push(std::move(entry));
// Trigger OnBufferAvailable callback if registered.
@@ -351,17 +354,16 @@
return {};
} else {
- ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!");
+ ALOGE("%s: Buffer queue is full!", __FUNCTION__);
return ErrorStatus(E2BIG);
}
}
-Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout,
- size_t* slot) {
- ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(),
- timeout);
+Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout,
+ size_t* slot) {
+ ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout);
- PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", count());
+ PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count());
if (count() == 0) {
if (!WaitForBuffers(timeout))
@@ -372,10 +374,11 @@
PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
entry.slot);
- std::shared_ptr<BufferHubBuffer> buffer = std::move(entry.buffer);
+ std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer);
*slot = entry.slot;
available_buffers_.pop();
+ unavailable_buffers_slot_.push_back(*slot);
return {std::move(buffer)};
}
@@ -440,6 +443,10 @@
Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
uint64_t usage, size_t buffer_count) {
+ if (buffer_count == 0) {
+ return {std::vector<size_t>()};
+ }
+
if (capacity() + buffer_count > kMaxQueueCapacity) {
ALOGE(
"ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
@@ -473,7 +480,7 @@
// Note that import might (though very unlikely) fail. If so, buffer_handle
// will be closed and included in returned buffer_slots.
- if (AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+ if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)),
buffer_slot)) {
ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
buffer_slot);
@@ -481,10 +488,13 @@
}
}
- if (buffer_slots.size() == 0) {
- // Error out if no buffer is allocated and improted.
- ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffers: no buffer allocated.");
- ErrorStatus(ENOMEM);
+ if (buffer_slots.size() != buffer_count) {
+ // Error out if the count of imported buffer(s) is not correct.
+ ALOGE(
+ "ProducerQueue::AllocateBuffers: requested to import %zu "
+ "buffers, but actually imported %zu buffers.",
+ buffer_count, buffer_slots.size());
+ return ErrorStatus(ENOMEM);
}
return {std::move(buffer_slots)};
@@ -503,16 +513,11 @@
return status.error_status();
}
- if (status.get().size() == 0) {
- ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated.");
- ErrorStatus(ENOMEM);
- }
-
return {status.get()[0]};
}
Status<void> ProducerQueue::AddBuffer(
- const std::shared_ptr<BufferProducer>& buffer, size_t slot) {
+ const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) {
ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
id(), buffer->id(), slot);
// For producer buffer, we need to enqueue the newly added buffer
@@ -524,11 +529,46 @@
return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
}
+Status<size_t> ProducerQueue::InsertBuffer(
+ const std::shared_ptr<ProducerBuffer>& buffer) {
+ if (buffer == nullptr ||
+ !BufferHubDefs::isClientGained(buffer->buffer_state(),
+ buffer->client_state_mask())) {
+ ALOGE(
+ "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
+ "gained state.");
+ return ErrorStatus(EINVAL);
+ }
+
+ auto status_or_slot =
+ InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
+ buffer->cid());
+ if (!status_or_slot) {
+ ALOGE(
+ "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
+ "buffer_cid=%d, error: %s.",
+ buffer->cid(), status_or_slot.GetErrorMessage().c_str());
+ return status_or_slot.error_status();
+ }
+
+ size_t slot = status_or_slot.get();
+
+ // Note that we are calling AddBuffer() from the base class to explicitly
+ // avoid Enqueue() the ProducerBuffer.
+ auto status = BufferHubQueue::AddBuffer(buffer, slot);
+ if (!status) {
+ ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
+ status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+ return {slot};
+}
+
Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
auto status =
InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
if (!status) {
- ALOGE("ProducerQueue::RemoveBuffer: Failed to remove producer buffer: %s",
+ ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__,
status.GetErrorMessage().c_str());
return status.error_status();
}
@@ -536,39 +576,89 @@
return BufferHubQueue::RemoveBuffer(slot);
}
-Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
+Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
int timeout, size_t* slot, LocalHandle* release_fence) {
DvrNativeBufferMetadata canonical_meta;
return Dequeue(timeout, slot, &canonical_meta, release_fence);
}
-pdx::Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
+pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* release_fence) {
+ pdx::LocalHandle* release_fence, bool gain_posted_buffer) {
ATRACE_NAME("ProducerQueue::Dequeue");
if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
- ALOGE("ProducerQueue::Dequeue: Invalid parameter.");
+ ALOGE("%s: Invalid parameter.", __FUNCTION__);
return ErrorStatus(EINVAL);
}
- auto status = BufferHubQueue::Dequeue(timeout, slot);
- if (!status)
- return status.error_status();
-
- auto buffer = std::static_pointer_cast<BufferProducer>(status.take());
- const int ret = buffer->GainAsync(out_meta, release_fence);
+ std::shared_ptr<ProducerBuffer> buffer;
+ Status<std::shared_ptr<BufferHubBase>> dequeue_status =
+ BufferHubQueue::Dequeue(timeout, slot);
+ if (dequeue_status.ok()) {
+ buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take());
+ } else {
+ if (gain_posted_buffer) {
+ Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status =
+ ProducerQueue::DequeueUnacquiredBuffer(slot);
+ if (!dequeue_unacquired_status.ok()) {
+ ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__,
+ dequeue_unacquired_status.error());
+ return dequeue_unacquired_status.error_status();
+ }
+ buffer = dequeue_unacquired_status.take();
+ } else {
+ return dequeue_status.error_status();
+ }
+ }
+ const int ret =
+ buffer->GainAsync(out_meta, release_fence, gain_posted_buffer);
if (ret < 0 && ret != -EALREADY)
return ErrorStatus(-ret);
return {std::move(buffer)};
}
+Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer(
+ size_t* slot) {
+ if (unavailable_buffers_slot_.size() < 1) {
+ ALOGE(
+ "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in "
+ "acquired state if exist.",
+ __FUNCTION__);
+ return ErrorStatus(ENOMEM);
+ }
+
+ // Find the first buffer that is not in acquired state from
+ // unavailable_buffers_slot_.
+ for (auto iter = unavailable_buffers_slot_.begin();
+ iter != unavailable_buffers_slot_.end(); iter++) {
+ std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter);
+ if (buffer == nullptr) {
+ ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__,
+ static_cast<int>(*slot));
+ return ErrorStatus(EIO);
+ }
+ if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) {
+ *slot = *iter;
+ unavailable_buffers_slot_.erase(iter);
+ unavailable_buffers_slot_.push_back(*slot);
+ ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d",
+ __FUNCTION__, static_cast<int>(*slot));
+ return {std::move(buffer)};
+ }
+ }
+ ALOGE(
+ "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.",
+ __FUNCTION__);
+ return ErrorStatus(EBUSY);
+}
+
pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
if (capacity() != 0) {
ALOGE(
- "ProducerQueue::TakeAsParcelable: producer queue can only be taken out"
- " as a parcelable when empty. Current queue capacity: %zu",
- capacity());
+ "%s: producer queue can only be taken out as a parcelable when empty. "
+ "Current queue capacity: %zu",
+ __FUNCTION__, capacity());
return ErrorStatus(EINVAL);
}
@@ -592,17 +682,16 @@
: BufferHubQueue(std::move(handle)) {
auto status = ImportQueue();
if (!status) {
- ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s",
+ ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
status.GetErrorMessage().c_str());
Close(-status.error());
}
auto import_status = ImportBuffers();
if (import_status) {
- ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.",
- import_status.get());
+ ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get());
} else {
- ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s",
+ ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
import_status.GetErrorMessage().c_str());
}
}
@@ -611,14 +700,11 @@
auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
if (!status) {
if (status.error() == EBADR) {
- ALOGI(
- "ConsumerQueue::ImportBuffers: Queue is silent, no buffers "
- "imported.");
+ ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__);
return {0};
} else {
- ALOGE(
- "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s",
- status.GetErrorMessage().c_str());
+ ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__,
+ status.GetErrorMessage().c_str());
return status.error_status();
}
}
@@ -629,22 +715,22 @@
auto buffer_handle_slots = status.take();
for (auto& buffer_handle_slot : buffer_handle_slots) {
- ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d",
+ ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__,
buffer_handle_slot.first.value());
- std::unique_ptr<BufferConsumer> buffer_consumer =
- BufferConsumer::Import(std::move(buffer_handle_slot.first));
- if (!buffer_consumer) {
- ALOGE("ConsumerQueue::ImportBuffers: Failed to import buffer: slot=%zu",
+ std::unique_ptr<ConsumerBuffer> consumer_buffer =
+ ConsumerBuffer::Import(std::move(buffer_handle_slot.first));
+ if (!consumer_buffer) {
+ ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__,
buffer_handle_slot.second);
last_error = ErrorStatus(EPIPE);
continue;
}
auto add_status =
- AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+ AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second);
if (!add_status) {
- ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s",
+ ALOGE("%s: Failed to add buffer: %s", __FUNCTION__,
add_status.GetErrorMessage().c_str());
last_error = add_status;
} else {
@@ -659,20 +745,20 @@
}
Status<void> ConsumerQueue::AddBuffer(
- const std::shared_ptr<BufferConsumer>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
- id(), buffer->id(), slot);
+ const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) {
+ ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(),
+ buffer->id(), slot);
return BufferHubQueue::AddBuffer(buffer, slot);
}
-Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
int timeout, size_t* slot, void* meta, size_t user_metadata_size,
LocalHandle* acquire_fence) {
if (user_metadata_size != user_metadata_size_) {
ALOGE(
- "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer "
- "does not match metadata size (%zu) for the queue.",
- user_metadata_size, user_metadata_size_);
+ "%s: Metadata size (%zu) for the dequeuing buffer does not match "
+ "metadata size (%zu) for the queue.",
+ __FUNCTION__, user_metadata_size, user_metadata_size_);
return ErrorStatus(EINVAL);
}
@@ -687,19 +773,19 @@
if (metadata_src) {
memcpy(meta, metadata_src, user_metadata_size);
} else {
- ALOGW("ConsumerQueue::Dequeue: no user-defined metadata.");
+ ALOGW("%s: no user-defined metadata.", __FUNCTION__);
}
}
return status;
}
-Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
pdx::LocalHandle* acquire_fence) {
ATRACE_NAME("ConsumerQueue::Dequeue");
if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
- ALOGE("ConsumerQueue::Dequeue: Invalid parameter.");
+ ALOGE("%s: Invalid parameter.", __FUNCTION__);
return ErrorStatus(EINVAL);
}
@@ -707,7 +793,7 @@
if (!status)
return status.error_status();
- auto buffer = std::static_pointer_cast<BufferConsumer>(status.take());
+ auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take());
const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
if (ret < 0)
return ErrorStatus(-ret);
@@ -716,19 +802,18 @@
}
Status<void> ConsumerQueue::OnBufferAllocated() {
- ALOGD_IF(TRACE, "ConsumerQueue::OnBufferAllocated: queue_id=%d", id());
+ ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id());
auto status = ImportBuffers();
if (!status) {
- ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s",
+ ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
status.GetErrorMessage().c_str());
return ErrorStatus(status.error());
} else if (status.get() == 0) {
- ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!");
+ ALOGW("%s: No new buffers allocated!", __FUNCTION__);
return ErrorStatus(ENOBUFS);
} else {
- ALOGD_IF(TRACE,
- "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.",
+ ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__,
status.get());
return {};
}
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
index 2cd7c45..f705749 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
@@ -35,7 +35,7 @@
}
status_t res = parcel->writeUint32(Magic);
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
return res;
}
@@ -53,10 +53,10 @@
}
uint32_t out_magic = 0;
- status_t res = NO_ERROR;
+ status_t res = OK;
res = parcel->readUint32(&out_magic);
- if (res != NO_ERROR)
+ if (res != OK)
return res;
if (out_magic != Magic) {
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 8f7b0a1..74b4b3d 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -13,10 +13,11 @@
// in these headers and their dependencies.
#include <pdx/client.h>
#include <pdx/status.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/buffer_hub_queue_parcelable.h>
#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/consumer_buffer.h>
#include <private/dvr/epoll_file_descriptor.h>
+#include <private/dvr/producer_buffer.h>
#if defined(__clang__)
#pragma clang diagnostic pop
@@ -31,13 +32,13 @@
class ConsumerQueue;
-// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are
+// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are
// automatically re-requeued when released by the remote side.
class BufferHubQueue : public pdx::Client {
public:
using BufferAvailableCallback = std::function<void()>;
using BufferRemovedCallback =
- std::function<void(const std::shared_ptr<BufferHubBuffer>&)>;
+ std::function<void(const std::shared_ptr<BufferHubBase>&)>;
virtual ~BufferHubQueue() {}
@@ -57,10 +58,10 @@
uint32_t default_width() const { return default_width_; }
// Returns the default buffer height of this buffer queue.
- uint32_t default_height() const { return static_cast<uint32_t>(default_height_); }
+ uint32_t default_height() const { return default_height_; }
// Returns the default buffer format of this buffer queue.
- uint32_t default_format() const { return static_cast<uint32_t>(default_format_); }
+ uint32_t default_format() const { return default_format_; }
// Creates a new consumer in handle form for immediate transport over RPC.
pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
@@ -93,7 +94,7 @@
: -1;
}
- std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
+ std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const {
return buffers_[slot];
}
@@ -142,7 +143,7 @@
// Register a buffer for management by the queue. Used by subclasses to add a
// buffer to internal bookkeeping.
- pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBuffer>& buffer,
+ pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer,
size_t slot);
// Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only
@@ -158,8 +159,8 @@
// block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
// while specifying a timeout equal to zero cause Dequeue() to return
// immediately, even if no buffers are available.
- pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout,
- size_t* slot);
+ pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout,
+ size_t* slot);
// Waits for buffers to become available and adds them to the available queue.
bool WaitForBuffers(int timeout);
@@ -172,10 +173,10 @@
// per-buffer data.
struct Entry {
Entry() : slot(0) {}
- Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer, size_t in_slot,
+ Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot,
uint64_t in_index)
: buffer(in_buffer), slot(in_slot), index(in_index) {}
- Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer,
+ Entry(const std::shared_ptr<BufferHubBase>& in_buffer,
std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence,
size_t in_slot)
: buffer(in_buffer),
@@ -185,7 +186,7 @@
Entry(Entry&&) = default;
Entry& operator=(Entry&&) = default;
- std::shared_ptr<BufferHubBuffer> buffer;
+ std::shared_ptr<BufferHubBase> buffer;
std::unique_ptr<uint8_t[]> metadata;
pdx::LocalHandle fence;
size_t slot;
@@ -208,6 +209,14 @@
// Size of the metadata that buffers in this queue cary.
size_t user_metadata_size_{0};
+ // Buffers and related data that are available for dequeue.
+ std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
+ available_buffers_;
+
+ // Slot of the buffers that are not available for normal dequeue. For example,
+ // the slot of posted or acquired buffers in the perspective of a producer.
+ std::vector<size_t> unavailable_buffers_slot_;
+
private:
void Initialize();
@@ -250,11 +259,7 @@
// Tracks the buffers belonging to this queue. Buffers are stored according to
// "slot" in this vector. Each slot is a logical id of the buffer within this
// queue regardless of its queue position or presence in the ring buffer.
- std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_;
-
- // Buffers and related data that are available for dequeue.
- std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
- available_buffers_;
+ std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_;
// Keeps track with how many buffers have been added into the queue.
size_t capacity_{0};
@@ -302,12 +307,12 @@
return BASE::Create(std::move(handle));
}
- // Get a buffer producer. Note that the method doesn't check whether the
+ // Get a producer buffer. Note that the method doesn't check whether the
// buffer slot has a valid buffer that has been allocated already. When no
// buffer has been imported before it returns nullptr; otherwise it returns
- // a shared pointer to a BufferProducer.
- std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const {
- return std::static_pointer_cast<BufferProducer>(
+ // a shared pointer to a ProducerBuffer.
+ std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const {
+ return std::static_pointer_cast<ProducerBuffer>(
BufferHubQueue::GetBuffer(slot));
}
@@ -328,9 +333,16 @@
// Add a producer buffer to populate the queue. Once added, a producer buffer
// is available to use (i.e. in GAINED state).
- pdx::Status<void> AddBuffer(const std::shared_ptr<BufferProducer>& buffer,
+ pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer,
size_t slot);
+ // Inserts a ProducerBuffer into the queue. On success, the method returns the
+ // |slot| number where the new buffer gets inserted. Note that the buffer
+ // being inserted should be in Gain'ed state prior to the call and it's
+ // considered as already Dequeued when the function returns.
+ pdx::Status<size_t> InsertBuffer(
+ const std::shared_ptr<ProducerBuffer>& buffer);
+
// Remove producer buffer from the queue.
pdx::Status<void> RemoveBuffer(size_t slot) override;
@@ -342,14 +354,33 @@
// Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
// and caller should call Post() once it's done writing to release the buffer
// to the consumer side.
- pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
+ // @return a buffer in gained state, which was originally in released state.
+ pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
int timeout, size_t* slot, pdx::LocalHandle* release_fence);
- pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
+
+ // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
+ // and caller should call Post() once it's done writing to release the buffer
+ // to the consumer side.
+ //
+ // @param timeout to dequeue a buffer.
+ // @param slot is the slot of the output ProducerBuffer.
+ // @param release_fence for gaining a buffer.
+ // @param out_meta metadata of the output buffer.
+ // @param gain_posted_buffer whether to gain posted buffer if no released
+ // buffer is available to gain.
+ // @return a buffer in gained state, which was originally in released state if
+ // gain_posted_buffer is false, or in posted/released state if
+ // gain_posted_buffer is true.
+ // TODO(b/112007999): gain_posted_buffer true is only used to prevent
+ // libdvrtracking from starving when there are non-responding clients. This
+ // gain_posted_buffer param can be removed once libdvrtracking start to use
+ // the new AHardwareBuffer API.
+ pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* release_fence);
+ pdx::LocalHandle* release_fence, bool gain_posted_buffer = false);
// Enqueues a producer buffer in the queue.
- pdx::Status<void> Enqueue(const std::shared_ptr<BufferProducer>& buffer,
+ pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer,
size_t slot, uint64_t index) {
return BufferHubQueue::Enqueue({buffer, slot, index});
}
@@ -367,16 +398,26 @@
// arguments as the constructors.
explicit ProducerQueue(pdx::LocalChannelHandle handle);
ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
+
+ // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
+ // and caller should call Post() once it's done writing to release the buffer
+ // to the consumer side.
+ //
+ // @param slot the slot of the returned buffer.
+ // @return a buffer in gained state, which was originally in posted state or
+ // released state.
+ pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer(
+ size_t* slot);
};
class ConsumerQueue : public BufferHubQueue {
public:
- // Get a buffer consumer. Note that the method doesn't check whether the
+ // Get a consumer buffer. Note that the method doesn't check whether the
// buffer slot has a valid buffer that has been imported already. When no
// buffer has been imported before it returns nullptr; otherwise returns a
- // shared pointer to a BufferConsumer.
- std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const {
- return std::static_pointer_cast<BufferConsumer>(
+ // shared pointer to a ConsumerBuffer.
+ std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const {
+ return std::static_pointer_cast<ConsumerBuffer>(
BufferHubQueue::GetBuffer(slot));
}
@@ -394,23 +435,23 @@
// Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
// mode, and caller should call Releasse() once it's done writing to release
// the buffer to the producer side. |meta| is passed along from BufferHub,
- // The user of BufferProducer is responsible with making sure that the
+ // The user of ProducerBuffer is responsible with making sure that the
// Dequeue() is done with the corect metadata type and size with those used
// when the buffer is orignally created.
template <typename Meta>
- pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) {
return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
}
- pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) {
return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
}
- pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
int timeout, size_t* slot, void* meta, size_t user_metadata_size,
pdx::LocalHandle* acquire_fence);
- pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
pdx::LocalHandle* acquire_fence);
@@ -423,7 +464,7 @@
// is NOT available to use until the producer side |Post| it. |WaitForBuffers|
// will catch the |Post| and |Acquire| the buffer to make it available for
// consumer.
- pdx::Status<void> AddBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+ pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
size_t slot);
pdx::Status<void> OnBufferAllocated() override;
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 47a2734..6ae603b 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -1,7 +1,9 @@
#include <base/logging.h>
#include <binder/Parcel.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <dvr/dvr_api.h>
#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
#include <gtest/gtest.h>
#include <poll.h>
@@ -111,7 +113,7 @@
// Consumer acquires a buffer.
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
+ EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
EXPECT_EQ(mi.index, i);
@@ -122,6 +124,147 @@
}
}
+TEST_F(BufferHubQueueTest,
+ TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) {
+ ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+
+ // Allocate 3 buffers to use.
+ const size_t test_queue_capacity = 3;
+ for (int64_t i = 0; i < test_queue_capacity; i++) {
+ AllocateBuffer();
+ }
+ EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
+
+ size_t producer_slot, consumer_slot;
+ LocalHandle fence;
+ DvrNativeBufferMetadata mi, mo;
+
+ // Producer posts 2 buffers and remember their posted sequence.
+ std::deque<size_t> posted_slots;
+ for (int64_t i = 0; i < 2; i++) {
+ auto p1_status =
+ producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
+ EXPECT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(p1, nullptr);
+
+ // Producer should not be gaining posted buffer when there are still
+ // available buffers to gain.
+ auto found_iter =
+ std::find(posted_slots.begin(), posted_slots.end(), producer_slot);
+ EXPECT_EQ(found_iter, posted_slots.end());
+ posted_slots.push_back(producer_slot);
+
+ // Producer posts the buffer.
+ mi.index = i;
+ EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
+ }
+
+ // Consumer acquires one buffer.
+ auto c1_status =
+ consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence);
+ EXPECT_TRUE(c1_status.ok());
+ auto c1 = c1_status.take();
+ ASSERT_NE(c1, nullptr);
+ // Consumer should get the oldest posted buffer. No checks here.
+ // posted_slots[0] should be in acquired state now.
+ EXPECT_EQ(mo.index, 0);
+ // Consumer releases the buffer.
+ EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
+ // posted_slots[0] should be in released state now.
+
+ // Producer gain and post 2 buffers.
+ for (int64_t i = 0; i < 2; i++) {
+ auto p1_status =
+ producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
+ EXPECT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(p1, nullptr);
+
+ // The gained buffer should be the one in released state or the one haven't
+ // been use.
+ EXPECT_NE(posted_slots[1], producer_slot);
+
+ mi.index = i + 2;
+ EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
+ }
+
+ // Producer gains a buffer.
+ auto p1_status =
+ producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
+ EXPECT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(p1, nullptr);
+
+ // The gained buffer should be the oldest posted buffer.
+ EXPECT_EQ(posted_slots[1], producer_slot);
+
+ // Producer posts the buffer.
+ mi.index = 4;
+ EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
+}
+
+TEST_F(BufferHubQueueTest,
+ TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) {
+ ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+
+ // Allocate 4 buffers to use.
+ const size_t test_queue_capacity = 4;
+ for (int64_t i = 0; i < test_queue_capacity; i++) {
+ AllocateBuffer();
+ }
+ EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
+
+ // Post all allowed buffers and remember their posted sequence.
+ std::deque<size_t> posted_slots;
+ for (int64_t i = 0; i < test_queue_capacity; i++) {
+ size_t slot;
+ LocalHandle fence;
+ DvrNativeBufferMetadata mi, mo;
+
+ // Producer gains a buffer.
+ auto p1_status =
+ producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
+ EXPECT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(p1, nullptr);
+
+ // Producer should not be gaining posted buffer when there are still
+ // available buffers to gain.
+ auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot);
+ EXPECT_EQ(found_iter, posted_slots.end());
+ posted_slots.push_back(slot);
+
+ // Producer posts the buffer.
+ mi.index = i;
+ EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
+ }
+
+ // Gain posted buffers in sequence.
+ const int64_t nb_dequeue_all_times = 2;
+ for (int j = 0; j < nb_dequeue_all_times; ++j) {
+ for (int i = 0; i < test_queue_capacity; ++i) {
+ size_t slot;
+ LocalHandle fence;
+ DvrNativeBufferMetadata mi, mo;
+
+ // Producer gains a buffer.
+ auto p1_status =
+ producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
+ EXPECT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(p1, nullptr);
+
+ // The gained buffer should be the oldest posted buffer.
+ EXPECT_EQ(posted_slots[i], slot);
+
+ // Producer posts the buffer.
+ mi.index = i + test_queue_capacity * (j + 1);
+ EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
+ }
+ }
+}
+
TEST_F(BufferHubQueueTest, TestProducerConsumer) {
const size_t kBufferCount = 16;
size_t slot;
@@ -181,6 +324,42 @@
}
}
+TEST_F(BufferHubQueueTest, TestInsertBuffer) {
+ ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
+
+ consumer_queue_ = producer_queue_->CreateConsumerQueue();
+ ASSERT_TRUE(consumer_queue_ != nullptr);
+ EXPECT_EQ(producer_queue_->capacity(), 0);
+ EXPECT_EQ(consumer_queue_->capacity(), 0);
+
+ std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
+ kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
+ ASSERT_TRUE(p1 != nullptr);
+ ASSERT_EQ(p1->GainAsync(), 0);
+
+ // Inserting a posted buffer will fail.
+ DvrNativeBufferMetadata meta;
+ EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
+ auto status_or_slot = producer_queue_->InsertBuffer(p1);
+ EXPECT_FALSE(status_or_slot.ok());
+ EXPECT_EQ(status_or_slot.error(), EINVAL);
+
+ // Inserting a gained buffer will succeed.
+ std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create(
+ kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
+ ASSERT_EQ(p2->GainAsync(), 0);
+ ASSERT_TRUE(p2 != nullptr);
+ status_or_slot = producer_queue_->InsertBuffer(p2);
+ EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
+ // This is the first buffer inserted, should take slot 0.
+ size_t slot = status_or_slot.get();
+ EXPECT_EQ(slot, 0);
+
+ // Wait and expect the consumer to kick up the newly inserted buffer.
+ WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
+ EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
+}
+
TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
DvrNativeBufferMetadata mo;
@@ -203,7 +382,7 @@
// Dequeue all the buffers and keep track of them in an array. This prevents
// the producer queue ring buffer ref counts from interfering with the tests.
struct Entry {
- std::shared_ptr<BufferProducer> buffer;
+ std::shared_ptr<ProducerBuffer> buffer;
LocalHandle fence;
size_t slot;
};
@@ -211,8 +390,8 @@
for (size_t i = 0; i < kBufferCount; i++) {
Entry* entry = &buffers[i];
- auto producer_status = producer_queue_->Dequeue(
- kTimeoutMs, &entry->slot, &mo, &entry->fence);
+ auto producer_status =
+ producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence);
ASSERT_TRUE(producer_status.ok());
entry->buffer = producer_status.take();
ASSERT_NE(nullptr, entry->buffer);
@@ -409,7 +588,7 @@
mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
+ EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
@@ -513,7 +692,7 @@
size_t cs1, cs2;
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
- ASSERT_TRUE(c1_status.ok());
+ ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
ASSERT_EQ(consumer_queue_->count(), 0U);
@@ -528,6 +707,30 @@
ASSERT_EQ(cs2, ps2);
}
+TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) {
+ ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+ ASSERT_EQ(producer_queue_->capacity(), 0);
+ auto status = producer_queue_->AllocateBuffers(
+ kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+ kBufferUsage, /*buffer_count=*/2);
+ ASSERT_TRUE(status.ok());
+ std::vector<size_t> buffer_slots = status.take();
+ ASSERT_EQ(buffer_slots.size(), 2);
+ ASSERT_EQ(producer_queue_->capacity(), 2);
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) {
+ ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+ ASSERT_EQ(producer_queue_->capacity(), 0);
+ auto status = producer_queue_->AllocateBuffers(
+ kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+ kBufferUsage, /*buffer_count=*/0);
+ ASSERT_TRUE(status.ok());
+ std::vector<size_t> buffer_slots = status.take();
+ ASSERT_EQ(buffer_slots.size(), 0);
+ ASSERT_EQ(producer_queue_->capacity(), 0);
+}
+
TEST_F(BufferHubQueueTest, TestUsageSetMask) {
const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
ASSERT_TRUE(
@@ -645,10 +848,10 @@
size_t slot;
LocalHandle fence;
pdx::Status<void> status;
- pdx::Status<std::shared_ptr<BufferConsumer>> consumer_status;
- pdx::Status<std::shared_ptr<BufferProducer>> producer_status;
- std::shared_ptr<BufferConsumer> consumer_buffer;
- std::shared_ptr<BufferProducer> producer_buffer;
+ pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status;
+ pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status;
+ std::shared_ptr<ConsumerBuffer> consumer_buffer;
+ std::shared_ptr<ProducerBuffer> producer_buffer;
DvrNativeBufferMetadata mi, mo;
ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
@@ -705,7 +908,7 @@
ASSERT_NE(producer_buffer, nullptr);
ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok());
+ ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
}
status = producer_queue_->FreeAllBuffers();
@@ -748,7 +951,7 @@
Parcel parcel;
status_t res;
res = output_parcelable.writeToParcel(&parcel);
- EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(res, OK);
// After written into parcelable, the output_parcelable is still valid has
// keeps the producer channel alive.
@@ -770,7 +973,7 @@
EXPECT_FALSE(input_parcelable.IsValid());
res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(res, OK);
EXPECT_TRUE(input_parcelable.IsValid());
EXPECT_EQ(producer_queue_, nullptr);
@@ -791,7 +994,7 @@
auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
EXPECT_TRUE(s3.ok());
- std::shared_ptr<BufferProducer> p1 = s3.take();
+ std::shared_ptr<ProducerBuffer> p1 = s3.take();
ASSERT_NE(p1, nullptr);
producer_meta.timestamp = 42;
@@ -799,7 +1002,7 @@
// Make sure the buffer can be dequeued from consumer side.
auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s4.ok());
+ EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
EXPECT_EQ(consumer_queue_->capacity(), 1U);
auto consumer = s4.take();
@@ -840,7 +1043,7 @@
EXPECT_FALSE(input_parcelable.IsValid());
res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(res, OK);
EXPECT_TRUE(input_parcelable.IsValid());
consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
@@ -858,7 +1061,7 @@
auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
EXPECT_TRUE(s2.ok());
- std::shared_ptr<BufferProducer> p1 = s2.take();
+ std::shared_ptr<ProducerBuffer> p1 = s2.take();
ASSERT_NE(p1, nullptr);
producer_meta.timestamp = 42;
@@ -866,7 +1069,7 @@
// Make sure the buffer can be dequeued from consumer side.
auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s3.ok());
+ EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
EXPECT_EQ(consumer_queue_->capacity(), 1U);
auto consumer = s3.take();
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 4f10f83..8cc7081 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,8 +108,8 @@
void ConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
// Can connect the first time.
- ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
- kTestControlledByApp, &output));
+ ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+ kTestControlledByApp, &output));
}
// Dequeue a buffer in a 'correct' fashion.
@@ -170,7 +170,7 @@
TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
}
TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
@@ -186,26 +186,24 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int32_t value = -1;
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
EXPECT_EQ(kDefaultFormat, value);
- EXPECT_EQ(NO_ERROR,
- mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
EXPECT_LE(0, value);
EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
- EXPECT_EQ(NO_ERROR,
+ EXPECT_EQ(OK,
mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
- EXPECT_EQ(NO_ERROR,
- mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
EXPECT_EQ(kDefaultConsumerUsageBits, value);
}
@@ -243,14 +241,14 @@
// Request the buffer (pre-requisite for queueing)
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
// Queue the buffer back into the BQ
- ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
EXPECT_EQ(kDefaultWidth, output.width);
EXPECT_EQ(kDefaultHeight, output.height);
@@ -313,7 +311,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
sp<Fence> nullFence = NULL;
@@ -332,7 +330,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder().setScalingMode(-1).build();
@@ -353,7 +351,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder()
@@ -372,7 +370,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
// Should be able to cancel buffer after a dequeue.
- EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
}
TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -380,16 +378,15 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
+ ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
const int minBuffers = 1;
const int maxBuffers =
BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
- << "async mode: " << false;
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
+ ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers))
<< "bufferCount: " << minBuffers;
// Should now be able to dequeue up to minBuffers times
@@ -399,14 +396,14 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
}
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers));
// queue the first buffer to enable max dequeued buffer count checking
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
- ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
sp<Fence> fence;
for (int i = 0; i < maxBuffers; ++i) {
@@ -414,25 +411,24 @@
}
// Cancel a buffer, so we can decrease the buffer count
- ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence));
// Should now be able to decrease the max dequeued count by 1
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
}
TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
+ ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
const int minBuffers = 1;
const int maxBuffers =
BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
- << "async mode: " << false;
+ ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
// Buffer count was out of range
EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
<< "bufferCount: " << 0;
@@ -440,7 +436,7 @@
<< "bufferCount: " << maxBuffers + 1;
// Set max dequeue count to 2
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
// Dequeue 2 buffers
int slot = -1;
sp<Fence> fence;
@@ -478,7 +474,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
// Shouldn't be able to request buffer after disconnect.
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
}
@@ -489,14 +485,14 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
// Shouldn't be able to queue buffer after disconnect.
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
}
@@ -507,10 +503,10 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
// Shouldn't be able to cancel buffer after disconnect.
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
}
@@ -524,32 +520,32 @@
constexpr int maxDequeuedBuffers = 1;
int minUndequeuedBuffers;
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
- EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false));
- EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
+ EXPECT_EQ(OK, mProducer->setAsyncMode(false));
+ EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
// Dequeue, request, and queue all buffers.
for (int i = 0; i < maxCapacity; i++) {
EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
}
// Disconnect then reconnect.
- EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
EXPECT_NO_FATAL_FAILURE(ConnectProducer());
// Dequeue, request, and queue all buffers.
for (int i = 0; i < maxCapacity; i++) {
EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
}
- EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
}
TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) {
@@ -568,21 +564,21 @@
EXPECT_TRUE(dummy_producer_parcelable.IsValid());
// Disconnect producer can be taken out, but only to an invalid parcelable.
- ASSERT_EQ(mProducer->disconnect(kTestApi), NO_ERROR);
+ ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE);
EXPECT_FALSE(producer_parcelable.IsValid());
- EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), NO_ERROR);
+ EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
EXPECT_TRUE(producer_parcelable.IsValid());
// Should still be able to query buffer dimension after disconnect.
int32_t value = -1;
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth);
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), NO_ERROR);
+ EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK);
EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight);
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), NO_ERROR);
+ EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK);
EXPECT_EQ(value, kDefaultFormat);
// But connect to API will fail.
@@ -598,7 +594,7 @@
ASSERT_TRUE(new_producer != nullptr);
EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
kTestControlledByApp, &output),
- NO_ERROR);
+ OK);
}
} // namespace
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index 9c67881..8c354fb 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -16,8 +16,8 @@
"display_client.cpp",
"display_manager_client.cpp",
"display_protocol.cpp",
- "vsync_client.cpp",
"shared_buffer_helpers.cpp",
+ "vsync_service.cpp",
]
localIncludeFiles = [
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
index 974c231..fdeeb70 100644
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -1,7 +1,6 @@
#include "include/private/dvr/display_manager_client.h"
#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/buffer_hub_queue_client.h>
#include <private/dvr/display_protocol.h>
#include <utils/Log.h>
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
index caf3182..f8f5b3d 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -5,7 +5,6 @@
#include <hardware/hwcomposer.h>
#include <pdx/client.h>
#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/buffer_hub_queue_client.h>
#include <private/dvr/display_protocol.h>
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
deleted file mode 100644
index 1eeb80e..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_client.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
-#define ANDROID_DVR_VSYNC_CLIENT_H_
-
-#include <stdint.h>
-
-#include <pdx/client.h>
-
-struct dvr_vsync_client {};
-
-namespace android {
-namespace dvr {
-
-/*
- * VSyncClient is a remote interface to the vsync service in displayd.
- * This class is used to wait for and retrieve information about the
- * display vsync.
- */
-class VSyncClient : public pdx::ClientBase<VSyncClient>,
- public dvr_vsync_client {
- public:
- /*
- * Wait for the next vsync signal.
- * The timestamp (in ns) is written into *ts when ts is non-NULL.
- */
- int Wait(int64_t* timestamp_ns);
-
- /*
- * Returns the file descriptor used to communicate with the vsync system
- * service or -1 on error.
- */
- int GetFd();
-
- /*
- * Clears the select/poll/epoll event so that subsequent calls to
- * these will not signal until the next vsync.
- */
- int Acknowledge();
-
- /*
- * Get the timestamp of the last vsync event in ns. This call has
- * the same side effect on events as Acknowledge(), which saves
- * an IPC message.
- */
- int GetLastTimestamp(int64_t* timestamp_ns);
-
- /*
- * Get vsync scheduling info.
- * Get the estimated timestamp of the next GPU lens warp preemption event in
- * ns. Also returns the corresponding vsync count that the next lens warp
- * operation will target. This call has the same side effect on events as
- * Acknowledge(), which saves an IPC message.
- */
- int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns,
- uint32_t* next_vsync_count);
-
- private:
- friend BASE;
-
- VSyncClient();
- explicit VSyncClient(long timeout_ms);
-
- VSyncClient(const VSyncClient&) = delete;
- void operator=(const VSyncClient&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_VSYNC_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
new file mode 100644
index 0000000..152464a
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
+#define ANDROID_DVR_VSYNC_SERVICE_H_
+
+#include <binder/IInterface.h>
+
+namespace android {
+namespace dvr {
+
+class IVsyncCallback : public IInterface {
+ public:
+ DECLARE_META_INTERFACE(VsyncCallback)
+
+ enum {
+ ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
+ };
+
+ virtual status_t onVsync(int64_t vsync_timestamp) = 0;
+};
+
+class BnVsyncCallback : public BnInterface<IVsyncCallback> {
+ public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0);
+};
+
+// Register a callback with IVsyncService to be notified of vsync events and
+// timestamps. There's also a shared memory vsync buffer defined in
+// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
+// memory buffer that make it preferable in certain situations:
+//
+// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
+// is always available as long as surface flinger is running.
+//
+// 2. IVsyncService will make a binder callback when a vsync event occurs. This
+// allows the client to not write code to implement periodic "get the latest
+// vsync" calls, which is necessary with the vsync shared memory buffer.
+//
+// 3. The IVsyncService provides the real vsync timestamp reported by hardware
+// composer, whereas the vsync shared memory buffer only has predicted vsync
+// times.
+class IVsyncService : public IInterface {
+public:
+ DECLARE_META_INTERFACE(VsyncService)
+
+ static const char* GetServiceName() { return "vrflinger_vsync"; }
+
+ enum {
+ REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+ UNREGISTER_CALLBACK
+ };
+
+ virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
+ virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
+};
+
+class BnVsyncService : public BnInterface<IVsyncService> {
+ public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags = 0);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
deleted file mode 100644
index bc6cf6c..0000000
--- a/libs/vr/libdisplay/vsync_client.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "include/private/dvr/vsync_client.h"
-
-#include <log/log.h>
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/display_protocol.h>
-
-using android::dvr::display::VSyncProtocol;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-
-VSyncClient::VSyncClient(long timeout_ms)
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- VSyncProtocol::kClientPath),
- timeout_ms) {}
-
-VSyncClient::VSyncClient()
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- VSyncProtocol::kClientPath)) {}
-
-int VSyncClient::Wait(int64_t* timestamp_ns) {
- auto status = InvokeRemoteMethod<VSyncProtocol::Wait>();
- if (!status) {
- ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- if (timestamp_ns != nullptr) {
- *timestamp_ns = status.get();
- }
- return 0;
-}
-
-int VSyncClient::GetFd() { return event_fd(); }
-
-int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
- auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>();
- if (!status) {
- ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
- *timestamp_ns = status.get();
- return 0;
-}
-
-int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
- uint32_t* next_vsync_count) {
- if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
- return -EINVAL;
-
- auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>();
- if (!status) {
- ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *vsync_period_ns = status.get().vsync_period_ns;
- *timestamp_ns = status.get().timestamp_ns;
- *next_vsync_count = status.get().next_vsync_count;
- return 0;
-}
-
-int VSyncClient::Acknowledge() {
- auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>();
- ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
new file mode 100644
index 0000000..4668b98
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_service.cpp
@@ -0,0 +1,146 @@
+#include "include/private/dvr/vsync_service.h"
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+status_t BnVsyncCallback::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch (code) {
+ case ON_VSYNC: {
+ CHECK_INTERFACE(IVsyncCallback, data, reply);
+ int64_t vsync_timestamp = 0;
+ status_t result = data.readInt64(&vsync_timestamp);
+ if (result != OK) {
+ ALOGE("onVsync failed to readInt64: %d", result);
+ return result;
+ }
+ onVsync(vsync_timestamp);
+ return OK;
+ }
+ default: {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ }
+}
+
+class BpVsyncCallback : public BpInterface<IVsyncCallback> {
+public:
+ explicit BpVsyncCallback(const sp<IBinder>& impl)
+ : BpInterface<IVsyncCallback>(impl) {}
+ virtual ~BpVsyncCallback() {}
+
+ virtual status_t onVsync(int64_t vsync_timestamp) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IVsyncCallback::getInterfaceDescriptor());
+ if (result != OK) {
+ ALOGE("onVsync failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeInt64(vsync_timestamp);
+ if (result != OK) {
+ ALOGE("onVsync failed to writeInt64: %d", result);
+ return result;
+ }
+ result = remote()->transact(
+ BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY);
+ if (result != OK) {
+ ALOGE("onVsync failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
+
+
+status_t BnVsyncService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch (code) {
+ case REGISTER_CALLBACK: {
+ CHECK_INTERFACE(IVsyncService, data, reply);
+ sp<IBinder> callback;
+ status_t result = data.readStrongBinder(&callback);
+ if (result != OK) {
+ ALOGE("registerCallback failed to readStrongBinder: %d", result);
+ return result;
+ }
+ registerCallback(interface_cast<IVsyncCallback>(callback));
+ return OK;
+ }
+ case UNREGISTER_CALLBACK: {
+ CHECK_INTERFACE(IVsyncService, data, reply);
+ sp<IBinder> callback;
+ status_t result = data.readStrongBinder(&callback);
+ if (result != OK) {
+ ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
+ return result;
+ }
+ unregisterCallback(interface_cast<IVsyncCallback>(callback));
+ return OK;
+ }
+ default: {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ }
+}
+
+class BpVsyncService : public BpInterface<IVsyncService> {
+public:
+ explicit BpVsyncService(const sp<IBinder>& impl)
+ : BpInterface<IVsyncService>(impl) {}
+ virtual ~BpVsyncService() {}
+
+ virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IVsyncService::getInterfaceDescriptor());
+ if (result != OK) {
+ ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(IInterface::asBinder(callback));
+ if (result != OK) {
+ ALOGE("registerCallback failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(
+ BnVsyncService::REGISTER_CALLBACK, data, &reply);
+ if (result != OK) {
+ ALOGE("registerCallback failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+
+ virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IVsyncService::getInterfaceDescriptor());
+ if (result != OK) {
+ ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(IInterface::asBinder(callback));
+ if (result != OK) {
+ ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(
+ BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
+ if (result != OK) {
+ ALOGE("unregisterCallback failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 16906f5..81a9b2d 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -19,7 +19,14 @@
vendor_available: true,
}
+cc_library_headers {
+ name: "libdvr_private_headers",
+ export_include_dirs: ["."],
+ vendor_available: false,
+}
+
cflags = [
+ "-DDVR_TRACKING_IMPLEMENTED=0",
"-DLOG_TAG=\"libdvr\"",
"-DTRACE=0",
"-Wall",
@@ -36,7 +43,7 @@
"dvr_performance.cpp",
"dvr_pose.cpp",
"dvr_surface.cpp",
- "dvr_vsync.cpp",
+ "dvr_tracking.cpp",
]
static_libs = [
@@ -66,7 +73,7 @@
]
cc_library_shared {
- name: "libdvr",
+ name: "libdvr.google",
owner: "google",
cflags: cflags,
header_libs: ["libdvr_headers"],
@@ -81,7 +88,7 @@
// restricting function access in the shared lib makes it inconvenient to use in
// test code.
cc_library_static {
- name: "libdvr_static",
+ name: "libdvr_static.google",
owner: "google",
cflags: cflags,
header_libs: ["libdvr_headers"],
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
index d14f040..e099f6a 100644
--- a/libs/vr/libdvr/dvr_api.cpp
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -12,6 +12,7 @@
#include <dvr/dvr_display_manager.h>
#include <dvr/dvr_performance.h>
#include <dvr/dvr_surface.h>
+#include <dvr/dvr_tracking.h>
#include <dvr/dvr_vsync.h>
// Headers not yet moved into libdvr.
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
index baf1f2f..c11706f 100644
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -2,7 +2,8 @@
#include <android/hardware_buffer.h>
#include <dvr/dvr_shared_buffers.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/consumer_buffer.h>
+#include <private/dvr/producer_buffer.h>
#include <ui/GraphicBuffer.h>
#include "dvr_internal.h"
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index 571558a..1ca653c 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -8,10 +8,10 @@
#include "dvr_buffer_queue_internal.h"
using namespace android;
-using android::dvr::BufferConsumer;
-using android::dvr::BufferHubBuffer;
-using android::dvr::BufferProducer;
+using android::dvr::BufferHubBase;
+using android::dvr::ConsumerBuffer;
using android::dvr::ConsumerQueue;
+using android::dvr::ProducerBuffer;
using android::dvr::ProducerQueue;
using android::dvr::ProducerQueueConfigBuilder;
using android::dvr::UsagePolicy;
@@ -103,13 +103,13 @@
"DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
write_buffers_[slot]->write_buffer = std::move(buffer_status.take());
- const auto& buffer_producer = write_buffers_[slot]->write_buffer;
- if (!buffer_producer)
+ const auto& producer_buffer = write_buffers_[slot]->write_buffer;
+ if (!producer_buffer)
return -ENOMEM;
- if (width_ == buffer_producer->width() &&
- height_ == buffer_producer->height() &&
- format_ == buffer_producer->format()) {
+ if (width_ == producer_buffer->width() &&
+ height_ == producer_buffer->height() &&
+ format_ == producer_buffer->format()) {
// Producer queue returns a buffer matches the current request.
break;
}
@@ -122,14 +122,14 @@
"DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu "
"(w=%u, h=%u, fmt=%u) is different from the buffer returned "
"(w=%u, h=%u, fmt=%u). Need re-allocation.",
- slot, width_, height_, format_, buffer_producer->width(),
- buffer_producer->height(), buffer_producer->format());
+ slot, width_, height_, format_, producer_buffer->width(),
+ producer_buffer->height(), producer_buffer->format());
// Currently, we are not storing |layer_count| and |usage| in queue
// configuration. Copy those setup from the last buffer dequeued before we
// remove it.
- uint32_t old_layer_count = buffer_producer->layer_count();
- uint64_t old_usage = buffer_producer->usage();
+ uint32_t old_layer_count = producer_buffer->layer_count();
+ uint64_t old_usage = producer_buffer->usage();
// Allocate a new producer buffer with new buffer configs. Note that if
// there are already multiple available buffers in the queue, the next one
@@ -439,11 +439,11 @@
consumer_queue_->SetBufferRemovedCallback(nullptr);
} else {
consumer_queue_->SetBufferRemovedCallback(
- [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) {
+ [callback, context](const std::shared_ptr<BufferHubBase>& buffer) {
// When buffer is removed from the queue, the slot is already invalid.
auto read_buffer = std::make_unique<DvrReadBuffer>();
read_buffer->read_buffer =
- std::static_pointer_cast<BufferConsumer>(buffer);
+ std::static_pointer_cast<ConsumerBuffer>(buffer);
callback(read_buffer.release(), context);
});
}
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
index 852f9a4..7f631e3 100644
--- a/libs/vr/libdvr/dvr_display_manager.cpp
+++ b/libs/vr/libdvr/dvr_display_manager.cpp
@@ -2,18 +2,18 @@
#include <dvr/dvr_buffer.h>
#include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
#include <private/dvr/display_client.h>
#include <private/dvr/display_manager_client.h>
#include "dvr_internal.h"
#include "dvr_buffer_queue_internal.h"
-using android::dvr::BufferConsumer;
+using android::dvr::ConsumerBuffer;
using android::dvr::display::DisplayManagerClient;
-using android::dvr::display::SurfaceAttributes;
using android::dvr::display::SurfaceAttribute;
+using android::dvr::display::SurfaceAttributes;
using android::dvr::display::SurfaceState;
using android::pdx::rpc::EmptyVariant;
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
index de8bb96..f845cd8 100644
--- a/libs/vr/libdvr/dvr_internal.h
+++ b/libs/vr/libdvr/dvr_internal.h
@@ -16,18 +16,11 @@
namespace android {
namespace dvr {
-class BufferProducer;
-class BufferConsumer;
class IonBuffer;
DvrBuffer* CreateDvrBufferFromIonBuffer(
const std::shared_ptr<IonBuffer>& ion_buffer);
-DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer(
- const std::shared_ptr<BufferConsumer>& buffer_consumer);
-DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer(
- const std::shared_ptr<BufferProducer>& buffer_producer);
-
} // namespace dvr
} // namespace android
@@ -39,7 +32,7 @@
// DvrWriteBuffer acquired from a DvrWriteBufferQueue.
int32_t slot = -1;
- std::shared_ptr<android::dvr::BufferProducer> write_buffer;
+ std::shared_ptr<android::dvr::ProducerBuffer> write_buffer;
};
struct DvrReadBuffer {
@@ -48,7 +41,7 @@
// DvrReadBuffer acquired from a DvrReadBufferQueue.
int32_t slot = -1;
- std::shared_ptr<android::dvr::BufferConsumer> read_buffer;
+ std::shared_ptr<android::dvr::ConsumerBuffer> read_buffer;
};
struct DvrBuffer {
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
index a3a47f1..0c7ec01 100644
--- a/libs/vr/libdvr/dvr_surface.cpp
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -14,7 +14,6 @@
using android::dvr::display::Surface;
using android::dvr::display::SurfaceAttributes;
using android::dvr::display::SurfaceAttributeValue;
-using android::dvr::CreateDvrReadBufferFromBufferConsumer;
using android::pdx::rpc::EmptyVariant;
namespace {
diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp
new file mode 100644
index 0000000..73addc9
--- /dev/null
+++ b/libs/vr/libdvr/dvr_tracking.cpp
@@ -0,0 +1,82 @@
+#include "include/dvr/dvr_tracking.h"
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#if !DVR_TRACKING_IMPLEMENTED
+
+extern "C" {
+
+// This file provides the stub implementation of dvrTrackingXXX APIs. On
+// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the
+// build file.
+int dvrTrackingCameraCreate(DvrTrackingCamera**) {
+ ALOGE("dvrTrackingCameraCreate is not implemented.");
+ return -ENOSYS;
+}
+
+void dvrTrackingCameraDestroy(DvrTrackingCamera*) {
+ ALOGE("dvrTrackingCameraDestroy is not implemented.");
+}
+
+int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) {
+ ALOGE("dvrTrackingCameraCreate is not implemented.");
+ return -ENOSYS;
+}
+
+int dvrTrackingCameraStop(DvrTrackingCamera*) {
+ ALOGE("dvrTrackingCameraCreate is not implemented.");
+ return -ENOSYS;
+}
+
+int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) {
+ ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
+ return -ENOSYS;
+}
+
+void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) {
+ ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented.");
+}
+
+int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*,
+ DvrTrackingFeatureCallback, void*) {
+ ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
+ return -ENOSYS;
+}
+
+int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) {
+ ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
+ return -ENOSYS;
+}
+
+int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*,
+ DvrReadBuffer*,
+ const DvrTrackingBufferMetadata*,
+ bool*) {
+ ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented.");
+ return -ENOSYS;
+}
+
+int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) {
+ ALOGE("dvrTrackingSensorsCreate is not implemented.");
+ return -ENOSYS;
+}
+
+void dvrTrackingSensorsDestroy(DvrTrackingSensors*) {
+ ALOGE("dvrTrackingSensorsDestroy is not implemented.");
+}
+
+int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback,
+ void*) {
+ ALOGE("dvrTrackingStart is not implemented.");
+ return -ENOSYS;
+}
+
+int dvrTrackingSensorsStop(DvrTrackingSensors*) {
+ ALOGE("dvrTrackingStop is not implemented.");
+ return -ENOSYS;
+}
+
+} // extern "C"
+
+#endif // DVR_TRACKING_IMPLEMENTED
diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp
deleted file mode 100644
index 099240e..0000000
--- a/libs/vr/libdvr/dvr_vsync.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "include/dvr/dvr_vsync.h"
-
-#include <utils/Log.h>
-
-#include <private/dvr/vsync_client.h>
-
-extern "C" {
-
-struct DvrVSyncClient {
- std::unique_ptr<android::dvr::VSyncClient> client;
-};
-
-int dvrVSyncClientCreate(DvrVSyncClient** client_out) {
- auto client = android::dvr::VSyncClient::Create();
- if (!client) {
- ALOGE("dvrVSyncClientCreate: Failed to create vsync client!");
- return -EIO;
- }
-
- *client_out = new DvrVSyncClient{std::move(client)};
- return 0;
-}
-
-void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; }
-
-int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
- int64_t* next_timestamp_ns,
- uint32_t* next_vsync_count) {
- return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns,
- next_vsync_count);
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 80ffc82..e383bb2 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -10,6 +10,7 @@
#include <dvr/dvr_display_types.h>
#include <dvr/dvr_hardware_composer_types.h>
#include <dvr/dvr_pose.h>
+#include <dvr/dvr_tracking_types.h>
#ifdef __cplusplus
extern "C" {
@@ -50,6 +51,12 @@
typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue;
typedef struct DvrSurfaceAttribute DvrSurfaceAttribute;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrTrackingCamera DvrTrackingCamera;
+typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor;
+typedef struct DvrTrackingSensors DvrTrackingSensors;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
// Note: To avoid breaking others during active development, only modify this
// struct by appending elements to the end.
// If you do feel we should to re-arrange or remove elements, please make a
@@ -367,12 +374,45 @@
typedef int (*DvrPerformanceSetSchedulerPolicyPtr)(
pid_t task_id, const char* scheduler_policy);
+// dvr_tracking.h
+typedef int (*DvrTrackingCameraCreatePtr)(DvrTrackingCamera** out_camera);
+typedef void (*DvrTrackingCameraDestroyPtr)(DvrTrackingCamera* camera);
+typedef int (*DvrTrackingCameraStartPtr)(DvrTrackingCamera* camera,
+ DvrWriteBufferQueue* write_queue);
+typedef int (*DvrTrackingCameraStopPtr)(DvrTrackingCamera* camera);
+
+typedef int (*DvrTrackingFeatureExtractorCreatePtr)(
+ DvrTrackingFeatureExtractor** out_extractor);
+typedef void (*DvrTrackingFeatureExtractorDestroyPtr)(
+ DvrTrackingFeatureExtractor* extractor);
+typedef void (*DvrTrackingFeatureCallback)(void* context,
+ const DvrTrackingFeatures* event);
+typedef int (*DvrTrackingFeatureExtractorStartPtr)(
+ DvrTrackingFeatureExtractor* extractor,
+ DvrTrackingFeatureCallback callback, void* context);
+typedef int (*DvrTrackingFeatureExtractorStopPtr)(
+ DvrTrackingFeatureExtractor* extractor);
+typedef int (*DvrTrackingFeatureExtractorProcessBufferPtr)(
+ DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer,
+ const DvrTrackingBufferMetadata* metadata, bool* out_skipped);
+
+typedef void (*DvrTrackingSensorEventCallback)(void* context,
+ DvrTrackingSensorEvent* event);
+typedef int (*DvrTrackingSensorsCreatePtr)(DvrTrackingSensors** out_sensors,
+ const char* mode);
+typedef void (*DvrTrackingSensorsDestroyPtr)(DvrTrackingSensors* sensors);
+typedef int (*DvrTrackingSensorsStartPtr)(
+ DvrTrackingSensors* sensors, DvrTrackingSensorEventCallback callback,
+ void* context);
+typedef int (*DvrTrackingSensorsStopPtr)(DvrTrackingSensors* sensors);
+
// The buffer metadata that an Android Surface (a.k.a. ANativeWindow)
// will populate. A DvrWriteBufferQueue must be created with this metadata iff
// ANativeWindow access is needed. Please do not remove, modify, or reorder
// existing data members. If new fields need to be added, please take extra care
// to make sure that new data field is padded properly the size of the struct
// stays same.
+// TODO(b/118893702): move the definition to libnativewindow or libui
struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata {
#ifdef __cplusplus
DvrNativeBufferMetadata()
@@ -426,11 +466,11 @@
// Only applicable for metadata retrieved from GainAsync. This indicates which
// consumer has pending fence that producer should epoll on.
- uint64_t release_fence_mask;
+ uint32_t release_fence_mask;
// Reserved bytes for so that the struct is forward compatible and padding to
// 104 bytes so the size is a multiple of 8.
- int32_t reserved[8];
+ int32_t reserved[9];
};
#ifdef __cplusplus
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index f0d8ec6..3006b61 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -85,9 +85,9 @@
DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents);
// V-Sync client
-DVR_V1_API_ENTRY(VSyncClientCreate);
-DVR_V1_API_ENTRY(VSyncClientDestroy);
-DVR_V1_API_ENTRY(VSyncClientGetSchedInfo);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientCreate);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientDestroy);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientGetSchedInfo);
// Display surface
DVR_V1_API_ENTRY(SurfaceCreate);
@@ -181,3 +181,20 @@
DVR_V1_API_ENTRY(PoseClientGetDataReader);
DVR_V1_API_ENTRY(PoseClientDataCapture);
DVR_V1_API_ENTRY(PoseClientDataReaderDestroy);
+
+// Tracking
+DVR_V1_API_ENTRY(TrackingCameraCreate);
+DVR_V1_API_ENTRY(TrackingCameraDestroy);
+DVR_V1_API_ENTRY(TrackingCameraStart);
+DVR_V1_API_ENTRY(TrackingCameraStop);
+
+DVR_V1_API_ENTRY(TrackingFeatureExtractorCreate);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorDestroy);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorStart);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorStop);
+DVR_V1_API_ENTRY(TrackingFeatureExtractorProcessBuffer);
+
+DVR_V1_API_ENTRY(TrackingSensorsCreate);
+DVR_V1_API_ENTRY(TrackingSensorsDestroy);
+DVR_V1_API_ENTRY(TrackingSensorsStart);
+DVR_V1_API_ENTRY(TrackingSensorsStop);
diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h
index 943384f..fe59d1f 100644
--- a/libs/vr/libdvr/include/dvr/dvr_deleter.h
+++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h
@@ -20,7 +20,6 @@
typedef struct DvrSurface DvrSurface;
typedef struct DvrHwcClient DvrHwcClient;
typedef struct DvrHwcFrame DvrHwcFrame;
-typedef struct DvrVSyncClient DvrVSyncClient;
void dvrBufferDestroy(DvrBuffer* buffer);
void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
@@ -32,7 +31,6 @@
void dvrSurfaceDestroy(DvrSurface* surface);
void dvrHwcClientDestroy(DvrHwcClient* client);
void dvrHwcFrameDestroy(DvrHwcFrame* frame);
-void dvrVSyncClientDestroy(DvrVSyncClient* client);
__END_DECLS
@@ -55,7 +53,6 @@
void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); }
void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); }
void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); }
- void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); }
};
// Helper to define unique pointers for DVR object types.
@@ -73,7 +70,6 @@
using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>;
using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>;
using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>;
-using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>;
// TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into
// the relevant constructors.
diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking.h b/libs/vr/libdvr/include/dvr/dvr_tracking.h
new file mode 100644
index 0000000..5e388f3
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_tracking.h
@@ -0,0 +1,185 @@
+#ifndef ANDROID_DVR_TRACKING_H_
+#define ANDROID_DVR_TRACKING_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_tracking_types.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrTrackingCamera DvrTrackingCamera;
+typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor;
+typedef struct DvrTrackingSensors DvrTrackingSensors;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
+// The callback for DvrTrackingFeatureExtractor that will deliver the feature
+// events. This callback is passed to dvrTrackingFeatureExtractorStart.
+typedef void (*DvrTrackingFeatureCallback)(void* context,
+ const DvrTrackingFeatures* event);
+
+// The callback for DvrTrackingSensors session that will deliver the events.
+// This callback is passed to dvrTrackingSensorsStart.
+typedef void (*DvrTrackingSensorEventCallback)(void* context,
+ DvrTrackingSensorEvent* event);
+
+// Creates a DvrTrackingCamera session.
+//
+// On creation, the session is not in operating mode. Client code must call
+// dvrTrackingCameraStart to bootstrap the underlying camera stack.
+//
+// There is no plan to expose camera configuration through this API. All camera
+// parameters are determined by the system optimized for better tracking
+// results. See b/78662281 for detailed deprecation plan of this API and the
+// Stage 2 of VR tracking data source refactoring.
+//
+// @param out_camera The pointer of a DvrTrackingCamera will be filled here if
+// the method call succeeds.
+// @return Zero on success, or negative error code.
+int dvrTrackingCameraCreate(DvrTrackingCamera** out_camera);
+
+// Destroys a DvrTrackingCamera handle.
+//
+// @param camera The DvrTrackingCamera of interest.
+void dvrTrackingCameraDestroy(DvrTrackingCamera* camera);
+
+// Starts the DvrTrackingCamera.
+//
+// On successful return, all DvrReadBufferQueue's associated with the given
+// write_queue will start to receive buffers from the camera stack. Note that
+// clients of this API should not assume the buffer dimension, format, and/or
+// usage of the outcoming buffers, as they are governed by the underlying camera
+// logic. Also note that it's the client's responsibility to consume buffers
+// from DvrReadBufferQueue on time and return them back to the producer;
+// otherwise the camera stack might be blocked.
+//
+// @param camera The DvrTrackingCamera of interest.
+// @param write_queue A DvrWriteBufferQueue that the camera stack can use to
+// populate the buffer into. The queue must be empty and the camera stack
+// will request buffer allocation with proper buffer dimension, format, and
+// usage. Note that the write queue must be created with user_metadata_size
+// set to sizeof(DvrTrackingBufferMetadata). On success, the write_queue
+// handle will become invalid and the ownership of the queue handle will be
+// transferred into the camera; otherwise, the write_queue handle will keep
+// untouched and the caller still has the ownership.
+// @return Zero on success, or negative error code.
+int dvrTrackingCameraStart(DvrTrackingCamera* camera,
+ DvrWriteBufferQueue* write_queue);
+
+// Stops the DvrTrackingCamera.
+//
+// On successful return, the DvrWriteBufferQueue set during
+// dvrTrackingCameraStart will stop getting new buffers from the camera stack.
+//
+// @param camera The DvrTrackingCamera of interest.
+// @return Zero on success, or negative error code.
+int dvrTrackingCameraStop(DvrTrackingCamera* camera);
+
+// Creates a DvrTrackingSensors session.
+//
+// This will initialize but not start device sensors (gyro / accel). Upon
+// successfull creation, the clients can call dvrTrackingSensorsStart to start
+// receiving sensor events.
+//
+// @param out_sensors The pointer of a DvrTrackingSensors will be filled here if
+// the method call succeeds.
+// @param mode The sensor mode.
+// mode="ndk": Use the Android NDK.
+// mode="direct": Use direct mode sensors (lower latency).
+// @return Zero on success, or negative error code.
+int dvrTrackingSensorsCreate(DvrTrackingSensors** out_sensors,
+ const char* mode);
+
+// Destroys a DvrTrackingSensors session.
+//
+// @param sensors The DvrTrackingSensors struct to destroy.
+void dvrTrackingSensorsDestroy(DvrTrackingSensors* sensors);
+
+// Starts the tracking sensor session.
+//
+// This will start the device sensors and start pumping the feature and sensor
+// events as they arrive.
+//
+// @param client A tracking client created by dvrTrackingSensorsCreate.
+// @param context A client supplied pointer that will be passed to the callback.
+// @param callback A callback that will receive the sensor events on an
+// arbitrary thread.
+// @return Zero on success, or negative error code.
+int dvrTrackingSensorsStart(DvrTrackingSensors* sensors,
+ DvrTrackingSensorEventCallback callback,
+ void* context);
+
+// Stops a DvrTrackingSensors session.
+//
+// This will stop the device sensors. dvrTrackingSensorsStart can be called to
+// restart them again.
+//
+// @param client A tracking client created by dvrTrackingClientCreate.
+// @return Zero on success, or negative error code.
+int dvrTrackingSensorsStop(DvrTrackingSensors* sensors);
+
+// Creates a tracking feature extractor.
+//
+// This will initialize but not start the feature extraction session. Upon
+// successful creation, the client can call dvrTrackingFeatureExtractorStart to
+// start receiving features.
+//
+// @param out_extractor The pointer of a DvrTrackingFeatureExtractor will be
+// filled here if the method call succeeds.
+int dvrTrackingFeatureExtractorCreate(
+ DvrTrackingFeatureExtractor** out_extractor);
+
+// Destroys a tracking feature extractor.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor* extractor);
+
+// Starts the tracking feature extractor.
+//
+// This will start the extractor and start pumping the output feature events to
+// the registered callback. Note that this method will create one or more
+// threads to handle feature processing.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor* extractor,
+ DvrTrackingFeatureCallback callback,
+ void* context);
+
+// Stops the tracking feature extractor.
+//
+// This will stop the extractor session and clean up all internal resourcse
+// related to this extractor. On succssful return, all internal therad started
+// by dvrTrackingFeatureExtractorStart should be stopped.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor* extractor);
+
+// Processes one buffer to extract features from.
+//
+// The buffer will be sent over to DSP for feature extraction. Once the process
+// is done, the processing thread will invoke DvrTrackingFeatureCallback with
+// newly extracted features. Note that not all buffers will be processed, as the
+// underlying DSP can only process buffers at a certain framerate. If a buffer
+// needs to be skipped, out_skipped filed will be set to true. Also note that
+// for successfully processed stereo buffer, two callbacks (one for each eye)
+// will be fired.
+//
+// @param extractor The DvrTrackingFeatureExtractor to destroy.
+// @param buffer The buffer to extract features from. Note that the buffer must
+// be in acquired state for the buffer to be processed. Also note that the
+// buffer will be released back to its producer on successful return of the
+// method.
+// @param metadata The metadata associated with the buffer. Should be populated
+// by DvrTrackingCamera session as user defined metadata.
+// @param out_skipped On successful return, the field will be set to true iff
+// the buffer was skipped; and false iff the buffer was processed. This
+// field is optional and nullptr can be passed here to ignore the field.
+// @return Zero on success, or negative error code.
+int dvrTrackingFeatureExtractorProcessBuffer(
+ DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer,
+ const DvrTrackingBufferMetadata* metadata, bool* out_skipped);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_TRACKING_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking_types.h b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h
new file mode 100644
index 0000000..81310d2
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h
@@ -0,0 +1,104 @@
+#ifndef ANDROID_DVR_TRACKING_TYPES_H_
+#define ANDROID_DVR_TRACKING_TYPES_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrTrackingBufferMetadata {
+ // Specifies the source of this image.
+ uint32_t camera_mask;
+ // Specifies the memory format of this image.
+ uint32_t format;
+ /// The width of the image data.
+ uint32_t width;
+ /// The height of the image data.
+ uint32_t height;
+ /// The number of bytes per scanline of image data.
+ uint32_t stride;
+ /// The frame number of this image.
+ int32_t frame_number;
+ /// The timestamp of this image in nanoseconds. Taken in the middle of the
+ /// exposure interval.
+ int64_t timestamp_ns;
+ // This is the timestamp for recording when the system using the HAL
+ // received the callback. It will not be populated by the HAL.
+ int64_t callback_timestamp_ns;
+ /// The exposure duration of this image in nanoseconds.
+ int64_t exposure_duration_ns;
+} DvrTrackingBufferMetadata;
+
+// Represents a set of features extracted from a camera frame. Note that this
+// should be in sync with TangoHalCallbacks defined in tango-hal.h.
+typedef struct DvrTrackingFeatures {
+ // Specifies the source of the features.
+ uint32_t camera_mask;
+
+ // This is unused.
+ uint32_t unused;
+
+ // The timestamp in nanoseconds from the image that generated the features.
+ // Taken in the middle of the exposure interval.
+ int64_t timestamp_ns;
+
+ // This is the timestamp for recording when the system using the HAL
+ // received the callback. It will not be populated by the HAL.
+ int64_t callback_timestamp_ns;
+
+ // The frame number from the image that generated the features.
+ int64_t frame_number;
+
+ // The number of features.
+ int count;
+
+ // An array of 2D image points for each feature in the current image.
+ // This is sub-pixel refined extremum location at the fine resolution.
+ float (*positions)[2];
+
+ // The id of these measurements.
+ int32_t* ids;
+
+ // The feature descriptors.
+ uint64_t (*descriptors)[8];
+
+ // Laplacian scores for each feature.
+ float* scores;
+
+ // Is this feature a minimum or maximum in the Laplacian image.
+ // 0 if the feature is a maximum, 1 if it is a minimum.
+ int32_t* is_minimum;
+
+ // This corresponds to the sub-pixel index of the laplacian image
+ // that the extremum was found.
+ float* scales;
+
+ // Computed orientation of keypoint as part of FREAK extraction, except
+ // it's represented in radians and measured anti-clockwise.
+ float* angles;
+
+ // Edge scores for each feature.
+ float* edge_scores;
+} DvrTrackingFeatures;
+
+// Represents a sensor event.
+typedef struct DvrTrackingSensorEvent {
+ // The sensor type.
+ int32_t sensor;
+
+ // Event type.
+ int32_t type;
+
+ // This is the timestamp recorded from the device. Taken in the middle
+ // of the integration interval and adjusted for any low pass filtering.
+ int64_t timestamp_ns;
+
+ // The event data.
+ float x;
+ float y;
+ float z;
+} DvrTrackingSensorEvent;
+
+__END_DECLS
+
+#endif // ANDROID_DVR_TRACKING_TYPES_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h
index 87fdf31..498bb5c 100644
--- a/libs/vr/libdvr/include/dvr/dvr_vsync.h
+++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h
@@ -6,8 +6,6 @@
__BEGIN_DECLS
-typedef struct DvrVSyncClient DvrVSyncClient;
-
// Represents a vsync sample. The size of this struct is 32 bytes.
typedef struct __attribute__((packed, aligned(16))) DvrVsync {
// The timestamp for the last vsync in nanoseconds.
@@ -29,19 +27,6 @@
uint8_t padding[8];
} DvrVsync;
-// Creates a new client to the system vsync service.
-int dvrVSyncClientCreate(DvrVSyncClient** client_out);
-
-// Destroys the vsync client.
-void dvrVSyncClientDestroy(DvrVSyncClient* client);
-
-// Get the estimated timestamp of the next GPU lens warp preemption event in/
-// ns. Also returns the corresponding vsync count that the next lens warp
-// operation will target.
-int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
- int64_t* next_timestamp_ns,
- uint32_t* next_vsync_count);
-
__END_DECLS
#endif // ANDROID_DVR_VSYNC_H_
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index 1ae75fb..357dffe 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -12,38 +12,36 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-shared_libraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
-]
-
-static_libraries = [
- "libdvr_static",
- "libchrome",
- "libdvrcommon",
- "libdisplay",
- "libbroadcastring",
-]
-
cc_test {
srcs: [
"dvr_display_manager-test.cpp",
"dvr_named_buffer-test.cpp",
+ "dvr_tracking-test.cpp",
],
header_libs: ["libdvr_headers"],
- static_libs: static_libraries,
- shared_libs: shared_libraries,
+ static_libs: [
+ "libdvr_static.google",
+ "libchrome",
+ "libdvrcommon",
+ "libdisplay",
+ "libbroadcastring",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbufferhubqueue",
+ "libcutils",
+ "libgui",
+ "liblog",
+ "libhardware",
+ "libui",
+ "libutils",
+ "libnativewindow",
+ "libpdx_default_transport",
+ ],
cflags: [
+ "-DDVR_TRACKING_IMPLEMENTED=0",
"-DLOG_TAG=\"dvr_api-test\"",
"-DTRACE=0",
"-Wno-missing-field-initializers",
@@ -51,4 +49,59 @@
"-g",
],
name: "dvr_api-test",
+
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
+}
+
+cc_test {
+ name: "dvr_buffer_queue-test",
+
+ // Includes the dvr_api.h header. Tests should only include "dvr_api.h",
+ // and shall only get access to |dvrGetApi|, as other symbols are hidden
+ // from the library.
+ include_dirs: ["frameworks/native/libs/vr/libdvr/include"],
+
+ srcs: ["dvr_buffer_queue-test.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ ],
+
+ cflags: [
+ "-DTRACE=0",
+ "-O2",
+ "-g",
+ ],
+
+ // DTS Should only link to NDK libraries.
+ sdk_version: "26",
+ stl: "c++_static",
+}
+
+cc_test {
+ name: "dvr_display-test",
+
+ include_dirs: [
+ "frameworks/native/libs/vr/libdvr/include",
+ "frameworks/native/libs/nativewindow/include",
+ ],
+
+ srcs: ["dvr_display-test.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ ],
+
+ cflags: [
+ "-DTRACE=0",
+ "-O2",
+ "-g",
+ ],
+
+ // DTS Should only link to NDK libraries.
+ sdk_version: "26",
+ stl: "c++_static",
}
diff --git a/libs/vr/libdvr/tests/Android.mk b/libs/vr/libdvr/tests/Android.mk
deleted file mode 100644
index 0f3840d..0000000
--- a/libs/vr/libdvr/tests/Android.mk
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2018 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# TODO(b/73133405): Currently, building cc_test against NDK using Android.bp
-# doesn't work well. Migrate to use Android.bp once b/73133405 gets fixed.
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= dvr_buffer_queue-test
-
-# Includes the dvr_api.h header. Tests should only include "dvr_api.h",
-# and shall only get access to |dvrGetApi|, as other symbols are hidden from the
-# library.
-LOCAL_C_INCLUDES := \
- frameworks/native/libs/vr/libdvr/include \
-
-LOCAL_SANITIZE := thread
-
-LOCAL_SRC_FILES := dvr_buffer_queue-test.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid \
- liblog \
-
-LOCAL_CFLAGS := \
- -DTRACE=0 \
- -O2 \
- -g \
-
-# DTS Should only link to NDK libraries.
-LOCAL_SDK_VERSION := 26
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_NATIVE_TEST)
-
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= dvr_display-test
-
-LOCAL_C_INCLUDES := \
- frameworks/native/libs/vr/libdvr/include \
- frameworks/native/libs/nativewindow/include
-
-LOCAL_SANITIZE := thread
-
-LOCAL_SRC_FILES := dvr_display-test.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid \
- liblog
-
-LOCAL_CFLAGS := \
- -DTRACE=0 \
- -O2 \
- -g
-
-# DTS Should only link to NDK libraries.
-LOCAL_SDK_VERSION := 26
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_NATIVE_TEST)
\ No newline at end of file
diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h
index d8359e7..5d2ec28 100644
--- a/libs/vr/libdvr/tests/dvr_api_test.h
+++ b/libs/vr/libdvr/tests/dvr_api_test.h
@@ -14,7 +14,7 @@
// workaround for an Android NDK bug. See more detail:
// https://github.com/android-ndk/ndk/issues/360
flags |= RTLD_NODELETE;
- platform_handle_ = dlopen("libdvr.so", flags);
+ platform_handle_ = dlopen("libdvr.google.so", flags);
ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing.";
auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>(
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
index 2d5f004..df06097 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -62,7 +62,7 @@
buffer_removed_count_);
}
- DvrWriteBufferQueue* write_queue_{nullptr};
+ DvrWriteBufferQueue* write_queue_ = nullptr;
int buffer_available_count_{0};
int buffer_removed_count_{0};
};
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index c9a5c09..ed72577 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -1,5 +1,6 @@
#include <android-base/properties.h>
#include <base/logging.h>
+#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <poll.h>
@@ -479,6 +480,11 @@
#endif
TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
+ // This test doesn't apply to standalone vr devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ return;
+ }
+
// Get surface state and verify there are no surfaces.
ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -518,6 +524,11 @@
}
TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
+ // This test doesn't apply to standalone vr devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ return;
+ }
+
// Get surface state and verify there are no surfaces.
ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -757,6 +768,11 @@
}
TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
+ // This test doesn't apply to standalone vr devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ return;
+ }
+
// Create an application surface.
auto surface_status = CreateApplicationSurface();
ASSERT_STATUS_OK(surface_status);
@@ -825,6 +841,11 @@
}
TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
+ // This test doesn't apply to standalone vr devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ return;
+ }
+
// Create an application surface.
auto surface_status = CreateApplicationSurface();
ASSERT_STATUS_OK(surface_status);
diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp
new file mode 100644
index 0000000..3b6d6e1
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_tracking-test.cpp
@@ -0,0 +1,103 @@
+#include <android/log.h>
+#include <gtest/gtest.h>
+
+#include "dvr_api_test.h"
+
+namespace {
+
+class DvrTrackingTest : public DvrApiTest {};
+
+#if DVR_TRACKING_IMPLEMENTED
+
+TEST_F(DvrTrackingTest, Implemented) {
+ ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
+ ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
+ ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
+
+ ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
+}
+
+TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) {
+ int ret;
+ ret = api_.TrackingCameraCreate(nullptr);
+ EXPECT_EQ(ret, -EINVAL);
+
+ DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42);
+ ret = api_.TrackingCameraCreate(&camera);
+ EXPECT_EQ(ret, -EINVAL);
+}
+
+TEST_F(DvrTrackingTest, CameraCreateDestroy) {
+ DvrTrackingCamera* camera = nullptr;
+ int ret = api_.TrackingCameraCreate(&camera);
+
+ EXPECT_EQ(ret, 0);
+ ASSERT_TRUE(camera != nullptr);
+
+ api_.TrackingCameraDestroy(camera);
+}
+
+TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) {
+ int ret;
+ ret = api_.TrackingFeatureExtractorCreate(nullptr);
+ EXPECT_EQ(ret, -EINVAL);
+
+ DvrTrackingFeatureExtractor* camera =
+ reinterpret_cast<DvrTrackingFeatureExtractor*>(42);
+ ret = api_.TrackingFeatureExtractorCreate(&camera);
+ EXPECT_EQ(ret, -EINVAL);
+}
+
+TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) {
+ DvrTrackingFeatureExtractor* camera = nullptr;
+ int ret = api_.TrackingFeatureExtractorCreate(&camera);
+
+ EXPECT_EQ(ret, 0);
+ ASSERT_TRUE(camera != nullptr);
+
+ api_.TrackingFeatureExtractorDestroy(camera);
+}
+
+#else // !DVR_TRACKING_IMPLEMENTED
+
+TEST_F(DvrTrackingTest, NotImplemented) {
+ ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
+ ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr);
+ ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
+ ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
+
+ EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS);
+ EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS);
+ EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS);
+
+ ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
+ ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
+
+ EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS);
+ EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr),
+ -ENOSYS);
+ EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS);
+ EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr,
+ nullptr, nullptr),
+ -ENOSYS);
+
+ ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr);
+ ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr);
+ ASSERT_TRUE(api_.TrackingSensorsStart != nullptr);
+ ASSERT_TRUE(api_.TrackingSensorsStop != nullptr);
+
+ EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS);
+ EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS);
+ EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS);
+}
+
+#endif // DVR_TRACKING_IMPLEMENTED
+
+} // namespace
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
index 853d53c..f5a2d5e 100644
--- a/libs/vr/libpdx/private/pdx/service.h
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -59,9 +59,18 @@
virtual ~Channel() {}
/*
+ * Accessors to the pid of the last active client.
+ */
+ pid_t GetActiveProcessId() const { return client_pid_; }
+ void SetActiveProcessId(pid_t pid) { client_pid_ = pid; }
+
+ /*
* Utility to get a shared_ptr reference from the channel context pointer.
*/
static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+
+ private:
+ pid_t client_pid_ = 0;
};
/*
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
index 68b8dd7..3769162 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -318,13 +318,7 @@
PDX_TRACE_NAME("Message::ReplyFileHandle");
auto svc = service_.lock();
if (!replied_ && svc) {
- Status<void> ret;
-
- if (handle)
- ret = svc->endpoint()->MessageReply(this, handle.Get());
- else
- ret = svc->endpoint()->MessageReply(this, handle.Get());
-
+ Status<void> ret = svc->endpoint()->MessageReply(this, handle.Get());
replied_ = ret.ok();
return ret;
} else {
diff --git a/libs/vr/libpdx_uds/channel_parcelable.cpp b/libs/vr/libpdx_uds/channel_parcelable.cpp
index e7bce27..5156846 100644
--- a/libs/vr/libpdx_uds/channel_parcelable.cpp
+++ b/libs/vr/libpdx_uds/channel_parcelable.cpp
@@ -36,7 +36,7 @@
}
status_t ChannelParcelable::writeToParcel(Parcel* parcel) const {
- status_t res = NO_ERROR;
+ status_t res = OK;
if (!IsValid()) {
ALOGE("ChannelParcelable::writeToParcel: Invalid channel parcel.");
@@ -44,20 +44,20 @@
}
res = parcel->writeUint32(kUdsMagicParcelHeader);
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("ChannelParcelable::writeToParcel: Cannot write magic: res=%d.", res);
return res;
}
res = parcel->writeFileDescriptor(data_fd_.Get());
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("ChannelParcelable::writeToParcel: Cannot write data fd: res=%d.",
res);
return res;
}
res = parcel->writeFileDescriptor(pollin_event_fd_.Get());
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE(
"ChannelParcelable::writeToParcel: Cannot write pollin event fd: "
"res=%d.",
@@ -66,7 +66,7 @@
}
res = parcel->writeFileDescriptor(pollhup_event_fd_.Get());
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE(
"ChannelParcelable::writeToParcel: Cannot write pollhup event fd: "
"res=%d.",
@@ -79,7 +79,7 @@
status_t ChannelParcelable::readFromParcel(const Parcel* parcel) {
uint32_t magic = 0;
- status_t res = NO_ERROR;
+ status_t res = OK;
if (IsValid()) {
ALOGE(
@@ -89,7 +89,7 @@
}
res = parcel->readUint32(&magic);
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("ChannelParcelable::readFromParcel: Failed to read magic: res=%d.",
res);
return res;
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
index 32d40e8..ecbfdba 100644
--- a/libs/vr/libpdx_uds/service_endpoint.cpp
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -521,6 +521,9 @@
info.flags = 0;
info.service = service_;
info.channel = GetChannelState(channel_id);
+ if (info.channel != nullptr) {
+ info.channel->SetActiveProcessId(request.cred.pid);
+ }
info.send_len = request.send_len;
info.recv_len = request.max_recv_len;
info.fd_count = request.file_descriptors.size();
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 26e8201..2829353 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -20,7 +20,6 @@
"display_surface.cpp",
"hardware_composer.cpp",
"vr_flinger.cpp",
- "vsync_service.cpp",
]
includeFiles = [ "include" ]
@@ -40,6 +39,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.3",
"libbinder",
"libbase",
"libbufferhubqueue",
@@ -65,6 +65,7 @@
headerLibraries = [
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
+ "android.hardware.graphics.composer@2.3-command-buffer",
"libdvr_headers",
"libsurfaceflinger_headers",
]
@@ -90,3 +91,7 @@
header_libs: headerLibraries,
name: "libvrflinger",
}
+
+subdirs = [
+ "tests",
+]
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
index 5d873d1..c360dee 100644
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -8,11 +8,11 @@
namespace android {
namespace dvr {
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
LocalHandle acquire_fence, std::size_t slot)
: buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
int* error) {
LocalHandle fence;
const int ret = buffer->Acquire(&fence);
@@ -75,7 +75,7 @@
return std::move(acquire_fence_);
}
-std::shared_ptr<BufferConsumer> AcquiredBuffer::ClaimBuffer() {
+std::shared_ptr<ConsumerBuffer> AcquiredBuffer::ClaimBuffer() {
return std::move(buffer_);
}
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
index 1a200aa..7643e75 100644
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -2,49 +2,49 @@
#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/consumer_buffer.h>
#include <memory>
namespace android {
namespace dvr {
-// Manages the ACQUIRE/RELEASE ownership cycle of a BufferConsumer.
+// Manages the ACQUIRE/RELEASE ownership cycle of a ConsumerBuffer.
class AcquiredBuffer {
public:
static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
- // Constructs an AcquiredBuffer from a BufferConsumer pointer and an acquire
- // fence. The BufferConsumer MUST be in the ACQUIRED state prior to calling
+ // Constructs an AcquiredBuffer from a ConsumerBuffer pointer and an acquire
+ // fence. The ConsumerBuffer MUST be in the ACQUIRED state prior to calling
// this constructor; the constructor does not attempt to ACQUIRE the buffer
// itself.
- AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+ AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
pdx::LocalHandle acquire_fence, std::size_t slot = 0);
- // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
+ // Constructs an AcquiredBuffer from a ConsumerBuffer. The ConsumerBuffer MUST
// be in the POSTED state prior to calling this constructor, as this
// constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
// this instance is left in the empty state. An optional error code is
// returned in |error|, which may be nullptr if not needed.
- AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error);
+ AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, int* error);
// Move constructor. Behaves similarly to the move assignment operator below.
AcquiredBuffer(AcquiredBuffer&& other) noexcept;
~AcquiredBuffer();
- // Move assignment operator. Moves the BufferConsumer and acquire fence from
- // |other| into this instance after RELEASING the current BufferConsumer and
+ // Move assignment operator. Moves the ConsumerBuffer and acquire fence from
+ // |other| into this instance after RELEASING the current ConsumerBuffer and
// closing the acquire fence. After the move |other| is left in the empty
// state.
AcquiredBuffer& operator=(AcquiredBuffer&& other) noexcept;
- // Accessors for the underlying BufferConsumer, the acquire fence, and the
+ // Accessors for the underlying ConsumerBuffer, the acquire fence, and the
// use-case specific sequence value from the acquisition (see
- // private/dvr/buffer_hub_client.h).
- std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+ // private/dvr/consumer_buffer.h).
+ std::shared_ptr<ConsumerBuffer> buffer() const { return buffer_; }
int acquire_fence() const { return acquire_fence_.Get(); }
// When non-empty, returns true if the acquired fence was signaled (or if the
@@ -58,11 +58,11 @@
// Returns the buffer, passing ownership to the caller. Caller is responsible
// for calling Release on the returned buffer.
- std::shared_ptr<BufferConsumer> ClaimBuffer();
+ std::shared_ptr<ConsumerBuffer> ClaimBuffer();
- // Releases the BufferConsumer, passing the release fence in |release_fence|
- // to the producer. On success, the BufferConsumer and acquire fence are set
- // to empty state; if release fails, the BufferConsumer and acquire fence are
+ // Releases the ConsumerBuffer, passing the release fence in |release_fence|
+ // to the producer. On success, the ConsumerBuffer and acquire fence are set
+ // to empty state; if release fails, the ConsumerBuffer and acquire fence are
// left in place and a negative error code is returned.
int Release(pdx::LocalHandle release_fence = {});
@@ -71,7 +71,7 @@
std::size_t slot() const { return slot_; }
private:
- std::shared_ptr<BufferConsumer> buffer_;
+ std::shared_ptr<ConsumerBuffer> buffer_;
// Mutable so that the fence can be closed when it is determined to be
// signaled during IsAvailable().
mutable pdx::LocalHandle acquire_fence_;
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 3090bd1..e0f2edd 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -4,7 +4,6 @@
#include <dvr/dvr_api.h>
#include <pdx/service.h>
#include <pdx/status.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/bufferhub_rpc.h>
#include <private/dvr/display_protocol.h>
@@ -60,11 +59,6 @@
void SetDisplayConfigurationUpdateNotifier(
DisplayConfigurationUpdateNotifier notifier);
- using VSyncCallback = HardwareComposer::VSyncCallback;
- void SetVSyncCallback(VSyncCallback callback) {
- hardware_composer_.SetVSyncCallback(callback);
- }
-
void GrantDisplayOwnership() { hardware_composer_.Enable(); }
void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
void OnBootFinished() { hardware_composer_.OnBootFinished(); }
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 64079e1..e1240d6 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1,5 +1,6 @@
#include "hardware_composer.h"
+#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <fcntl.h>
@@ -52,6 +53,10 @@
const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display";
+// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
+// events. Name ours similarly.
+const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
+
// How long to wait after boot finishes before we turn the display off.
constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
@@ -131,6 +136,7 @@
UpdatePostThreadState(PostThreadState::Quit, true);
if (post_thread_.joinable())
post_thread_.join();
+ composer_callback_->SetVsyncService(nullptr);
}
bool HardwareComposer::Initialize(
@@ -147,6 +153,13 @@
primary_display_ = GetDisplayParams(composer, primary_display_id, true);
+ vsync_service_ = new VsyncService;
+ sp<IServiceManager> sm(defaultServiceManager());
+ auto result = sm->addService(String16(VsyncService::GetServiceName()),
+ vsync_service_, false);
+ LOG_ALWAYS_FATAL_IF(result != android::OK,
+ "addService(%s) failed", VsyncService::GetServiceName());
+
post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
LOG_ALWAYS_FATAL_IF(
!post_thread_event_fd_,
@@ -223,6 +236,7 @@
LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
"Registered composer callback but didn't get hotplug for primary"
" display");
+ composer_callback_->SetVsyncService(vsync_service_);
}
void HardwareComposer::OnPostThreadResumed() {
@@ -242,7 +256,10 @@
// Standalones only create the composer client once and then use SetPowerMode
// to control the screen on pause/resume.
if (!is_standalone_device_) {
- composer_callback_ = nullptr;
+ if (composer_callback_ != nullptr) {
+ composer_callback_->SetVsyncService(nullptr);
+ composer_callback_ = nullptr;
+ }
composer_.reset(nullptr);
} else {
EnableDisplay(*target_display_, false);
@@ -336,7 +353,6 @@
// According to the documentation, this fence is signaled at the time of
// vsync/DMA for physical displays.
if (error == HWC::Error::None) {
- ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
retire_fence_fds_.emplace_back(present_fence);
} else {
ATRACE_INT("HardwareComposer: PresentResult", error);
@@ -775,6 +791,11 @@
std::unique_lock<std::mutex> lock(post_thread_mutex_);
ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
+ if (was_running) {
+ vsync_trace_parity_ = false;
+ ATRACE_INT(kVsyncTraceEventName, 0);
+ }
+
// Tear down resources.
OnPostThreadPaused();
was_running = false;
@@ -848,6 +869,9 @@
vsync_timestamp = status.get();
}
+ vsync_trace_parity_ = !vsync_trace_parity_;
+ ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0);
+
// Advance the vsync counter only if the system is keeping up with hardware
// vsync to give clients an indication of the delays.
if (vsync_prediction_interval_ == 1)
@@ -867,11 +891,6 @@
vsync_ring_->Publish(vsync);
}
- // Signal all of the vsync clients. Because absolute time is used for the
- // wakeup time below, this can take a little time if necessary.
- if (vsync_callback_)
- vsync_callback_(vsync_timestamp, /*frame_time_estimate*/ 0, vsync_count_);
-
{
// Sleep until shortly before vsync.
ATRACE_NAME("sleep");
@@ -1063,8 +1082,45 @@
layers_.size());
}
-void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
- vsync_callback_ = callback;
+std::vector<sp<IVsyncCallback>>::const_iterator
+HardwareComposer::VsyncService::FindCallback(
+ const sp<IVsyncCallback>& callback) const {
+ sp<IBinder> binder = IInterface::asBinder(callback);
+ return std::find_if(callbacks_.cbegin(), callbacks_.cend(),
+ [&](const sp<IVsyncCallback>& callback) {
+ return IInterface::asBinder(callback) == binder;
+ });
+}
+
+status_t HardwareComposer::VsyncService::registerCallback(
+ const sp<IVsyncCallback> callback) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ if (FindCallback(callback) == callbacks_.cend()) {
+ callbacks_.push_back(callback);
+ }
+ return OK;
+}
+
+status_t HardwareComposer::VsyncService::unregisterCallback(
+ const sp<IVsyncCallback> callback) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ auto iter = FindCallback(callback);
+ if (iter != callbacks_.cend()) {
+ callbacks_.erase(iter);
+ }
+ return OK;
+}
+
+void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
+ ATRACE_NAME("VsyncService::OnVsync");
+ std::lock_guard<std::mutex> autolock(mutex_);
+ for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
+ if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) {
+ iter = callbacks_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
}
Return<void> HardwareComposer::ComposerCallback::onHotplug(
@@ -1123,16 +1179,26 @@
Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
int64_t timestamp) {
+ TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
+ display, timestamp);
+ std::lock_guard<std::mutex> lock(mutex_);
DisplayInfo* display_info = GetDisplayInfo(display);
if (display_info) {
- TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
- display, timestamp);
display_info->callback_vsync_timestamp = timestamp;
}
+ if (primary_display_.id == display && vsync_service_ != nullptr) {
+ vsync_service_->OnVsync(timestamp);
+ }
return Void();
}
+void HardwareComposer::ComposerCallback::SetVsyncService(
+ const sp<VsyncService>& vsync_service) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ vsync_service_ = vsync_service;
+}
+
HardwareComposer::ComposerCallback::Displays
HardwareComposer::ComposerCallback::GetDisplays() {
std::lock_guard<std::mutex> lock(mutex_);
@@ -1149,6 +1215,7 @@
Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
hwc2_display_t display) {
+ std::lock_guard<std::mutex> autolock(mutex_);
DisplayInfo* display_info = GetDisplayInfo(display);
if (!display_info) {
ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
@@ -1160,7 +1227,6 @@
if (!event_fd) {
// Fall back to returning the last timestamp returned by the vsync
// callback.
- std::lock_guard<std::mutex> autolock(mutex_);
return display_info->callback_vsync_timestamp;
}
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index f1a755b..db0d6a7 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -22,8 +22,8 @@
#include <dvr/dvr_vsync.h>
#include <pdx/file_handle.h>
#include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/shared_buffer_helpers.h>
+#include <private/dvr/vsync_service.h>
#include "acquired_buffer.h"
#include "display_surface.h"
@@ -300,8 +300,6 @@
// will access the state and whether it needs to be synchronized.
class HardwareComposer {
public:
- // Type for vsync callback.
- using VSyncCallback = std::function<void(int64_t, int64_t, uint32_t)>;
using RequestDisplayCallback = std::function<void(bool)>;
HardwareComposer();
@@ -325,8 +323,6 @@
std::string Dump();
- void SetVSyncCallback(VSyncCallback callback);
-
const DisplayParams& GetPrimaryDisplayParams() const {
return primary_display_;
}
@@ -350,6 +346,18 @@
// on/off. Returns true on success, false on failure.
bool EnableDisplay(const DisplayParams& display, bool enabled);
+ class VsyncService : public BnVsyncService {
+ public:
+ status_t registerCallback(const sp<IVsyncCallback> callback) override;
+ status_t unregisterCallback(const sp<IVsyncCallback> callback) override;
+ void OnVsync(int64_t vsync_timestamp);
+ private:
+ std::vector<sp<IVsyncCallback>>::const_iterator FindCallback(
+ const sp<IVsyncCallback>& callback) const;
+ std::mutex mutex_;
+ std::vector<sp<IVsyncCallback>> callbacks_;
+ };
+
class ComposerCallback : public Hwc2::IComposerCallback {
public:
ComposerCallback() = default;
@@ -360,6 +368,7 @@
int64_t timestamp) override;
bool GotFirstHotplug() { return got_first_hotplug_; }
+ void SetVsyncService(const sp<VsyncService>& vsync_service);
struct Displays {
hwc2_display_t primary_display = 0;
@@ -385,6 +394,7 @@
DisplayInfo primary_display_;
std::optional<DisplayInfo> external_display_;
bool external_display_was_hotplugged_ = false;
+ sp<VsyncService> vsync_service_;
};
HWC::Error Validate(hwc2_display_t display);
@@ -484,9 +494,6 @@
// vector must be sorted by surface_id in ascending order.
std::vector<Layer> layers_;
- // Handler to hook vsync events outside of this class.
- VSyncCallback vsync_callback_;
-
// The layer posting thread. This thread wakes up a short time before vsync to
// hand buffers to hardware composer.
std::thread post_thread_;
@@ -534,6 +541,9 @@
DvrConfig post_thread_config_;
std::mutex shared_config_mutex_;
+ bool vsync_trace_parity_ = false;
+ sp<VsyncService> vsync_service_;
+
static constexpr int kPostThreadInterrupted = 1;
HardwareComposer(const HardwareComposer&) = delete;
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
new file mode 100644
index 0000000..c884cb3
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -0,0 +1,39 @@
+shared_libs = [
+ "android.hardware.configstore-utils",
+ "android.hardware.configstore@1.0",
+ "libbinder",
+ "libbufferhubqueue",
+ "libcutils",
+ "libgui",
+ "libhidlbase",
+ "liblog",
+ "libui",
+ "libutils",
+ "libnativewindow",
+ "libpdx_default_transport",
+]
+
+static_libs = [
+ "libdisplay",
+]
+
+cc_test {
+ srcs: ["vrflinger_test.cpp"],
+ // See go/apct-presubmit for documentation on how this .filter file is used
+ // by Android's automated testing infrastructure for test filtering.
+ data: ["vrflinger_test.filter"],
+ static_libs: static_libs,
+ shared_libs: shared_libs,
+ cflags: [
+ "-DLOG_TAG=\"VrFlingerTest\"",
+ "-DTRACE=0",
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ name: "vrflinger_test",
+
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
+}
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
new file mode 100644
index 0000000..7075e88
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -0,0 +1,261 @@
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware_buffer.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+
+#include <private/dvr/display_client.h>
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using android::dvr::display::DisplayClient;
+using android::dvr::display::Surface;
+using android::dvr::display::SurfaceAttribute;
+using android::dvr::display::SurfaceAttributeValue;
+
+namespace android {
+namespace dvr {
+
+// The transaction code for asking surface flinger if vr flinger is active. This
+// is done as a hidden api since it's only used for tests. See the "case 1028"
+// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp.
+constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028;
+
+// The maximum amount of time to give vr flinger to activate/deactivate. If the
+// switch hasn't completed in this amount of time, the test will fail.
+constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1);
+
+// How long to wait between each check to see if the vr flinger switch
+// completed.
+constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
+
+// How long to wait for a device that boots to VR to have vr flinger ready.
+constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30);
+
+// A Binder connection to surface flinger.
+class SurfaceFlingerConnection {
+ public:
+ static std::unique_ptr<SurfaceFlingerConnection> Create() {
+ sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>(
+ defaultServiceManager()->getService(String16("SurfaceFlinger")));
+ if (surface_flinger == nullptr) {
+ return nullptr;
+ }
+
+ return std::unique_ptr<SurfaceFlingerConnection>(
+ new SurfaceFlingerConnection(surface_flinger));
+ }
+
+ // Returns true if the surface flinger process is still running. We use this
+ // to detect if surface flinger has crashed.
+ bool IsAlive() {
+ IInterface::asBinder(surface_flinger_)->pingBinder();
+ return IInterface::asBinder(surface_flinger_)->isBinderAlive();
+ }
+
+ // Return true if vr flinger is currently active, false otherwise. If there's
+ // an error communicating with surface flinger, std::nullopt is returned.
+ std::optional<bool> IsVrFlingerActive() {
+ Parcel data, reply;
+ status_t result =
+ data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
+ if (result != OK) {
+ return std::nullopt;
+ }
+ result = IInterface::asBinder(surface_flinger_)
+ ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
+ if (result != OK) {
+ return std::nullopt;
+ }
+ bool vr_flinger_active;
+ result = reply.readBool(&vr_flinger_active);
+ if (result != OK) {
+ return std::nullopt;
+ }
+ return vr_flinger_active;
+ }
+
+ enum class VrFlingerSwitchResult : int8_t {
+ kSuccess,
+ kTimedOut,
+ kCommunicationError,
+ kSurfaceFlingerDied
+ };
+
+ // Wait for vr flinger to become active or inactive.
+ VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) {
+ return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval,
+ kVrFlingerSwitchMaxTime);
+ }
+
+ // Wait for vr flinger to become active or inactive, specifying custom timeouts.
+ VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active,
+ std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+ while (1) {
+ std::this_thread::sleep_for(pollInterval);
+ if (!IsAlive()) {
+ return VrFlingerSwitchResult::kSurfaceFlingerDied;
+ }
+ std::optional<bool> vr_flinger_active = IsVrFlingerActive();
+ if (!vr_flinger_active.has_value()) {
+ return VrFlingerSwitchResult::kCommunicationError;
+ }
+ if (vr_flinger_active.value() == wait_active) {
+ return VrFlingerSwitchResult::kSuccess;
+ } else if (std::chrono::steady_clock::now() - start_time > timeout) {
+ return VrFlingerSwitchResult::kTimedOut;
+ }
+ }
+ }
+
+ private:
+ SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger)
+ : surface_flinger_(surface_flinger) {}
+
+ sp<ISurfaceComposer> surface_flinger_ = nullptr;
+};
+
+// This test activates vr flinger by creating a vr flinger surface, then
+// deactivates vr flinger by destroying the surface. We verify that vr flinger
+// is activated and deactivated as expected, and that surface flinger doesn't
+// crash.
+//
+// If the device doesn't support vr flinger (as repoted by ConfigStore), the
+// test does nothing.
+//
+// If the device is a standalone vr device, the test also does nothing, since
+// this test verifies the behavior of display handoff from surface flinger to vr
+// flinger and back, and standalone devices never hand control of the display
+// back to surface flinger.
+TEST(VrFlingerTest, ActivateDeactivate) {
+ android::ProcessState::self()->startThreadPool();
+
+ // Exit immediately if the device doesn't support vr flinger. This ConfigStore
+ // check is the same mechanism used by surface flinger to decide if it should
+ // initialize vr flinger.
+ bool vr_flinger_enabled =
+ getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(
+ false);
+ if (!vr_flinger_enabled) {
+ return;
+ }
+
+ // This test doesn't apply to standalone vr devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ return;
+ }
+
+ auto surface_flinger_connection = SurfaceFlingerConnection::Create();
+ ASSERT_NE(surface_flinger_connection, nullptr);
+
+ // Verify we start off with vr flinger disabled.
+ ASSERT_TRUE(surface_flinger_connection->IsAlive());
+ auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
+ ASSERT_TRUE(vr_flinger_active.has_value());
+ ASSERT_FALSE(vr_flinger_active.value());
+
+ // Create a vr flinger surface, and verify vr flinger becomes active.
+ // Introduce a scope so that, at the end of the scope, the vr flinger surface
+ // is destroyed, and vr flinger deactivates.
+ {
+ auto display_client = DisplayClient::Create();
+ ASSERT_NE(display_client, nullptr);
+ auto metrics = display_client->GetDisplayMetrics();
+ ASSERT_TRUE(metrics.ok());
+
+ auto surface = Surface::CreateSurface({
+ {SurfaceAttribute::Direct, SurfaceAttributeValue(true)},
+ {SurfaceAttribute::Visible, SurfaceAttributeValue(true)},
+ });
+ ASSERT_TRUE(surface.ok());
+ ASSERT_TRUE(surface.get() != nullptr);
+
+ auto queue = surface.get()->CreateQueue(
+ metrics.get().display_width, metrics.get().display_height,
+ /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ /*capacity=*/1,
+ /*metadata_size=*/0);
+ ASSERT_TRUE(queue.ok());
+ ASSERT_TRUE(queue.get() != nullptr);
+
+ size_t slot;
+ pdx::LocalHandle release_fence;
+ auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence);
+ ASSERT_TRUE(buffer.ok());
+ ASSERT_TRUE(buffer.get() != nullptr);
+
+ ASSERT_EQ(buffer.get()->width(), metrics.get().display_width);
+ ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);
+
+ void* raw_buf = nullptr;
+ ASSERT_GE(buffer.get()->buffer()->Lock(
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, /*x=*/0, /*y=*/0,
+ buffer.get()->width(), buffer.get()->height(), &raw_buf),
+ 0);
+ ASSERT_NE(raw_buf, nullptr);
+ uint32_t* pixels = static_cast<uint32_t*>(raw_buf);
+
+ for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) {
+ pixels[i] = 0x0000ff00;
+ }
+
+ ASSERT_GE(buffer.get()->buffer()->Unlock(), 0);
+
+ ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0);
+
+ ASSERT_EQ(
+ surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true),
+ SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
+ }
+
+ // Now that the vr flinger surface is destroyed, vr flinger should deactivate.
+ ASSERT_EQ(
+ surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false),
+ SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
+}
+
+// This test runs only on devices that boot to vr. Such a device should boot to
+// a state where vr flinger is running, and the test verifies this after a
+// delay.
+TEST(BootVrFlingerTest, BootsToVrFlinger) {
+ // Exit if we are not running on a device that boots to vr.
+ if (!property_get_bool("ro.boot.vr", false)) {
+ return;
+ }
+
+ auto surface_flinger_connection = SurfaceFlingerConnection::Create();
+ ASSERT_NE(surface_flinger_connection, nullptr);
+
+ // Verify that vr flinger is enabled.
+ ASSERT_TRUE(surface_flinger_connection->IsAlive());
+ auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
+ ASSERT_TRUE(vr_flinger_active.has_value());
+
+ bool active_value = vr_flinger_active.value();
+ if (!active_value) {
+ // Try again, but delay up to 30 seconds.
+ ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true,
+ kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout),
+ SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.filter b/libs/vr/libvrflinger/tests/vrflinger_test.filter
new file mode 100644
index 0000000..030bb7b
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.filter
@@ -0,0 +1,5 @@
+{
+ "presubmit": {
+ "filter": "BootVrFlingerTest.*"
+ }
+}
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index a27d58d..a8a8476 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -23,7 +23,6 @@
#include "DisplayHardware/ComposerHal.h"
#include "display_manager_service.h"
#include "display_service.h"
-#include "vsync_service.h"
namespace android {
namespace dvr {
@@ -85,16 +84,6 @@
CHECK_ERROR(!service, error, "Failed to create display manager service.");
dispatcher_->AddService(service);
- service = android::dvr::VSyncService::Create();
- CHECK_ERROR(!service, error, "Failed to create vsync service.");
- dispatcher_->AddService(service);
-
- display_service_->SetVSyncCallback(
- std::bind(&android::dvr::VSyncService::VSyncEvent,
- std::static_pointer_cast<android::dvr::VSyncService>(service),
- std::placeholders::_1, std::placeholders::_2,
- std::placeholders::_3));
-
dispatcher_thread_ = std::thread([this]() {
prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
ALOGI("Entering message loop.");
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
deleted file mode 100644
index b8d8b08..0000000
--- a/libs/vr/libvrflinger/vsync_service.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-#include "vsync_service.h"
-
-#include <hardware/hwcomposer.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/prctl.h>
-#include <time.h>
-#include <utils/Trace.h>
-
-#include <dvr/dvr_display_types.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/display_protocol.h>
-
-using android::dvr::display::VSyncProtocol;
-using android::dvr::display::VSyncSchedInfo;
-using android::pdx::Channel;
-using android::pdx::Message;
-using android::pdx::MessageInfo;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace android {
-namespace dvr {
-
-VSyncService::VSyncService()
- : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)),
- last_vsync_(0),
- current_vsync_(0),
- compositor_time_ns_(0),
- current_vsync_count_(0) {}
-
-VSyncService::~VSyncService() {}
-
-void VSyncService::VSyncEvent(int64_t timestamp_ns,
- int64_t compositor_time_ns,
- uint32_t vsync_count) {
- ATRACE_NAME("VSyncService::VSyncEvent");
- std::lock_guard<std::mutex> autolock(mutex_);
-
- last_vsync_ = current_vsync_;
- current_vsync_ = timestamp_ns;
- compositor_time_ns_ = compositor_time_ns;
- current_vsync_count_ = vsync_count;
-
- NotifyWaiters();
- UpdateClients();
-}
-
-std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
- const MessageInfo& info = message.GetInfo();
-
- auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
- AddClient(client);
-
- return client;
-}
-
-void VSyncService::OnChannelClose(pdx::Message& /*message*/,
- const std::shared_ptr<Channel>& channel) {
- auto client = std::static_pointer_cast<VSyncChannel>(channel);
- if (!client) {
- ALOGW("WARNING: VSyncChannel was NULL!!!\n");
- return;
- }
-
- RemoveClient(client);
-}
-
-void VSyncService::AddWaiter(pdx::Message& message) {
- std::lock_guard<std::mutex> autolock(mutex_);
- std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
- waiters_.push_back(std::move(waiter));
-}
-
-void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
- std::lock_guard<std::mutex> autolock(mutex_);
- clients_.push_back(client);
-}
-
-void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
- std::lock_guard<std::mutex> autolock(mutex_);
- clients_.remove(client);
-}
-
-// Private. Assumes mutex is held.
-void VSyncService::NotifyWaiters() {
- ATRACE_NAME("VSyncService::NotifyWaiters");
- auto first = waiters_.begin();
- auto last = waiters_.end();
-
- while (first != last) {
- (*first)->Notify(current_vsync_);
- waiters_.erase(first++);
- }
-}
-
-// Private. Assumes mutex is held.
-void VSyncService::UpdateClients() {
- ATRACE_NAME("VSyncService::UpdateClients");
- auto first = clients_.begin();
- auto last = clients_.end();
-
- while (first != last) {
- (*first)->Signal();
- first++;
- }
-}
-
-pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
- ATRACE_NAME("VSyncService::HandleMessage");
- switch (message.GetOp()) {
- case VSyncProtocol::Wait::Opcode:
- AddWaiter(message);
- return {};
-
- case VSyncProtocol::GetLastTimestamp::Opcode:
- DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>(
- *this, &VSyncService::OnGetLastTimestamp, message);
- return {};
-
- case VSyncProtocol::GetSchedInfo::Opcode:
- DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>(
- *this, &VSyncService::OnGetSchedInfo, message);
- return {};
-
- case VSyncProtocol::Acknowledge::Opcode:
- DispatchRemoteMethod<VSyncProtocol::Acknowledge>(
- *this, &VSyncService::OnAcknowledge, message);
- return {};
-
- default:
- return Service::HandleMessage(message);
- }
-}
-
-pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) {
- auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
- std::lock_guard<std::mutex> autolock(mutex_);
-
- // Getting the timestamp has the side effect of ACKing.
- client->Ack();
- return {current_vsync_};
-}
-
-pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo(
- pdx::Message& message) {
- auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
- std::lock_guard<std::mutex> autolock(mutex_);
-
- // Getting the timestamp has the side effect of ACKing.
- client->Ack();
-
- uint32_t next_vsync_count = current_vsync_count_ + 1;
- int64_t current_time = GetSystemClockNs();
- int64_t vsync_period_ns = 0;
- int64_t next_warp;
- if (current_vsync_ == 0 || last_vsync_ == 0) {
- // Handle startup when current_vsync_ or last_vsync_ are 0.
- // Normally should not happen because vsync_service is running before
- // applications, but in case it does a sane time prevents applications
- // from malfunctioning.
- vsync_period_ns = 20000000;
- next_warp = current_time;
- } else {
- // TODO(jbates) When we have an accurate reading of the true vsync
- // period, use that instead of this estimated value.
- vsync_period_ns = current_vsync_ - last_vsync_;
- // Clamp the period, because when there are no surfaces the last_vsync_
- // value will get stale. Note this is temporary and goes away as soon
- // as we have an accurate vsync period reported by the system.
- vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
- next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
- // If the request missed the present window, move up to the next vsync.
- if (current_time > next_warp) {
- next_warp += vsync_period_ns;
- ++next_vsync_count;
- }
- }
-
- return {{vsync_period_ns, next_warp, next_vsync_count}};
-}
-
-pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) {
- auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
- std::lock_guard<std::mutex> autolock(mutex_);
- client->Ack();
- return {};
-}
-
-void VSyncWaiter::Notify(int64_t timestamp) {
- timestamp_ = timestamp;
- DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait,
- message_);
-}
-
-pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) {
- return {timestamp_};
-}
-
-void VSyncChannel::Ack() {
- ALOGD_IF(TRACE > 1, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
- service_.ModifyChannelEvents(cid_, POLLPRI, 0);
-}
-
-void VSyncChannel::Signal() {
- ALOGD_IF(TRACE > 1, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
- service_.ModifyChannelEvents(cid_, 0, POLLPRI);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h
deleted file mode 100644
index 822f02b..0000000
--- a/libs/vr/libvrflinger/vsync_service.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
-
-#include <pdx/service.h>
-
-#include <list>
-#include <memory>
-#include <mutex>
-#include <thread>
-
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-// VSyncWaiter encapsulates a client blocked waiting for the next vsync.
-// It is used to enqueue the Message to reply to when the next vsync event
-// occurs.
-class VSyncWaiter {
- public:
- explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {}
-
- void Notify(int64_t timestamp);
-
- private:
- pdx::Status<int64_t> OnWait(pdx::Message& message);
-
- pdx::Message message_;
- int64_t timestamp_ = 0;
-
- VSyncWaiter(const VSyncWaiter&) = delete;
- void operator=(const VSyncWaiter&) = delete;
-};
-
-// VSyncChannel manages the service-side per-client context for each client
-// using the service.
-class VSyncChannel : public pdx::Channel {
- public:
- VSyncChannel(pdx::Service& service, int pid, int cid)
- : service_(service), pid_(pid), cid_(cid) {}
-
- void Ack();
- void Signal();
-
- private:
- pdx::Service& service_;
- pid_t pid_;
- int cid_;
-
- VSyncChannel(const VSyncChannel&) = delete;
- void operator=(const VSyncChannel&) = delete;
-};
-
-// VSyncService implements the displayd vsync service over ServiceFS.
-class VSyncService : public pdx::ServiceBase<VSyncService> {
- public:
- ~VSyncService() override;
-
- pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
- std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
- void OnChannelClose(pdx::Message& message,
- const std::shared_ptr<pdx::Channel>& channel) override;
-
- // Called by the hardware composer HAL, or similar, whenever a vsync event
- // occurs on the primary display. |compositor_time_ns| is the number of ns
- // before the next vsync when the compositor will preempt the GPU to do EDS
- // and lens warp.
- void VSyncEvent(int64_t timestamp_ns, int64_t compositor_time_ns,
- uint32_t vsync_count);
-
- private:
- friend BASE;
-
- VSyncService();
-
- pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message);
- pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message);
- pdx::Status<void> OnAcknowledge(pdx::Message& message);
-
- void NotifierThreadFunction();
-
- void AddWaiter(pdx::Message& message);
- void NotifyWaiters();
- void UpdateClients();
-
- void AddClient(const std::shared_ptr<VSyncChannel>& client);
- void RemoveClient(const std::shared_ptr<VSyncChannel>& client);
-
- int64_t last_vsync_;
- int64_t current_vsync_;
- int64_t compositor_time_ns_;
- uint32_t current_vsync_count_;
-
- std::mutex mutex_;
-
- std::list<std::unique_ptr<VSyncWaiter>> waiters_;
- std::list<std::shared_ptr<VSyncChannel>> clients_;
-
- VSyncService(const VSyncService&) = delete;
- void operator=(VSyncService&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
index 4acc085..4ff6a09 100644
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -8,8 +8,8 @@
#include <pdx/client.h>
#include <pdx/default_transport/client_channel_factory.h>
#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_client.h>
#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/consumer_buffer.h>
#include <private/dvr/display_client.h>
#include <private/dvr/pose-ipc.h>
#include <private/dvr/shared_buffer_helpers.h>
@@ -221,14 +221,14 @@
return -status.error();
}
- auto buffer = BufferConsumer::Import(status.take());
+ auto buffer = ConsumerBuffer::Import(status.take());
if (!buffer) {
ALOGE("Pose failed to import ring buffer");
return -EIO;
}
constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync);
void* addr = nullptr;
- int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+ int ret = buffer->GetBlobReadWritePointer(size, &addr);
if (ret < 0 || !addr) {
ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
return -EIO;
@@ -290,7 +290,7 @@
const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr;
struct ControllerClientState {
- std::unique_ptr<BufferConsumer> pose_buffer;
+ std::unique_ptr<ConsumerBuffer> pose_buffer;
const DvrPoseAsync* mapped_pose_buffer = nullptr;
};
ControllerClientState controllers_[MAX_CONTROLLERS];
diff --git a/libs/vr/public.libraries-google.txt b/libs/vr/public.libraries-google.txt
new file mode 100644
index 0000000..8271b94
--- /dev/null
+++ b/libs/vr/public.libraries-google.txt
@@ -0,0 +1 @@
+libdvr.google.so
\ No newline at end of file
diff --git a/opengl/include/EGL/Platform.h b/opengl/include/EGL/Platform.h
new file mode 100644
index 0000000..f2b501c
--- /dev/null
+++ b/opengl/include/EGL/Platform.h
@@ -0,0 +1,338 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Platform.h: The public interface ANGLE exposes to the API layer, for
+// doing platform-specific tasks like gathering data, or for tracing.
+
+#ifndef ANGLE_PLATFORM_H
+#define ANGLE_PLATFORM_H
+
+#include <stdint.h>
+#include <array>
+
+#define EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX 0x3482
+
+#if defined(_WIN32)
+# if !defined(LIBANGLE_IMPLEMENTATION)
+# define ANGLE_PLATFORM_EXPORT __declspec(dllimport)
+# else
+# define ANGLE_PLATFORM_EXPORT __declspec(dllexport)
+# endif
+#elif defined(__GNUC__) || defined(__clang__)
+# define ANGLE_PLATFORM_EXPORT __attribute__((visibility ("default")))
+#endif
+#if !defined(ANGLE_PLATFORM_EXPORT)
+# define ANGLE_PLATFORM_EXPORT
+#endif
+
+#if defined(_WIN32)
+# define ANGLE_APIENTRY __stdcall
+#else
+# define ANGLE_APIENTRY
+#endif
+
+namespace angle
+{
+struct WorkaroundsD3D;
+struct FeaturesVk;
+using TraceEventHandle = uint64_t;
+using EGLDisplayType = void *;
+struct PlatformMethods;
+
+// Use a C-like API to not trigger undefined calling behaviour.
+// Avoid using decltype here to work around sanitizer limitations.
+// TODO(jmadill): Use decltype here if/when UBSAN is fixed.
+
+// System --------------------------------------------------------------
+
+// Wall clock time in seconds since the epoch.
+// TODO(jmadill): investigate using an ANGLE internal time library
+using CurrentTimeFunc = double (*)(PlatformMethods *platform);
+inline double DefaultCurrentTime(PlatformMethods *platform)
+{
+ return 0.0;
+}
+
+// Monotonically increasing time in seconds from an arbitrary fixed point in the past.
+// This function is expected to return at least millisecond-precision values. For this reason,
+// it is recommended that the fixed point be no further in the past than the epoch.
+using MonotonicallyIncreasingTimeFunc = double (*)(PlatformMethods *platform);
+inline double DefaultMonotonicallyIncreasingTime(PlatformMethods *platform)
+{
+ return 0.0;
+}
+
+// Logging ------------------------------------------------------------
+
+// Log an error message within the platform implementation.
+using LogErrorFunc = void (*)(PlatformMethods *platform, const char *errorMessage);
+inline void DefaultLogError(PlatformMethods *platform, const char *errorMessage)
+{
+}
+
+// Log a warning message within the platform implementation.
+using LogWarningFunc = void (*)(PlatformMethods *platform, const char *warningMessage);
+inline void DefaultLogWarning(PlatformMethods *platform, const char *warningMessage)
+{
+}
+
+// Log an info message within the platform implementation.
+using LogInfoFunc = void (*)(PlatformMethods *platform, const char *infoMessage);
+inline void DefaultLogInfo(PlatformMethods *platform, const char *infoMessage)
+{
+}
+
+// Tracing --------
+
+// Get a pointer to the enabled state of the given trace category. The
+// embedder can dynamically change the enabled state as trace event
+// recording is started and stopped by the application. Only long-lived
+// literal strings should be given as the category name. The implementation
+// expects the returned pointer to be held permanently in a local static. If
+// the unsigned char is non-zero, tracing is enabled. If tracing is enabled,
+// addTraceEvent is expected to be called by the trace event macros.
+using GetTraceCategoryEnabledFlagFunc = const unsigned char *(*)(PlatformMethods *platform,
+ const char *categoryName);
+inline const unsigned char *DefaultGetTraceCategoryEnabledFlag(PlatformMethods *platform,
+ const char *categoryName)
+{
+ return nullptr;
+}
+
+//
+// Add a trace event to the platform tracing system. Depending on the actual
+// enabled state, this event may be recorded or dropped.
+// - phase specifies the type of event:
+// - BEGIN ('B'): Marks the beginning of a scoped event.
+// - END ('E'): Marks the end of a scoped event.
+// - COMPLETE ('X'): Marks the beginning of a scoped event, but doesn't
+// need a matching END event. Instead, at the end of the scope,
+// updateTraceEventDuration() must be called with the TraceEventHandle
+// returned from addTraceEvent().
+// - INSTANT ('I'): Standalone, instantaneous event.
+// - START ('S'): Marks the beginning of an asynchronous event (the end
+// event can occur in a different scope or thread). The id parameter is
+// used to match START/FINISH pairs.
+// - FINISH ('F'): Marks the end of an asynchronous event.
+// - COUNTER ('C'): Used to trace integer quantities that change over
+// time. The argument values are expected to be of type int.
+// - METADATA ('M'): Reserved for internal use.
+// - categoryEnabled is the pointer returned by getTraceCategoryEnabledFlag.
+// - name is the name of the event. Also used to match BEGIN/END and
+// START/FINISH pairs.
+// - id optionally allows events of the same name to be distinguished from
+// each other. For example, to trace the construction and destruction of
+// objects, specify the pointer as the id parameter.
+// - timestamp should be a time value returned from monotonicallyIncreasingTime.
+// - numArgs specifies the number of elements in argNames, argTypes, and
+// argValues.
+// - argNames is the array of argument names. Use long-lived literal strings
+// or specify the COPY flag.
+// - argTypes is the array of argument types:
+// - BOOL (1): bool
+// - UINT (2): unsigned long long
+// - INT (3): long long
+// - DOUBLE (4): double
+// - POINTER (5): void*
+// - STRING (6): char* (long-lived null-terminated char* string)
+// - COPY_STRING (7): char* (temporary null-terminated char* string)
+// - CONVERTABLE (8): WebConvertableToTraceFormat
+// - argValues is the array of argument values. Each value is the unsigned
+// long long member of a union of all supported types.
+// - flags can be 0 or one or more of the following, ORed together:
+// - COPY (0x1): treat all strings (name, argNames and argValues of type
+// string) as temporary so that they will be copied by addTraceEvent.
+// - HAS_ID (0x2): use the id argument to uniquely identify the event for
+// matching with other events of the same name.
+// - MANGLE_ID (0x4): specify this flag if the id parameter is the value
+// of a pointer.
+using AddTraceEventFunc = angle::TraceEventHandle (*)(PlatformMethods *platform,
+ char phase,
+ const unsigned char *categoryEnabledFlag,
+ const char *name,
+ unsigned long long id,
+ double timestamp,
+ int numArgs,
+ const char **argNames,
+ const unsigned char *argTypes,
+ const unsigned long long *argValues,
+ unsigned char flags);
+inline angle::TraceEventHandle DefaultAddTraceEvent(PlatformMethods *platform,
+ char phase,
+ const unsigned char *categoryEnabledFlag,
+ const char *name,
+ unsigned long long id,
+ double timestamp,
+ int numArgs,
+ const char **argNames,
+ const unsigned char *argTypes,
+ const unsigned long long *argValues,
+ unsigned char flags)
+{
+ return 0;
+}
+
+// Set the duration field of a COMPLETE trace event.
+using UpdateTraceEventDurationFunc = void (*)(PlatformMethods *platform,
+ const unsigned char *categoryEnabledFlag,
+ const char *name,
+ angle::TraceEventHandle eventHandle);
+inline void DefaultUpdateTraceEventDuration(PlatformMethods *platform,
+ const unsigned char *categoryEnabledFlag,
+ const char *name,
+ angle::TraceEventHandle eventHandle)
+{
+}
+
+// Callbacks for reporting histogram data.
+// CustomCounts histogram has exponential bucket sizes, so that min=1, max=1000000, bucketCount=50
+// would do.
+using HistogramCustomCountsFunc = void (*)(PlatformMethods *platform,
+ const char *name,
+ int sample,
+ int min,
+ int max,
+ int bucketCount);
+inline void DefaultHistogramCustomCounts(PlatformMethods *platform,
+ const char *name,
+ int sample,
+ int min,
+ int max,
+ int bucketCount)
+{
+}
+// Enumeration histogram buckets are linear, boundaryValue should be larger than any possible sample
+// value.
+using HistogramEnumerationFunc = void (*)(PlatformMethods *platform,
+ const char *name,
+ int sample,
+ int boundaryValue);
+inline void DefaultHistogramEnumeration(PlatformMethods *platform,
+ const char *name,
+ int sample,
+ int boundaryValue)
+{
+}
+// Unlike enumeration histograms, sparse histograms only allocate memory for non-empty buckets.
+using HistogramSparseFunc = void (*)(PlatformMethods *platform, const char *name, int sample);
+inline void DefaultHistogramSparse(PlatformMethods *platform, const char *name, int sample)
+{
+}
+// Boolean histograms track two-state variables.
+using HistogramBooleanFunc = void (*)(PlatformMethods *platform, const char *name, bool sample);
+inline void DefaultHistogramBoolean(PlatformMethods *platform, const char *name, bool sample)
+{
+}
+
+// Allows us to programatically override ANGLE's default workarounds for testing purposes.
+using OverrideWorkaroundsD3DFunc = void (*)(PlatformMethods *platform,
+ angle::WorkaroundsD3D *workaroundsD3D);
+inline void DefaultOverrideWorkaroundsD3D(PlatformMethods *platform,
+ angle::WorkaroundsD3D *workaroundsD3D)
+{
+}
+
+using OverrideFeaturesVkFunc = void (*)(PlatformMethods *platform,
+ angle::FeaturesVk *workaroundsVulkan);
+inline void DefaultOverrideFeaturesVk(PlatformMethods *platform,
+ angle::FeaturesVk *workaroundsVulkan)
+{
+}
+
+// Callback on a successful program link with the program binary. Can be used to store
+// shaders to disk. Keys are a 160-bit SHA-1 hash.
+using ProgramKeyType = std::array<uint8_t, 20>;
+using CacheProgramFunc = void (*)(PlatformMethods *platform,
+ const ProgramKeyType &key,
+ size_t programSize,
+ const uint8_t *programBytes);
+inline void DefaultCacheProgram(PlatformMethods *platform,
+ const ProgramKeyType &key,
+ size_t programSize,
+ const uint8_t *programBytes)
+{
+}
+
+// Platform methods are enumerated here once.
+#define ANGLE_PLATFORM_OP(OP) \
+ OP(currentTime, CurrentTime) \
+ OP(monotonicallyIncreasingTime, MonotonicallyIncreasingTime) \
+ OP(logError, LogError) \
+ OP(logWarning, LogWarning) \
+ OP(logInfo, LogInfo) \
+ OP(getTraceCategoryEnabledFlag, GetTraceCategoryEnabledFlag) \
+ OP(addTraceEvent, AddTraceEvent) \
+ OP(updateTraceEventDuration, UpdateTraceEventDuration) \
+ OP(histogramCustomCounts, HistogramCustomCounts) \
+ OP(histogramEnumeration, HistogramEnumeration) \
+ OP(histogramSparse, HistogramSparse) \
+ OP(histogramBoolean, HistogramBoolean) \
+ OP(overrideWorkaroundsD3D, OverrideWorkaroundsD3D) \
+ OP(overrideFeaturesVk, OverrideFeaturesVk) \
+ OP(cacheProgram, CacheProgram)
+
+#define ANGLE_PLATFORM_METHOD_DEF(Name, CapsName) CapsName##Func Name = Default##CapsName;
+
+struct ANGLE_PLATFORM_EXPORT PlatformMethods
+{
+ PlatformMethods() {}
+
+ // User data pointer for any implementation specific members. Put it at the start of the
+ // platform structure so it doesn't become overwritten if one version of the platform
+ // adds or removes new members.
+ void *context = 0;
+
+ ANGLE_PLATFORM_OP(ANGLE_PLATFORM_METHOD_DEF);
+};
+
+#undef ANGLE_PLATFORM_METHOD_DEF
+
+// Subtract one to account for the context pointer.
+constexpr unsigned int g_NumPlatformMethods = (sizeof(PlatformMethods) / sizeof(uintptr_t)) - 1;
+
+#define ANGLE_PLATFORM_METHOD_STRING(Name) #Name
+#define ANGLE_PLATFORM_METHOD_STRING2(Name, CapsName) ANGLE_PLATFORM_METHOD_STRING(Name),
+
+constexpr const char *const g_PlatformMethodNames[g_NumPlatformMethods] = {
+ ANGLE_PLATFORM_OP(ANGLE_PLATFORM_METHOD_STRING2)};
+
+#undef ANGLE_PLATFORM_METHOD_STRING2
+#undef ANGLE_PLATFORM_METHOD_STRING
+
+} // namespace angle
+
+extern "C" {
+
+// Gets the platform methods on the passed-in EGL display. If the method name signature does not
+// match the compiled signature for this ANGLE, false is returned. On success true is returned.
+// The application should set any platform methods it cares about on the returned pointer.
+// If display is not valid, behaviour is undefined.
+
+ANGLE_PLATFORM_EXPORT bool ANGLE_APIENTRY ANGLEGetDisplayPlatform(angle::EGLDisplayType display,
+ const char *const methodNames[],
+ unsigned int methodNameCount,
+ void *context,
+ void *platformMethodsOut);
+
+// Sets the platform methods back to their defaults.
+// If display is not valid, behaviour is undefined.
+ANGLE_PLATFORM_EXPORT void ANGLE_APIENTRY ANGLEResetDisplayPlatform(angle::EGLDisplayType display);
+
+} // extern "C"
+
+namespace angle
+{
+typedef bool(ANGLE_APIENTRY *GetDisplayPlatformFunc)(angle::EGLDisplayType,
+ const char *const *,
+ unsigned int,
+ void *,
+ void *);
+typedef void(ANGLE_APIENTRY *ResetDisplayPlatformFunc)(angle::EGLDisplayType);
+} // namespace angle
+
+// This function is not exported
+angle::PlatformMethods *ANGLEPlatformCurrent();
+
+#endif // ANGLE_PLATFORM_H
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index 93a3965..c9e8b7c 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -33,12 +33,12 @@
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: a732b061e7 $ on $Git commit date: 2017-06-17 23:27:53 +0100 $
+** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
*/
#include <EGL/eglplatform.h>
-/* Generated on date 20170627 */
+/* Generated on date 20180517 */
/* Generated C header for:
* API: egl
@@ -235,10 +235,66 @@
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
#endif /* EGL_VERSION_1_4 */
-/* This version of Android does not yet support EGL 1.5, but the following
- * portion of EGL 1.5 is included in order to support portions of "eglext.h".
- */
+#ifndef EGL_VERSION_1_5
+#define EGL_VERSION_1_5 1
+typedef void *EGLSync;
typedef intptr_t EGLAttrib;
+typedef khronos_utime_nanoseconds_t EGLTime;
+typedef void *EGLImage;
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
+#define EGL_NO_RESET_NOTIFICATION 0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET 0x31BF
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2
+#define EGL_OPENGL_ES3_BIT 0x00000040
+#define EGL_CL_EVENT_HANDLE 0x309C
+#define EGL_SYNC_CL_EVENT 0x30FE
+#define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0
+#define EGL_SYNC_TYPE 0x30F7
+#define EGL_SYNC_STATUS 0x30F1
+#define EGL_SYNC_CONDITION 0x30F8
+#define EGL_SIGNALED 0x30F2
+#define EGL_UNSIGNALED 0x30F3
+#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001
+#define EGL_FOREVER 0xFFFFFFFFFFFFFFFFull
+#define EGL_TIMEOUT_EXPIRED 0x30F5
+#define EGL_CONDITION_SATISFIED 0x30F6
+#define EGL_NO_SYNC EGL_CAST(EGLSync,0)
+#define EGL_SYNC_FENCE 0x30F9
+#define EGL_GL_COLORSPACE 0x309D
+#define EGL_GL_COLORSPACE_SRGB 0x3089
+#define EGL_GL_COLORSPACE_LINEAR 0x308A
+#define EGL_GL_RENDERBUFFER 0x30B9
+#define EGL_GL_TEXTURE_2D 0x30B1
+#define EGL_GL_TEXTURE_LEVEL 0x30BC
+#define EGL_GL_TEXTURE_3D 0x30B2
+#define EGL_GL_TEXTURE_ZOFFSET 0x30BD
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
+#define EGL_IMAGE_PRESERVED 0x30D2
+#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)
+EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttrib (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+EGLAPI EGLImage EGLAPIENTRY eglCreateImage (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImage (EGLDisplay dpy, EGLImage image);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif /* EGL_VERSION_1_5 */
#ifdef __cplusplus
}
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 44f4dbc..501bf58 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -28,17 +28,17 @@
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/*
-** This header is generated from the Khronos OpenGL / OpenGL ES XML
-** API Registry. The current version of the Registry, generator scripts
+** This header is generated from the Khronos EGL XML API Registry.
+** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: feaaeb19e1 $ on $Git commit date: 2018-02-26 20:49:02 -0800 $
+** Khronos $Git commit SHA1: 726475c203 $ on $Git commit date: 2018-10-03 23:51:49 -0700 $
*/
#include <EGL/eglplatform.h>
-#define EGL_EGLEXT_VERSION 20180228
+#define EGL_EGLEXT_VERSION 20181204
/* Generated C header for:
* API: egl
@@ -618,6 +618,16 @@
#define EGL_EXT_client_extensions 1
#endif /* EGL_EXT_client_extensions */
+#ifndef EGL_EXT_client_sync
+#define EGL_EXT_client_sync 1
+#define EGL_SYNC_CLIENT_EXT 0x3364
+#define EGL_SYNC_CLIENT_SIGNAL_EXT 0x3365
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCLIENTSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglClientSignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_client_sync */
+
#ifndef EGL_EXT_compositor
#define EGL_EXT_compositor 1
#define EGL_PRIMARY_COMPOSITOR_CONTEXT_EXT 0x3460
@@ -671,6 +681,7 @@
#ifndef EGL_EXT_device_drm
#define EGL_EXT_device_drm 1
#define EGL_DRM_DEVICE_FILE_EXT 0x3233
+#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif /* EGL_EXT_device_drm */
#ifndef EGL_EXT_device_enumeration
@@ -706,6 +717,11 @@
#define EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT 0x3362
#endif /* EGL_EXT_gl_colorspace_display_p3_linear */
+#ifndef EGL_EXT_gl_colorspace_display_p3_passthrough
+#define EGL_EXT_gl_colorspace_display_p3_passthrough 1
+#define EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT 0x3490
+#endif /* EGL_EXT_gl_colorspace_display_p3_passthrough */
+
#ifndef EGL_EXT_gl_colorspace_scrgb
#define EGL_EXT_gl_colorspace_scrgb 1
#define EGL_GL_COLORSPACE_SCRGB_EXT 0x3351
@@ -903,6 +919,14 @@
#endif
#endif /* EGL_EXT_swap_buffers_with_damage */
+#ifndef EGL_EXT_sync_reuse
+#define EGL_EXT_sync_reuse 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglUnsignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_sync_reuse */
+
#ifndef EGL_EXT_yuv_surface
#define EGL_EXT_yuv_surface 1
#define EGL_YUV_ORDER_EXT 0x3301
@@ -1147,6 +1171,14 @@
#define EGL_STREAM_FIFO_SYNCHRONOUS_NV 0x3336
#endif /* EGL_NV_stream_fifo_synchronous */
+#ifndef EGL_NV_stream_flush
+#define EGL_NV_stream_flush 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMFLUSHNVPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamFlushNV (EGLDisplay dpy, EGLStreamKHR stream);
+#endif
+#endif /* EGL_NV_stream_flush */
+
#ifndef EGL_NV_stream_frame_limits
#define EGL_NV_stream_frame_limits 1
#define EGL_PRODUCER_MAX_FRAME_HINT_NV 0x3337
@@ -1285,17 +1317,6 @@
#define EGL_NATIVE_SURFACE_TIZEN 0x32A1
#endif /* EGL_TIZEN_image_native_surface */
-/* This is a private Android extension that does not exist in the EGL registry,
- * formerly used to work around a hardware issue on Nexus 4. It is deprecated
- * and unimplemented. It has been added to this header manually. */
-#ifndef EGL_ANDROID_image_crop
-#define EGL_ANDROID_image_crop 1
-#define EGL_IMAGE_CROP_LEFT_ANDROID 0x3148
-#define EGL_IMAGE_CROP_TOP_ANDROID 0x3149
-#define EGL_IMAGE_CROP_RIGHT_ANDROID 0x314A
-#define EGL_IMAGE_CROP_BOTTOM_ANDROID 0x314B
-#endif
-
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
new file mode 100644
index 0000000..0556ea1
--- /dev/null
+++ b/opengl/include/EGL/eglext_angle.h
@@ -0,0 +1,191 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// 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_
+
+// clang-format off
+
+#ifndef EGL_ANGLE_robust_resource_initialization
+#define EGL_ANGLE_robust_resource_initialization 1
+#define EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE 0x3453
+#endif /* EGL_ANGLE_robust_resource_initialization */
+
+#ifndef EGL_ANGLE_keyed_mutex
+#define EGL_ANGLE_keyed_mutex 1
+#define EGL_DXGI_KEYED_MUTEX_ANGLE 0x33A2
+#endif /* EGL_ANGLE_keyed_mutex */
+
+#ifndef EGL_ANGLE_d3d_texture_client_buffer
+#define EGL_ANGLE_d3d_texture_client_buffer 1
+#define EGL_D3D_TEXTURE_ANGLE 0x33A3
+#endif /* EGL_ANGLE_d3d_texture_client_buffer */
+
+#ifndef EGL_ANGLE_software_display
+#define EGL_ANGLE_software_display 1
+#define EGL_SOFTWARE_DISPLAY_ANGLE ((EGLNativeDisplayType)-1)
+#endif /* EGL_ANGLE_software_display */
+
+#ifndef EGL_ANGLE_direct3d_display
+#define EGL_ANGLE_direct3d_display 1
+#define EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
+#define EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)
+#endif /* EGL_ANGLE_direct3d_display */
+
+#ifndef EGL_ANGLE_direct_composition
+#define EGL_ANGLE_direct_composition 1
+#define EGL_DIRECT_COMPOSITION_ANGLE 0x33A5
+#endif /* EGL_ANGLE_direct_composition */
+
+#ifndef EGL_ANGLE_platform_angle
+#define EGL_ANGLE_platform_angle 1
+#define EGL_PLATFORM_ANGLE_ANGLE 0x3202
+#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
+#define EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204
+#define EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205
+#define EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE 0x3206
+#define EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE 0x3451
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x345E
+#endif /* EGL_ANGLE_platform_angle */
+
+#ifndef EGL_ANGLE_platform_angle_d3d
+#define EGL_ANGLE_platform_angle_d3d 1
+#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
+#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE 0x320B
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE 0x320C
+#define EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE 0x320F
+#endif /* EGL_ANGLE_platform_angle_d3d */
+
+#ifndef EGL_ANGLE_platform_angle_opengl
+#define EGL_ANGLE_platform_angle_opengl 1
+#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D
+#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320E
+#define EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE 0x3480
+#endif /* EGL_ANGLE_platform_angle_opengl */
+
+#ifndef EGL_ANGLE_platform_angle_null
+#define EGL_ANGLE_platform_angle_null 1
+#define EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE 0x33AE
+#endif /* EGL_ANGLE_platform_angle_null */
+
+#ifndef EGL_ANGLE_platform_angle_vulkan
+#define EGL_ANGLE_platform_angle_vulkan 1
+#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
+#endif /* EGL_ANGLE_platform_angle_vulkan */
+
+#ifndef EGL_ANGLE_platform_angle_context_virtualization
+#define EGL_ANGLE_platform_angle_context_virtualization 1
+#define EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE 0x3481
+#endif /* EGL_ANGLE_platform_angle_context_virtualization */
+
+#ifndef EGL_ANGLE_x11_visual
+#define EGL_ANGLE_x11_visual
+#define EGL_X11_VISUAL_ID_ANGLE 0x33A3
+#endif /* EGL_ANGLE_x11_visual */
+
+#ifndef EGL_ANGLE_flexible_surface_compatibility
+#define EGL_ANGLE_flexible_surface_compatibility 1
+#define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6
+#endif /* EGL_ANGLE_flexible_surface_compatibility */
+
+#ifndef EGL_ANGLE_surface_orientation
+#define EGL_ANGLE_surface_orientation
+#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
+#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
+#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
+#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
+#endif /* EGL_ANGLE_surface_orientation */
+
+#ifndef EGL_ANGLE_experimental_present_path
+#define EGL_ANGLE_experimental_present_path
+#define EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE 0x33A4
+#define EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE 0x33A9
+#define EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE 0x33AA
+#endif /* EGL_ANGLE_experimental_present_path */
+
+#ifndef EGL_ANGLE_stream_producer_d3d_texture
+#define EGL_ANGLE_stream_producer_d3d_texture
+#define EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE 0x33AB
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLCREATESTREAMPRODUCERD3DTEXTUREANGLEPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list);
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLSTREAMPOSTD3DTEXTUREANGLEPROC)(EGLDisplay dpy, EGLStreamKHR stream, void *texture, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglCreateStreamProducerD3DTextureANGLE(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamPostD3DTextureANGLE(EGLDisplay dpy, EGLStreamKHR stream, void *texture, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_ANGLE_stream_producer_d3d_texture */
+
+#ifndef EGL_ANGLE_create_context_webgl_compatibility
+#define EGL_ANGLE_create_context_webgl_compatibility 1
+#define EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE 0x33AC
+#endif /* EGL_ANGLE_create_context_webgl_compatibility */
+
+#ifndef EGL_ANGLE_display_texture_share_group
+#define EGL_ANGLE_display_texture_share_group 1
+#define EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE 0x33AF
+#endif /* EGL_ANGLE_display_texture_share_group */
+
+#ifndef EGL_CHROMIUM_create_context_bind_generates_resource
+#define EGL_CHROMIUM_create_context_bind_generates_resource 1
+#define EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM 0x33AD
+#endif /* EGL_CHROMIUM_create_context_bind_generates_resource */
+
+#ifndef EGL_ANGLE_create_context_client_arrays
+#define EGL_ANGLE_create_context_client_arrays 1
+#define EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE 0x3452
+#endif /* EGL_ANGLE_create_context_client_arrays */
+
+#ifndef EGL_ANGLE_device_creation
+#define EGL_ANGLE_device_creation 1
+typedef EGLDeviceEXT(EGLAPIENTRYP PFNEGLCREATEDEVICEANGLEPROC) (EGLint device_type, void *native_device, const EGLAttrib *attrib_list);
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLRELEASEDEVICEANGLEPROC) (EGLDeviceEXT device);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLDeviceEXT EGLAPIENTRY eglCreateDeviceANGLE(EGLint device_type, void *native_device, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglReleaseDeviceANGLE(EGLDeviceEXT device);
+#endif
+#endif /* EGL_ANGLE_device_creation */
+
+#ifndef EGL_ANGLE_program_cache_control
+#define EGL_ANGLE_program_cache_control 1
+#define EGL_PROGRAM_CACHE_SIZE_ANGLE 0x3455
+#define EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE 0x3456
+#define EGL_PROGRAM_CACHE_RESIZE_ANGLE 0x3457
+#define EGL_PROGRAM_CACHE_TRIM_ANGLE 0x3458
+#define EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE 0x3459
+typedef EGLint (EGLAPIENTRYP PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC) (EGLDisplay dpy, EGLenum attrib);
+typedef void (EGLAPIENTRYP PFNEGLPROGRAMCACHEQUERYANGLEPROC) (EGLDisplay dpy, EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize);
+typedef void (EGLAPIENTRYP PFNEGPROGRAMCACHELPOPULATEANGLEPROC) (EGLDisplay dpy, const void *key, EGLint keysize, const void *binary, EGLint binarysize);
+typedef EGLint (EGLAPIENTRYP PFNEGLPROGRAMCACHERESIZEANGLEPROC) (EGLDisplay dpy, EGLint limit, EGLenum mode);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLint EGLAPIENTRY eglProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib);
+EGLAPI void EGLAPIENTRY eglProgramCacheQueryANGLE(EGLDisplay dpy, EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize);
+EGLAPI void EGLAPIENTRY eglProgramCachePopulateANGLE(EGLDisplay dpy, const void *key, EGLint keysize, const void *binary, EGLint binarysize);
+EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limit, EGLenum mode);
+#endif
+#endif /* EGL_ANGLE_program_cache_control */
+
+#ifndef EGL_ANGLE_iosurface_client_buffer
+#define EGL_ANGLE_iosurface_client_buffer 1
+#define EGL_IOSURFACE_ANGLE 0x3454
+#define EGL_IOSURFACE_PLANE_ANGLE 0x345A
+#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B
+#define EGL_TEXTURE_TYPE_ANGLE 0x345C
+#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
+#endif /* EGL_ANGLE_iosurface_client_buffer */
+
+#ifndef EGL_ANGLE_create_context_extensions_enabled
+#define EGL_ANGLE_create_context_extensions_enabled 1
+#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
+#endif /* EGL_ANGLE_create_context_extensions_enabled */
+
+// clang-format on
+
+#endif // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index c77c333..0bc2cb9 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -77,12 +77,24 @@
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
-#elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+#elif defined(WL_EGL_PLATFORM)
+
+typedef struct wl_display *EGLNativeDisplayType;
+typedef struct wl_egl_pixmap *EGLNativePixmapType;
+typedef struct wl_egl_window *EGLNativeWindowType;
+
+#elif defined(__GBM__)
+
+typedef struct gbm_device *EGLNativeDisplayType;
+typedef struct gbm_bo *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
+
#elif defined(__ANDROID__) || defined(ANDROID)
struct ANativeWindow;
@@ -92,7 +104,13 @@
typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
-#elif defined(__unix__)
+#elif defined(USE_OZONE)
+
+typedef intptr_t EGLNativeDisplayType;
+typedef intptr_t EGLNativeWindowType;
+typedef intptr_t EGLNativePixmapType;
+
+#elif defined(__unix__) || defined(USE_X11)
/* X11 (tentative) */
#include <X11/Xlib.h>
@@ -102,6 +120,20 @@
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
+#elif defined(__APPLE__)
+
+typedef int EGLNativeDisplayType;
+typedef void *EGLNativeWindowType;
+typedef void *EGLNativePixmapType;
+
+#elif defined(__HAIKU__)
+
+#include <kernel/image.h>
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#else
#error "Platform not recognized"
#endif
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index d43c164..c0bace8 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -140,16 +140,22 @@
"EGL/egl_cache.cpp",
"EGL/egl_display.cpp",
"EGL/egl_object.cpp",
+ "EGL/egl_layers.cpp",
"EGL/egl.cpp",
"EGL/eglApi.cpp",
+ "EGL/egl_platform_entries.cpp",
"EGL/Loader.cpp",
+ "EGL/egl_angle_platform.cpp",
],
shared_libs: [
"libvndksupport",
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
+ "libbase",
"libhidlbase",
"libhidltransport",
+ "libnativebridge_lazy",
+ "libnativeloader_lazy",
"libutils",
],
static_libs: [
@@ -191,6 +197,7 @@
defaults: ["gles_libs_defaults"],
srcs: ["GLES_CM/gl.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv1\""],
+ version_script: "libGLESv1_CM.map.txt",
}
//##############################################################################
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index b3752f5..74c4d7d 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -79,7 +79,7 @@
}
std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
- CacheEntry dummyEntry(dummyKey, NULL);
+ CacheEntry dummyEntry(dummyKey, nullptr);
while (true) {
auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
@@ -139,7 +139,7 @@
return 0;
}
std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false));
- CacheEntry dummyEntry(dummyKey, NULL);
+ CacheEntry dummyEntry(dummyKey, nullptr);
auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry);
if (index == mCacheEntries.end() || dummyEntry < *index) {
ALOGV("get: no cache entry found for key of size %zu", keySize);
@@ -308,7 +308,7 @@
mData(copyData ? malloc(size) : data),
mSize(size),
mOwnsData(copyData) {
- if (data != NULL && copyData) {
+ if (data != nullptr && copyData) {
memcpy(const_cast<void*>(mData), data, size);
}
}
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index 1f5d535..e5c5e5b 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -97,6 +97,10 @@
//
int unflatten(void const* buffer, size_t size);
+ // clear flushes out all contents of the cache then the BlobCache, leaving
+ // it in an empty state.
+ void clear() { mCacheEntries.clear(); }
+
protected:
// mMaxTotalSize is the maximum size that all cache entries can occupy. This
// includes space for both keys and values. When a call to BlobCache::set
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index edbaaf0..cf67cf4 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -97,7 +97,7 @@
TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0));
}
TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
@@ -169,7 +169,7 @@
}
mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
}
TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
@@ -219,7 +219,7 @@
}
mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
- ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+ ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
}
TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
@@ -237,7 +237,7 @@
int numCached = 0;
for (int i = 0; i < 256; i++) {
uint8_t k = i;
- if (mBC->get(&k, 1, NULL, 0) == 1) {
+ if (mBC->get(&k, 1, nullptr, 0) == 1) {
numCached++;
}
}
@@ -260,7 +260,7 @@
int numCached = 0;
for (int i = 0; i < maxEntries+1; i++) {
uint8_t k = i;
- if (mBC->get(&k, 1, NULL, 0) == 1) {
+ if (mBC->get(&k, 1, nullptr, 0) == 1) {
numCached++;
}
}
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 7923715..cc42ac7 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -77,7 +77,7 @@
return;
}
- uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
+ uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize,
PROT_READ, MAP_PRIVATE, fd, 0));
if (buf == MAP_FAILED) {
ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 91a3455..259242b 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,7 +17,7 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "Loader.h"
+#include <EGL/Loader.h>
#include <string>
@@ -27,18 +27,17 @@
#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_platform_entries.h"
#include "egl_trace.h"
#include "egldefs.h"
-
-extern "C" {
- android_namespace_t* android_get_exported_namespace(const char*);
-}
+#include <EGL/eglext_angle.h>
// ----------------------------------------------------------------------------
namespace android {
@@ -129,7 +128,7 @@
{
dso[0] = gles;
for (size_t i=1 ; i<NELEM(dso) ; i++)
- dso[i] = 0;
+ dso[i] = nullptr;
}
Loader::driver_t::~driver_t()
@@ -137,7 +136,7 @@
for (size_t i=0 ; i<NELEM(dso) ; i++) {
if (dso[i]) {
dlclose(dso[i]);
- dso[i] = 0;
+ dso[i] = nullptr;
}
}
}
@@ -163,7 +162,7 @@
// ----------------------------------------------------------------------------
Loader::Loader()
- : getProcAddress(NULL)
+ : getProcAddress(nullptr)
{
}
@@ -219,16 +218,26 @@
void* Loader::open(egl_connection_t* cnx)
{
ATRACE_CALL();
+ const nsecs_t openTime = systemTime();
void* dso;
- driver_t* hnd = 0;
+ driver_t* hnd = nullptr;
setEmulatorGlesValue();
+ // Check if we should use ANGLE early, so loading each driver doesn't require repeated queries.
+ if (android::GraphicsEnv::getInstance().shouldUseAngle()) {
+ cnx->shouldUseAngle = true;
+ } else {
+ cnx->shouldUseAngle = false;
+ }
+
dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);
if (dso) {
hnd = new driver_t(dso);
} else {
+ android::GraphicsEnv::getInstance().clearDriverLoadingInfo(
+ android::GraphicsEnv::Api::API_GL);
// Always load EGL first
dso = load_driver("EGL", cnx, EGL);
if (dso) {
@@ -238,29 +247,53 @@
}
}
+ if (!hnd) {
+ android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL,
+ false, systemTime() - openTime);
+ }
+
LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation");
cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
+ if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) {
+ android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL,
+ false, systemTime() - openTime);
+ }
+
LOG_ALWAYS_FATAL_IF(!cnx->libEgl,
"couldn't load system EGL wrapper libraries");
LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,
"couldn't load system OpenGL ES wrapper libraries");
+ android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, true,
+ systemTime() - openTime);
+
return (void*)hnd;
}
-void Loader::close(void* driver)
+void Loader::close(egl_connection_t* cnx)
{
- driver_t* hnd = (driver_t*)driver;
+ driver_t* hnd = (driver_t*) cnx->dso;
delete hnd;
+ cnx->dso = nullptr;
+
+ cnx->shouldUseAngle = false;
+ cnx->angleDecided = false;
+ cnx->useAngle = false;
+
+ if (cnx->vendorEGL) {
+ dlclose(cnx->vendorEGL);
+ cnx->vendorEGL = nullptr;
+ }
}
void Loader::init_api(void* dso,
char const * const * api,
+ char const * const * ref_api,
__eglMustCastToProperFunctionPointerType* curr,
getProcAddressType getProcAddress)
{
@@ -270,13 +303,22 @@
char scrap[SIZE];
while (*api) {
char const * name = *api;
+ if (ref_api) {
+ char const * ref_name = *ref_api;
+ if (std::strcmp(name, ref_name) != 0) {
+ *curr++ = nullptr;
+ ref_api++;
+ continue;
+ }
+ }
+
__eglMustCastToProperFunctionPointerType f =
(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
- if (f == NULL) {
+ if (f == nullptr) {
// couldn't find the entry-point, use eglGetProcAddress()
f = getProcAddress(name);
}
- if (f == NULL) {
+ if (f == nullptr) {
// Try without the OES postfix
ssize_t index = ssize_t(strlen(name)) - 3;
if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) {
@@ -286,7 +328,7 @@
//ALOGD_IF(f, "found <%s> instead", scrap);
}
}
- if (f == NULL) {
+ if (f == nullptr) {
// Try with the OES postfix
ssize_t index = ssize_t(strlen(name)) - 3;
if (index>0 && strcmp(name+index, "OES")) {
@@ -295,7 +337,7 @@
//ALOGD_IF(f, "found <%s> instead", scrap);
}
}
- if (f == NULL) {
+ if (f == nullptr) {
//ALOGD("%s", name);
f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
@@ -314,6 +356,7 @@
}
*curr++ = f;
api++;
+ if (ref_api) ref_api++;
}
}
@@ -406,9 +449,9 @@
}
DIR* d = opendir(search);
- if (d != NULL) {
+ if (d != nullptr) {
struct dirent* e;
- while ((e = readdir(d)) != NULL) {
+ while ((e = readdir(d)) != nullptr) {
if (e->d_type == DT_DIR) {
continue;
}
@@ -434,7 +477,7 @@
std::string absolutePath = MatchFile::find(kind);
if (absolutePath.empty()) {
// this happens often, we don't want to log an error
- return 0;
+ return nullptr;
}
const char* const driver_absolute_path = absolutePath.c_str();
@@ -444,10 +487,10 @@
// sphal namespace.
void* dso = do_android_load_sphal_library(driver_absolute_path,
RTLD_NOW | RTLD_LOCAL);
- if (dso == 0) {
+ if (dso == nullptr) {
const char* err = dlerror();
ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown");
- return 0;
+ return nullptr;
}
ALOGD("loaded %s", driver_absolute_path);
@@ -455,6 +498,81 @@
return dso;
}
+static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+
+ std::string name = std::string("lib") + kind + "_angle.so";
+
+ void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+
+ if (so) {
+ ALOGD("dlopen_ext from APK (%s) success at %p", name.c_str(), so);
+ return so;
+ } else {
+ ALOGE("dlopen_ext(\"%s\") failed: %s", name.c_str(), dlerror());
+ }
+
+ return nullptr;
+}
+
+static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) {
+ // Only attempt to load ANGLE libs
+ if (strcmp(kind, "EGL") != 0 && strcmp(kind, "GLESv2") != 0 && strcmp(kind, "GLESv1_CM") != 0) {
+ return nullptr;
+ }
+
+ void* so = nullptr;
+
+ if ((cnx->shouldUseAngle) || android::GraphicsEnv::getInstance().shouldUseAngle()) {
+ so = load_angle_from_namespace(kind, ns);
+ cnx->shouldUseAngle = true;
+ } else {
+ cnx->shouldUseAngle = false;
+ }
+
+ if (so) {
+ ALOGV("Loaded ANGLE %s library for '%s' (instead of native)", kind,
+ android::GraphicsEnv::getInstance().getAngleAppName().c_str());
+ cnx->useAngle = true;
+
+ char prop[PROPERTY_VALUE_MAX];
+
+ property_get("debug.hwui.renderer", prop, "UNSET");
+ ALOGV("Skia's renderer set to %s", prop);
+
+ EGLint angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
+ property_get("debug.angle.backend", prop, "0");
+ switch (atoi(prop)) {
+ case 1:
+ ALOGV("%s: Requesting OpenGLES back-end", __FUNCTION__);
+ angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
+ break;
+ case 2:
+ ALOGV("%s: Requesting Vulkan back-end", __FUNCTION__);
+ angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
+ break;
+ default:
+ break;
+ }
+
+ cnx->angleBackend = angleBackendDefault;
+ if (!cnx->vendorEGL && (cnx->angleBackend == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) {
+ // Find and load vendor libEGL for ANGLE's GL back-end to use.
+ cnx->vendorEGL = load_system_driver("EGL");
+ }
+ } else {
+ ALOGV("Loaded native %s library for '%s' (instead of ANGLE)", kind,
+ android::GraphicsEnv::getInstance().getAngleAppName().c_str());
+ cnx->useAngle = false;
+ }
+ cnx->angleDecided = true;
+
+ return so;
+}
+
static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
"ro.hardware.egl",
"ro.board.platform",
@@ -486,16 +604,26 @@
ATRACE_CALL();
void* dso = nullptr;
-#ifndef __ANDROID_VNDK__
- android_namespace_t* ns = android_getDriverNamespace();
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
if (ns) {
- dso = load_updated_driver(kind, ns);
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::ANGLE);
+ dso = load_angle(kind, ns, cnx);
+ }
+#ifndef __ANDROID_VNDK__
+ if (!dso) {
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
+ if (ns) {
+ android::GraphicsEnv::getInstance().setDriverToLoad(
+ android::GraphicsEnv::Driver::GL_UPDATED);
+ dso = load_updated_driver(kind, ns);
+ }
}
#endif
if (!dso) {
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL);
dso = load_system_driver(kind);
if (!dso)
- return NULL;
+ return nullptr;
}
if (mask & EGL) {
@@ -512,11 +640,11 @@
char const * name = *api;
__eglMustCastToProperFunctionPointerType f =
(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
- if (f == NULL) {
+ if (f == nullptr) {
// couldn't find the entry-point, use eglGetProcAddress()
f = getProcAddress(name);
- if (f == NULL) {
- f = (__eglMustCastToProperFunctionPointerType)0;
+ if (f == nullptr) {
+ f = (__eglMustCastToProperFunctionPointerType)nullptr;
}
}
*curr++ = f;
@@ -525,14 +653,14 @@
}
if (mask & GLESv1_CM) {
- init_api(dso, gl_names,
+ init_api(dso, gl_names_1, gl_names,
(__eglMustCastToProperFunctionPointerType*)
&cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,
getProcAddress);
}
if (mask & GLESv2) {
- init_api(dso, gl_names,
+ init_api(dso, gl_names, nullptr,
(__eglMustCastToProperFunctionPointerType*)
&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
getProcAddress);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 6a32bb3..392887d 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -29,11 +29,12 @@
class Loader {
typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*);
-
+
enum {
EGL = 0x01,
GLESv1_CM = 0x02,
- GLESv2 = 0x04
+ GLESv2 = 0x04,
+ PLATFORM = 0x08
};
struct driver_t {
explicit driver_t(void* gles);
@@ -42,25 +43,26 @@
int set(void* hnd, int32_t api);
void* dso[3];
};
-
+
getProcAddressType getProcAddress;
public:
static Loader& getInstance();
~Loader();
-
+
void* open(egl_connection_t* cnx);
- void close(void* driver);
-
+ void close(egl_connection_t* cnx);
+
private:
Loader();
void *load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask);
static __attribute__((noinline))
- void init_api(void* dso,
- char const * const * api,
- __eglMustCastToProperFunctionPointerType* curr,
- getProcAddressType getProcAddress);
+ void init_api(void* dso,
+ char const * const * api,
+ char const * const * ref_api,
+ __eglMustCastToProperFunctionPointerType* curr,
+ getProcAddressType getProcAddress);
};
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index f53cf3f..8870d5f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -30,11 +30,10 @@
#include "egl_tls.h"
#include "egl_display.h"
#include "egl_object.h"
+#include "egl_layers.h"
#include "CallStack.h"
#include "Loader.h"
-typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
-
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -89,22 +88,22 @@
egl_display_ptr validate_display(EGLDisplay dpy) {
egl_display_ptr dp = get_display(dpy);
if (!dp)
- return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL));
+ return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr));
if (!dp->isReady())
- return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL));
+ return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr));
return dp;
}
egl_display_ptr validate_display_connection(EGLDisplay dpy,
egl_connection_t*& cnx) {
- cnx = NULL;
+ cnx = nullptr;
egl_display_ptr dp = validate_display(dpy);
if (!dp)
return dp;
cnx = &gEGLImpl;
- if (cnx->dso == 0) {
- return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL));
+ if (cnx->dso == nullptr) {
+ return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr));
}
return dp;
}
@@ -117,14 +116,14 @@
EGLContext context = egl_tls_t::getContext();
if (context == EGL_NO_CONTEXT)
- return NULL;
+ return nullptr;
egl_context_t const * const c = get_context(context);
- if (c == NULL) // this should never happen, by construction
- return NULL;
+ if (c == nullptr) // this should never happen, by construction
+ return nullptr;
if (name != GL_EXTENSIONS)
- return NULL;
+ return nullptr;
return (const GLubyte *)c->gl_extensions.c_str();
}
@@ -135,19 +134,19 @@
EGLContext context = egl_tls_t::getContext();
if (context == EGL_NO_CONTEXT)
- return NULL;
+ return nullptr;
egl_context_t const * const c = get_context(context);
- if (c == NULL) // this should never happen, by construction
- return NULL;
+ if (c == nullptr) // this should never happen, by construction
+ return nullptr;
if (name != GL_EXTENSIONS)
- return NULL;
+ return nullptr;
// if index is out of bounds, assume it will be in the default
// implementation too, so we don't have to generate a GL error here
if (index >= c->tokenized_gl_extensions.size())
- return NULL;
+ return nullptr;
return (const GLubyte *)c->tokenized_gl_extensions[index].c_str();
}
@@ -161,12 +160,16 @@
return -1;
egl_context_t const * const c = get_context(context);
- if (c == NULL) // this should never happen, by construction
+ if (c == nullptr) // this should never happen, by construction
return -1;
return (GLint)c->tokenized_gl_extensions.size();
}
+egl_connection_t* egl_get_connection() {
+ return &gEGLImpl;
+}
+
// ----------------------------------------------------------------------------
// this mutex protects:
@@ -184,7 +187,7 @@
// dynamically load our EGL implementation
egl_connection_t* cnx = &gEGLImpl;
- if (cnx->dso == 0) {
+ if (cnx->dso == nullptr) {
cnx->hooks[egl_connection_t::GLESv1_INDEX] =
&gHooks[egl_connection_t::GLESv1_INDEX];
cnx->hooks[egl_connection_t::GLESv2_INDEX] =
@@ -192,6 +195,14 @@
cnx->dso = loader.open(cnx);
}
+ // Check to see if any layers are enabled and route functions through them
+ if (cnx->dso) {
+ // Layers can be enabled long after the drivers have been loaded.
+ // They will only be initialized once.
+ LayerLoader& layer_loader(LayerLoader::getInstance());
+ layer_loader.InitLayers(cnx);
+ }
+
return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
@@ -249,12 +260,22 @@
char const * const gl_names[] = {
#include "../entries.in"
- NULL
+ nullptr
+};
+
+char const * const gl_names_1[] = {
+ #include "../entries_gles1.in"
+ nullptr
};
char const * const egl_names[] = {
#include "egl_entries.in"
- NULL
+ nullptr
+};
+
+char const * const platform_names[] = {
+ #include "platform_entries.in"
+ nullptr
};
#undef GL_ENTRY
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 36deedc..29a966d 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1,5 +1,5 @@
/*
- ** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018, 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.
@@ -16,1170 +16,225 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <hardware/gralloc1.h>
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <android/hardware_buffer.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 "../egl_impl.h"
-#include "egl_display.h"
-#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_platform_entries.h"
#include "egl_tls.h"
#include "egl_trace.h"
using namespace android;
-// ----------------------------------------------------------------------------
-
namespace android {
-using nsecs_t = int64_t;
+extern EGLBoolean egl_init_drivers();
-struct extention_map_t {
- const char* name;
- __eglMustCastToProperFunctionPointerType address;
-};
+} // namespace android
-/*
- * This is the list of EGL extensions exposed to applications.
- *
- * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
- * wrapper and are always available.
- *
- * The rest (gExtensionString) depend on support in the EGL driver, and are
- * only available if the driver supports them. However, some of these must be
- * supported because they are used by the Android system itself; these are
- * listed as mandatory below and are required by the CDD. The system *assumes*
- * the mandatory extensions are present and may not function properly if some
- * are missing.
- *
- * NOTE: Both strings MUST have a single space as the last character.
- */
-
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
-
-// clang-format off
-// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
- "EGL_KHR_get_all_proc_addresses "
- "EGL_ANDROID_presentation_time "
- "EGL_KHR_swap_buffers_with_damage "
- "EGL_ANDROID_get_native_client_buffer "
- "EGL_ANDROID_front_buffer_auto_refresh "
- "EGL_ANDROID_get_frame_timestamps "
- "EGL_EXT_surface_SMPTE2086_metadata "
- "EGL_EXT_surface_CTA861_3_metadata "
- ;
-
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString =
- "EGL_KHR_image " // mandatory
- "EGL_KHR_image_base " // mandatory
- "EGL_EXT_image_gl_colorspace "
- "EGL_KHR_image_pixmap "
- "EGL_KHR_lock_surface "
- "EGL_KHR_gl_colorspace "
- "EGL_KHR_gl_texture_2D_image "
- "EGL_KHR_gl_texture_3D_image "
- "EGL_KHR_gl_texture_cubemap_image "
- "EGL_KHR_gl_renderbuffer_image "
- "EGL_KHR_reusable_sync "
- "EGL_KHR_fence_sync "
- "EGL_KHR_create_context "
- "EGL_KHR_config_attribs "
- "EGL_KHR_surfaceless_context "
- "EGL_KHR_stream "
- "EGL_KHR_stream_fifo "
- "EGL_KHR_stream_producer_eglsurface "
- "EGL_KHR_stream_consumer_gltexture "
- "EGL_KHR_stream_cross_process_fd "
- "EGL_EXT_create_context_robustness "
- "EGL_NV_system_time "
- "EGL_ANDROID_image_native_buffer " // mandatory
- "EGL_KHR_wait_sync " // strongly recommended
- "EGL_ANDROID_recordable " // mandatory
- "EGL_KHR_partial_update " // strongly recommended
- "EGL_EXT_pixel_format_float "
- "EGL_EXT_buffer_age " // strongly recommended with partial_update
- "EGL_KHR_create_context_no_error "
- "EGL_KHR_mutable_render_buffer "
- "EGL_EXT_yuv_surface "
- "EGL_EXT_protected_content "
- "EGL_IMG_context_priority "
- "EGL_KHR_no_config_context "
- ;
-// clang-format on
-
-// extensions not exposed to applications but used by the ANDROID system
-// "EGL_ANDROID_blob_cache " // strongly recommended
-// "EGL_IMG_hibernate_process " // optional
-// "EGL_ANDROID_native_fence_sync " // strongly recommended
-// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
-// "EGL_ANDROID_image_crop " // optional
-
-/*
- * EGL Extensions entry-points exposed to 3rd party applications
- * (keep in sync with gExtensionString above)
- *
- */
-static const extention_map_t sExtensionMap[] = {
- // EGL_KHR_lock_surface
- { "eglLockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
- { "eglUnlockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
-
- // EGL_KHR_image, EGL_KHR_image_base
- { "eglCreateImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
- { "eglDestroyImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
-
- // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
- { "eglCreateSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
- { "eglDestroySyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
- { "eglClientWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
- { "eglSignalSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
- { "eglGetSyncAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
-
- // EGL_NV_system_time
- { "eglGetSystemTimeFrequencyNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
- { "eglGetSystemTimeNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
-
- // EGL_KHR_wait_sync
- { "eglWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
-
- // EGL_ANDROID_presentation_time
- { "eglPresentationTimeANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
-
- // EGL_KHR_swap_buffers_with_damage
- { "eglSwapBuffersWithDamageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
-
- // EGL_ANDROID_get_native_client_buffer
- { "eglGetNativeClientBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
-
- // EGL_KHR_partial_update
- { "eglSetDamageRegionKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
-
- { "eglCreateStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
- { "eglDestroyStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
- { "eglStreamAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
- { "eglQueryStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
- { "eglQueryStreamu64KHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
- { "eglQueryStreamTimeKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
- { "eglCreateStreamProducerSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
- { "eglStreamConsumerGLTextureExternalKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
- { "eglStreamConsumerAcquireKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
- { "eglStreamConsumerReleaseKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
- { "eglGetStreamFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
- { "eglCreateStreamFromFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
-
- // EGL_ANDROID_get_frame_timestamps
- { "eglGetNextFrameIdANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
- { "eglGetCompositorTimingANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
- { "eglGetCompositorTimingSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
- { "eglGetFrameTimestampsANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
- { "eglGetFrameTimestampSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
-
- // EGL_ANDROID_native_fence_sync
- { "eglDupNativeFenceFDANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
-};
-
-/*
- * These extensions entry-points should not be exposed to applications.
- * They're used internally by the Android EGL layer.
- */
-#define FILTER_EXTENSIONS(procname) \
- (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
- !strcmp((procname), "eglHibernateProcessIMG") || \
- !strcmp((procname), "eglAwakenProcessIMG"))
-
-// accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
-
-static int sGLExtentionSlot = 0;
-static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void(*findProcAddress(const char* name,
- const extention_map_t* map, size_t n))() {
- for (uint32_t i=0 ; i<n ; i++) {
- if (!strcmp(name, map[i].name)) {
- return map[i].address;
- }
- }
- return NULL;
+static inline void clearError() {
+ egl_tls_t::clearError();
}
-// ----------------------------------------------------------------------------
-
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
-extern gl_hooks_t gHooksTrace;
-
-} // namespace android;
-
-
-// ----------------------------------------------------------------------------
-
-static inline void clearError() { egl_tls_t::clearError(); }
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
-
-// ----------------------------------------------------------------------------
-
-EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
-{
+EGLDisplay eglGetDisplay(EGLNativeDisplayType display) {
ATRACE_CALL();
clearError();
- uintptr_t index = reinterpret_cast<uintptr_t>(display);
- if (index >= NUM_DISPLAYS) {
- return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
- }
-
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
}
- EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
- return dpy;
+ // Call down the chain, which usually points directly to the impl
+ // but may also be routed through layers
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetDisplay(display);
}
-// ----------------------------------------------------------------------------
-// Initialization
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
+EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list) {
+ ATRACE_CALL();
clearError();
- egl_display_ptr dp = get_display(dpy);
- if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
- EGLBoolean res = dp->initialize(major, minor);
-
- return res;
-}
-
-EGLBoolean eglTerminate(EGLDisplay dpy)
-{
- // NOTE: don't unload the drivers b/c some APIs can be called
- // after eglTerminate() has been called. eglTerminate() only
- // terminates an EGLDisplay, not a EGL itself.
-
- clearError();
-
- egl_display_ptr dp = get_display(dpy);
- if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
- EGLBoolean res = dp->terminate();
-
- return res;
-}
-
-// ----------------------------------------------------------------------------
-// configuration
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglGetConfigs( EGLDisplay dpy,
- EGLConfig *configs,
- EGLint config_size, EGLint *num_config)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- if (num_config==0) {
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
}
- EGLBoolean res = EGL_FALSE;
- *num_config = 0;
+ // Call down the chain, which usually points directly to the impl
+ // but may also be routed through layers
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetPlatformDisplay(platform, display, attrib_list);
+}
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+ clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- res = cnx->egl.eglGetConfigs(
- dp->disp.dpy, configs, config_size, num_config);
- }
-
- return res;
+ return cnx->platform.eglInitialize(dpy, major, minor);
}
-EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
- EGLConfig *configs, EGLint config_size,
- EGLint *num_config)
-{
+EGLBoolean eglTerminate(EGLDisplay dpy) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- if (num_config==0) {
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
-
- EGLBoolean res = EGL_FALSE;
- *num_config = 0;
-
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")) {
- size_t attribCount = 0;
- EGLint attrib = attrib_list[0];
-
- // Only enable MSAA if the context is OpenGL ES 2.0 and
- // if no caveat is requested
- const EGLint *attribRendererable = NULL;
- const EGLint *attribCaveat = NULL;
-
- // Count the number of attributes and look for
- // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
- while (attrib != EGL_NONE) {
- attrib = attrib_list[attribCount];
- switch (attrib) {
- case EGL_RENDERABLE_TYPE:
- attribRendererable = &attrib_list[attribCount];
- break;
- case EGL_CONFIG_CAVEAT:
- attribCaveat = &attrib_list[attribCount];
- break;
- default:
- break;
- }
- attribCount++;
- }
-
- if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
- (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
-
- // Insert 2 extra attributes to force-enable MSAA 4x
- EGLint aaAttribs[attribCount + 4];
- aaAttribs[0] = EGL_SAMPLE_BUFFERS;
- aaAttribs[1] = 1;
- aaAttribs[2] = EGL_SAMPLES;
- aaAttribs[3] = 4;
-
- memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
- EGLint numConfigAA;
- EGLBoolean resAA = cnx->egl.eglChooseConfig(
- dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
- if (resAA == EGL_TRUE && numConfigAA > 0) {
- ALOGD("Enabling MSAA 4x");
- *num_config = numConfigAA;
- return resAA;
- }
- }
- }
- }
-
- res = cnx->egl.eglChooseConfig(
- dp->disp.dpy, attrib_list, configs, config_size, num_config);
- }
- return res;
+ return cnx->platform.eglTerminate(dpy);
}
-EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
- EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+ EGLint* num_config) {
clearError();
- egl_connection_t* cnx = NULL;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (!dp) return EGL_FALSE;
-
- return cnx->egl.eglGetConfigAttrib(
- dp->disp.dpy, config, attribute, value);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config);
}
-// ----------------------------------------------------------------------------
-// surfaces
-// ----------------------------------------------------------------------------
-
-// Translates EGL color spaces to Android data spaces.
-static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
- if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
- return HAL_DATASPACE_UNKNOWN;
- } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
- return HAL_DATASPACE_SRGB;
- } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
- return HAL_DATASPACE_DISPLAY_P3;
- } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
- return HAL_DATASPACE_DISPLAY_P3_LINEAR;
- } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
- return HAL_DATASPACE_V0_SCRGB;
- } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
- return HAL_DATASPACE_V0_SCRGB_LINEAR;
- } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
- return HAL_DATASPACE_BT2020_LINEAR;
- } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
- return HAL_DATASPACE_BT2020_PQ;
- }
- return HAL_DATASPACE_UNKNOWN;
-}
-
-// Get the colorspace value that should be reported from queries. When the colorspace
-// is unknown (no attribute passed), default to reporting LINEAR.
-static EGLint getReportedColorSpace(EGLint colorspace) {
- return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
-}
-
-// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
- std::vector<EGLint> colorSpaces;
-
- // sRGB and linear are always supported when color space support is present.
- colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
- colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
-
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_display_p3")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_scrgb")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_scrgb_linear")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_bt2020_linear")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_bt2020_pq")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_display_p3_linear")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
- }
- return colorSpaces;
-}
-
-// Cleans up color space related parameters that the driver does not understand.
-// If there is no color space attribute in attrib_list, colorSpace is left
-// unmodified.
-static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window,
- const EGLint* attrib_list, EGLint* colorSpace,
- std::vector<EGLint>* strippedAttribList) {
- for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
- bool copyAttribute = true;
- if (attr[0] == EGL_GL_COLORSPACE_KHR) {
- switch (attr[1]) {
- case EGL_GL_COLORSPACE_LINEAR_KHR:
- case EGL_GL_COLORSPACE_SRGB_KHR:
- case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
- case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
- case EGL_GL_COLORSPACE_SCRGB_EXT:
- case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
- case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
- case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
- // Fail immediately if the driver doesn't have color space support at all.
- if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
- break;
- default:
- // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_*
- return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
- }
- *colorSpace = attr[1];
-
- // Strip the attribute if the driver doesn't understand it.
- copyAttribute = false;
- std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp);
- for (auto driverColorSpace : driverColorSpaces) {
- if (attr[1] == driverColorSpace) {
- copyAttribute = true;
- break;
- }
- }
-
- // If the driver doesn't understand it, we should map sRGB-encoded P3 to
- // sRGB rather than just dropping the colorspace on the floor.
- // For this format, the driver is expected to apply the sRGB
- // transfer function during framebuffer operations.
- if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
- strippedAttribList->push_back(attr[0]);
- strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
- }
- }
- if (copyAttribute) {
- strippedAttribList->push_back(attr[0]);
- strippedAttribList->push_back(attr[1]);
- }
- }
- // Terminate the attribute list.
- strippedAttribList->push_back(EGL_NONE);
-
- // If the passed color space has wide color gamut, check whether the target native window
- // supports wide color.
- const bool colorSpaceIsNarrow =
- *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
- *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR ||
- *colorSpace == EGL_UNKNOWN;
- if (window && !colorSpaceIsNarrow) {
- bool windowSupportsWideColor = true;
- // Ordinarily we'd put a call to native_window_get_wide_color_support
- // at the beginning of the function so that we'll have the
- // result when needed elsewhere in the function.
- // However, because eglCreateWindowSurface is called by SurfaceFlinger and
- // SurfaceFlinger is required to answer the call below we would
- // end up in a deadlock situation. By moving the call to only happen
- // if the application has specifically asked for wide-color we avoid
- // the deadlock with SurfaceFlinger since it will not ask for a
- // wide-color surface.
- int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
-
- if (err) {
- ALOGE("processAttributes: invalid window (win=%p) "
- "failed (%#x) (already connected to another API?)",
- window, err);
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE);
- }
- if (!windowSupportsWideColor) {
- // Application has asked for a wide-color colorspace but
- // wide-color support isn't available on the display the window is on.
- return setError(EGL_BAD_MATCH, EGL_FALSE);
- }
- }
- return true;
-}
-
-// Gets the native pixel format corrsponding to the passed EGLConfig.
-void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
- android_pixel_format* format) {
- // Set the native window's buffers format to match what this config requests.
- // Whether to use sRGB gamma is not part of the EGLconfig, but is part
- // of our native format. So if sRGB gamma is requested, we have to
- // modify the EGLconfig's format before setting the native window's
- // format.
-
- EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
-
- EGLint a = 0;
- EGLint r, g, b;
- r = g = b = 0;
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
- EGLint colorDepth = r + g + b;
-
- // Today, the driver only understands sRGB and linear on 888X
- // formats. Strip other colorspaces from the attribute list and
- // only use them to set the dataspace via
- // native_window_set_buffers_dataspace
- // if pixel format is RGBX 8888
- // TBD: Can test for future extensions that indicate that driver
- // handles requested color space and we can let it through.
- // allow SRGB and LINEAR. All others need to be stripped.
- // else if 565, 4444
- // TBD: Can we assume these are supported if 8888 is?
- // else if FP16 or 1010102
- // strip colorspace from attribs.
- // endif
- if (a == 0) {
- if (colorDepth <= 16) {
- *format = HAL_PIXEL_FORMAT_RGB_565;
- } else {
- if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
- if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
- } else {
- *format = HAL_PIXEL_FORMAT_RGBX_8888;
- }
- } else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
- }
- }
- } else {
- if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
- if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
- } else {
- *format = HAL_PIXEL_FORMAT_RGBA_8888;
- }
- } else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
- }
- }
-}
-
-EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
- android_smpte2086_metadata smpteMetadata;
- if (s->getSmpte2086Metadata(smpteMetadata)) {
- int err =
- native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
- s->resetSmpte2086Metadata();
- if (err != 0) {
- ALOGE("error setting native window smpte2086 metadata: %s (%d)",
- strerror(-err), err);
- return EGL_FALSE;
- }
- }
- android_cta861_3_metadata cta8613Metadata;
- if (s->getCta8613Metadata(cta8613Metadata)) {
- int err =
- native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
- s->resetCta8613Metadata();
- if (err != 0) {
- ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
- strerror(-err), err);
- return EGL_FALSE;
- }
- }
- return EGL_TRUE;
-}
-
-EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
- NativeWindowType window,
- const EGLint *attrib_list)
-{
- const EGLint *origAttribList = attrib_list;
+EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+ EGLint config_size, EGLint* num_config) {
clearError();
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- if (!window) {
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
-
- int value = 0;
- window->query(window, NATIVE_WINDOW_IS_VALID, &value);
- if (!value) {
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
-
- int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
- if (result < 0) {
- ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
- "failed (%#x) (already connected to another API?)",
- window, result);
- return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
- }
-
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
-
- // now select correct colorspace and dataspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, window, attrib_list, &colorSpace,
- &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- return EGL_NO_SURFACE;
- }
- attrib_list = strippedAttribList.data();
-
- {
- int err = native_window_set_buffers_format(window, format);
- if (err != 0) {
- ALOGE("error setting native window pixel format: %s (%d)",
- strerror(-err), err);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
- }
-
- android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
- // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. HAL_DATASPACE_UNKNOWN
- // is the default value, but it may have changed at this point.
- int err = native_window_set_buffers_data_space(window, dataSpace);
- if (err != 0) {
- ALOGE("error setting native window pixel dataSpace: %s (%d)",
- strerror(-err), err);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
-
- // the EGL spec requires that a new EGLSurface default to swap interval
- // 1, so explicitly set that on the window here.
- ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
- anw->setSwapInterval(anw, 1);
-
- EGLSurface surface = cnx->egl.eglCreateWindowSurface(
- iDpy, config, window, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s =
- new egl_surface_t(dp.get(), config, window, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
-
- // EGLSurface creation failed
- native_window_set_buffers_format(window, 0);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- }
- return EGL_NO_SURFACE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config);
}
-EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config,
- NativePixmapType pixmap,
- const EGLint *attrib_list)
-{
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) {
clearError();
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
-
- // now select a corresponding sRGB format if needed
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, attrib_list, &colorSpace,
- &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return EGL_NO_SURFACE;
- }
- attrib_list = strippedAttribList.data();
-
- EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
- dp->disp.dpy, config, pixmap, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s =
- new egl_surface_t(dp.get(), config, NULL, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
- }
- return EGL_NO_SURFACE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value);
}
-EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
- const EGLint *attrib_list)
-{
+EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+ const EGLint* attrib_list) {
clearError();
- egl_connection_t* cnx = NULL;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
-
- // Select correct colorspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, attrib_list, &colorSpace,
- &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return EGL_NO_SURFACE;
- }
- attrib_list = strippedAttribList.data();
-
- EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
- dp->disp.dpy, config, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s =
- new egl_surface_t(dp.get(), config, NULL, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
- }
- return EGL_NO_SURFACE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list);
}
-EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
-{
+EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window,
+ const EGLAttrib* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t * const s = get_surface(surface);
- EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
- if (result == EGL_TRUE) {
- _s.terminate();
- }
- return result;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
}
-EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
- EGLint attribute, EGLint *value)
-{
+EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap,
+ const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list);
+}
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap,
+ const EGLAttrib* attrib_list) {
+ clearError();
- egl_surface_t const * const s = get_surface(surface);
- if (s->getColorSpaceAttribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->getSmpte2086Attribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->getCta8613Attribute(attribute, value)) {
- return EGL_TRUE;
- }
- return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list);
+}
+
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list);
+}
+
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroySurface(dpy, surface);
+}
+
+EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQuerySurface(dpy, surface, attribute, value);
}
void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
ATRACE_CALL();
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return;
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ cnx->platform.eglBeginFrame(dpy, surface);
}
-// ----------------------------------------------------------------------------
-// Contexts
-// ----------------------------------------------------------------------------
-
-EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
- EGLContext share_list, const EGLint *attrib_list)
-{
- clearError();
-
- egl_connection_t* cnx = NULL;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- if (share_list != EGL_NO_CONTEXT) {
- if (!ContextRef(dp.get(), share_list).get()) {
- return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
- }
- egl_context_t* const c = get_context(share_list);
- share_list = c->context;
- }
- EGLContext context = cnx->egl.eglCreateContext(
- dp->disp.dpy, config, share_list, attrib_list);
- if (context != EGL_NO_CONTEXT) {
- // figure out if it's a GLESv1 or GLESv2
- int version = 0;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION) {
- if (value == 1) {
- version = egl_connection_t::GLESv1_INDEX;
- } else if (value == 2 || value == 3) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- }
- };
- }
- egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
- version);
- return c;
- }
- }
- return EGL_NO_CONTEXT;
-}
-
-EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return EGL_FALSE;
-
- ContextRef _c(dp.get(), ctx);
- if (!_c.get())
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- egl_context_t * const c = get_context(ctx);
- EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
- if (result == EGL_TRUE) {
- _c.terminate();
- }
- return result;
-}
-
-EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
- EGLSurface read, EGLContext ctx)
-{
- clearError();
-
- egl_display_ptr dp = validate_display(dpy);
- if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
- // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
- // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
- // a valid but uninitialized display.
- if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
- (draw != EGL_NO_SURFACE) ) {
- if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
-
- // get a reference to the object passed in
- ContextRef _c(dp.get(), ctx);
- SurfaceRef _d(dp.get(), draw);
- SurfaceRef _r(dp.get(), read);
-
- // validate the context (if not EGL_NO_CONTEXT)
- if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
- // EGL_NO_CONTEXT is valid
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- }
-
- // these are the underlying implementation's object
- EGLContext impl_ctx = EGL_NO_CONTEXT;
- EGLSurface impl_draw = EGL_NO_SURFACE;
- EGLSurface impl_read = EGL_NO_SURFACE;
-
- // these are our objects structs passed in
- egl_context_t * c = NULL;
- egl_surface_t const * d = NULL;
- egl_surface_t const * r = NULL;
-
- // these are the current objects structs
- egl_context_t * cur_c = get_context(getContext());
-
- if (ctx != EGL_NO_CONTEXT) {
- c = get_context(ctx);
- impl_ctx = c->context;
- } else {
- // no context given, use the implementation of the current context
- if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
- // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
- return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
- }
- if (cur_c == NULL) {
- // no current context
- // not an error, there is just no current context.
- return EGL_TRUE;
- }
- }
-
- // retrieve the underlying implementation's draw EGLSurface
- if (draw != EGL_NO_SURFACE) {
- if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- d = get_surface(draw);
- impl_draw = d->surface;
- }
-
- // retrieve the underlying implementation's read EGLSurface
- if (read != EGL_NO_SURFACE) {
- if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- r = get_surface(read);
- impl_read = r->surface;
- }
-
-
- EGLBoolean result = dp->makeCurrent(c, cur_c,
- draw, read, ctx,
- impl_draw, impl_read, impl_ctx);
-
- if (result == EGL_TRUE) {
- if (c) {
- setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
- egl_tls_t::setContext(ctx);
- _c.acquire();
- _r.acquire();
- _d.acquire();
- } else {
- setGLHooksThreadSpecific(&gHooksNoContext);
- egl_tls_t::setContext(EGL_NO_CONTEXT);
- }
- } else {
- // this will ALOGE the error
- egl_connection_t* const cnx = &gEGLImpl;
- result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
- }
- return result;
-}
-
-
-EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
- EGLint attribute, EGLint *value)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- ContextRef _c(dp.get(), ctx);
- if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- egl_context_t * const c = get_context(ctx);
- return c->cnx->egl.eglQueryContext(
- dp->disp.dpy, c->context, attribute, value);
-
-}
-
-EGLContext eglGetCurrentContext(void)
-{
- // could be called before eglInitialize(), but we wouldn't have a context
- // then, and this function would correctly return EGL_NO_CONTEXT.
-
- clearError();
-
- EGLContext ctx = getContext();
- return ctx;
-}
-
-EGLSurface eglGetCurrentSurface(EGLint readdraw)
-{
- // could be called before eglInitialize(), but we wouldn't have a context
- // then, and this function would correctly return EGL_NO_SURFACE.
-
- clearError();
-
- EGLContext ctx = getContext();
- if (ctx) {
- egl_context_t const * const c = get_context(ctx);
- if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
- switch (readdraw) {
- case EGL_READ: return c->read;
- case EGL_DRAW: return c->draw;
- default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
- }
- }
- return EGL_NO_SURFACE;
-}
-
-EGLDisplay eglGetCurrentDisplay(void)
-{
- // could be called before eglInitialize(), but we wouldn't have a context
- // then, and this function would correctly return EGL_NO_DISPLAY.
-
- clearError();
-
- EGLContext ctx = getContext();
- if (ctx) {
- egl_context_t const * const c = get_context(ctx);
- if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
- return c->dpy;
- }
- return EGL_NO_DISPLAY;
-}
-
-EGLBoolean eglWaitGL(void)
-{
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+ const EGLint* attrib_list) {
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- return cnx->egl.eglWaitGL();
+ return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list);
}
-EGLBoolean eglWaitNative(EGLint engine)
-{
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- return cnx->egl.eglWaitNative(engine);
+ return cnx->platform.eglDestroyContext(dpy, ctx);
}
-EGLint eglGetError(void)
-{
- EGLint err = EGL_SUCCESS;
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+ clearError();
+
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- err = cnx->egl.eglGetError();
- }
- if (err == EGL_SUCCESS) {
- err = egl_tls_t::getError();
- }
- return err;
+ return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx);
}
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
- const char* procname) {
- const egl_connection_t* cnx = &gEGLImpl;
- void* proc = NULL;
+EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+ clearError();
- proc = dlsym(cnx->libEgl, procname);
- if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
- proc = dlsym(cnx->libGles2, procname);
- if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
- proc = dlsym(cnx->libGles1, procname);
- if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
- return NULL;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryContext(dpy, ctx, attribute, value);
}
-__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
-{
+EGLContext eglGetCurrentContext(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCurrentContext();
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCurrentSurface(readdraw);
+}
+
+EGLDisplay eglGetCurrentDisplay(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCurrentDisplay();
+}
+
+EGLBoolean eglWaitGL(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglWaitGL();
+}
+
+EGLBoolean eglWaitNative(EGLint engine) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglWaitNative(engine);
+}
+
+EGLint eglGetError(void) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetError();
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) {
// eglGetProcAddress() could be the very first function called
// in which case we must make sure we've initialized ourselves, this
// happens the first time egl_get_display() is called.
@@ -1188,431 +243,87 @@
if (egl_init_drivers() == EGL_FALSE) {
setError(EGL_BAD_PARAMETER, NULL);
- return NULL;
+ return nullptr;
}
- if (FILTER_EXTENSIONS(procname)) {
- return NULL;
- }
-
- __eglMustCastToProperFunctionPointerType addr;
- addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
- if (addr) return addr;
-
- addr = findBuiltinWrapper(procname);
- if (addr) return addr;
-
- // this protects accesses to sGLExtentionMap and sGLExtentionSlot
- pthread_mutex_lock(&sExtensionMapMutex);
-
- /*
- * Since eglGetProcAddress() is not associated to anything, it needs
- * to return a function pointer that "works" regardless of what
- * the current context is.
- *
- * For this reason, we return a "forwarder", a small stub that takes
- * care of calling the function associated with the context
- * currently bound.
- *
- * We first look for extensions we've already resolved, if we're seeing
- * this extension for the first time, we go through all our
- * implementations and call eglGetProcAddress() and record the
- * result in the appropriate implementation hooks and return the
- * address of the forwarder corresponding to that hook set.
- *
- */
-
- const std::string name(procname);
-
- auto& extentionMap = sGLExtentionMap;
- auto pos = extentionMap.find(name);
- addr = (pos != extentionMap.end()) ? pos->second : nullptr;
- const int slot = sGLExtentionSlot;
-
- ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
- "no more slots for eglGetProcAddress(\"%s\")",
- procname);
-
- if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
- bool found = false;
-
- egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglGetProcAddress) {
- // Extensions are independent of the bound context
- addr =
- cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
- cnx->egl.eglGetProcAddress(procname);
- if (addr) found = true;
- }
-
- if (found) {
- addr = gExtensionForwarders[slot];
- extentionMap[name] = addr;
- sGLExtentionSlot++;
- }
- }
-
- pthread_mutex_unlock(&sExtensionMapMutex);
- return addr;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetProcAddress(procname);
}
-class FrameCompletionThread {
-public:
-
- static void queueSync(EGLSyncKHR sync) {
- static FrameCompletionThread thread;
-
- char name[64];
-
- std::lock_guard<std::mutex> lock(thread.mMutex);
- snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
- ATRACE_NAME(name);
-
- thread.mQueue.push_back(sync);
- thread.mCondition.notify_one();
- thread.mFramesQueued++;
- ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
- }
-
-private:
-
- FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
- std::thread thread(&FrameCompletionThread::loop, this);
- thread.detach();
- }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
- void loop() {
- while (true) {
- threadLoop();
- }
- }
-#pragma clang diagnostic pop
-
- void threadLoop() {
- EGLSyncKHR sync;
- uint32_t frameNum;
- {
- std::unique_lock<std::mutex> lock(mMutex);
- while (mQueue.empty()) {
- mCondition.wait(lock);
- }
- sync = mQueue[0];
- frameNum = mFramesCompleted;
- }
- EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- {
- char name[64];
- snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
- ATRACE_NAME(name);
-
- EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
- if (result == EGL_FALSE) {
- ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
- } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGE("FrameCompletion: timeout waiting for fence");
- }
- eglDestroySyncKHR(dpy, sync);
- }
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mQueue.pop_front();
- mFramesCompleted++;
- ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
- }
- }
-
- uint32_t mFramesQueued;
- uint32_t mFramesCompleted;
- std::deque<EGLSyncKHR> mQueue;
- std::condition_variable mCondition;
- std::mutex mMutex;
-};
-
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
- EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+ EGLint n_rects) {
ATRACE_CALL();
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), draw);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t* const s = get_surface(draw);
-
- if (CC_UNLIKELY(dp->traceGpuCompletion)) {
- EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
- if (sync != EGL_NO_SYNC_KHR) {
- FrameCompletionThread::queueSync(sync);
- }
- }
-
- if (CC_UNLIKELY(dp->finishOnSwap)) {
- uint32_t pixel;
- egl_context_t * const c = get_context( egl_tls_t::getContext() );
- if (c) {
- // glReadPixels() ensures that the frame is complete
- s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
- GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
- }
- }
-
- if (!sendSurfaceMetadata(s)) {
- native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
- return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
- }
-
- if (n_rects == 0) {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
- }
-
- std::vector<android_native_rect_t> androidRects((size_t)n_rects);
- for (int r = 0; r < n_rects; ++r) {
- int offset = r * 4;
- int x = rects[offset];
- int y = rects[offset + 1];
- int width = rects[offset + 2];
- int height = rects[offset + 3];
- android_native_rect_t androidRect;
- androidRect.left = x;
- androidRect.top = y + height;
- androidRect.right = x + width;
- androidRect.bottom = y;
- androidRects.push_back(androidRect);
- }
- native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), androidRects.size());
-
- if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
- return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- } else {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
- }
-}
-
-EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
-{
- return eglSwapBuffersWithDamageKHR(dpy, surface, NULL, 0);
-}
-
-EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface,
- NativePixmapType target)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
-}
-
-const char* eglQueryString(EGLDisplay dpy, EGLint name)
-{
- clearError();
-
- // Generate an error quietly when client extensions (as defined by
- // EGL_EXT_client_extensions) are queried. We do not want to rely on
- // validate_display to generate the error as validate_display would log
- // the error, which can be misleading.
- //
- // If we want to support EGL_EXT_client_extensions later, we can return
- // the client extension string here instead.
- if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
- return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)0);
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) NULL;
-
- switch (name) {
- case EGL_VENDOR:
- return dp->getVendorString();
- case EGL_VERSION:
- return dp->getVersionString();
- case EGL_EXTENSIONS:
- return dp->getExtensionString();
- case EGL_CLIENT_APIS:
- return dp->getClientApiString();
- default:
- break;
- }
- return setError(EGL_BAD_PARAMETER, (const char *)0);
-}
-
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) NULL;
-
- switch (name) {
- case EGL_VENDOR:
- return dp->disp.queryString.vendor;
- case EGL_VERSION:
- return dp->disp.queryString.version;
- case EGL_EXTENSIONS:
- return dp->disp.queryString.extensions;
- case EGL_CLIENT_APIS:
- return dp->disp.queryString.clientApi;
- default:
- break;
- }
- return setError(EGL_BAD_PARAMETER, (const char *)0);
-}
-
-// ----------------------------------------------------------------------------
-// EGL 1.1
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglSurfaceAttrib(
- EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t * const s = get_surface(surface);
-
- if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
- if (!s->getNativeWindow()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- }
- int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- if (attribute == EGL_TIMESTAMPS_ANDROID) {
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
- int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- if (s->setSmpte2086Attribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->setCta8613Attribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->cnx->egl.eglSurfaceAttrib) {
- return s->cnx->egl.eglSurfaceAttrib(
- dp->disp.dpy, s->surface, attribute, value);
- }
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglBindTexImage(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglBindTexImage) {
- return s->cnx->egl.eglBindTexImage(
- dp->disp.dpy, s->surface, buffer);
- }
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglReleaseTexImage(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglReleaseTexImage) {
- return s->cnx->egl.eglReleaseTexImage(
- dp->disp.dpy, s->surface, buffer);
- }
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglSwapInterval) {
- res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
- }
-
- return res;
+ return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects);
}
-
-// ----------------------------------------------------------------------------
-// EGL 1.2
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglWaitClient(void)
-{
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+ ATRACE_CALL();
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- EGLBoolean res;
- if (cnx->egl.eglWaitClient) {
- res = cnx->egl.eglWaitClient();
- } else {
- res = cnx->egl.eglWaitGL();
- }
- return res;
+ return cnx->platform.eglSwapBuffers(dpy, surface);
}
-EGLBoolean eglBindAPI(EGLenum api)
-{
+EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
clearError();
- if (egl_init_drivers() == EGL_FALSE) {
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
-
- // bind this API on all EGLs
- EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglBindAPI) {
- res = cnx->egl.eglBindAPI(api);
- }
- return res;
+ return cnx->platform.eglCopyBuffers(dpy, surface, target);
}
-EGLenum eglQueryAPI(void)
-{
+const char* eglQueryString(EGLDisplay dpy, EGLint name) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryString(dpy, name);
+}
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryStringImplementationANDROID(dpy, name);
+}
+
+EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value);
+}
+
+EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglBindTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglReleaseTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglSwapInterval(dpy, interval);
+}
+
+EGLBoolean eglWaitClient(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglWaitClient();
+}
+
+EGLBoolean eglBindAPI(EGLenum api) {
clearError();
if (egl_init_drivers() == EGL_FALSE) {
@@ -1620,824 +331,335 @@
}
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryAPI) {
- return cnx->egl.eglQueryAPI();
- }
-
- // or, it can only be OpenGL ES
- return EGL_OPENGL_ES_API;
+ return cnx->platform.eglBindAPI(api);
}
-EGLBoolean eglReleaseThread(void)
-{
+EGLenum eglQueryAPI(void) {
+ clearError();
+
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryAPI();
+}
+
+EGLBoolean eglReleaseThread(void) {
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglReleaseThread) {
- cnx->egl.eglReleaseThread();
- }
-
- // If there is context bound to the thread, release it
- egl_display_t::loseCurrent(get_context(getContext()));
-
- egl_tls_t::clearTLS();
- return EGL_TRUE;
+ return cnx->platform.eglReleaseThread();
}
-EGLSurface eglCreatePbufferFromClientBuffer(
- EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
- EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+ EGLConfig config, const EGLint* attrib_list) {
clearError();
- egl_connection_t* cnx = NULL;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (!dp) return EGL_FALSE;
- if (cnx->egl.eglCreatePbufferFromClientBuffer) {
- return cnx->egl.eglCreatePbufferFromClientBuffer(
- dp->disp.dpy, buftype, buffer, config, attrib_list);
- }
- return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config,
+ attrib_list);
}
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 3
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
- const EGLint *attrib_list)
-{
+EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglLockSurfaceKHR) {
- return s->cnx->egl.eglLockSurfaceKHR(
- dp->disp.dpy, s->surface, attrib_list);
- }
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list);
}
-EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglUnlockSurfaceKHR) {
- return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
- }
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglUnlockSurfaceKHR(dpy, surface);
}
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
- EGLClientBuffer buffer, const EGLint *attrib_list)
-{
+ EGLClientBuffer buffer, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_IMAGE_KHR;
-
- ContextRef _c(dp.get(), ctx);
- egl_context_t * const c = _c.get();
-
- // Temporary hack: eglImageCreateKHR should accept EGL_GL_COLORSPACE_LINEAR_KHR,
- // EGL_GL_COLORSPACE_SRGB_KHR and EGL_GL_COLORSPACE_DEFAULT_EXT if
- // EGL_EXT_image_gl_colorspace is supported, but some drivers don't like
- // the DEFAULT value and generate an error.
- std::vector<EGLint> strippedAttribList;
- for (const EGLint *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
- if (attr[0] == EGL_GL_COLORSPACE_KHR &&
- dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
- if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
- attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) {
- continue;
- }
- }
- strippedAttribList.push_back(attr[0]);
- strippedAttribList.push_back(attr[1]);
- }
- strippedAttribList.push_back(EGL_NONE);
-
- EGLImageKHR result = EGL_NO_IMAGE_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateImageKHR) {
- result = cnx->egl.eglCreateImageKHR(
- dp->disp.dpy,
- c ? c->context : EGL_NO_CONTEXT,
- target, buffer, strippedAttribList.data());
- }
- return result;
+ return cnx->platform.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list);
}
-EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
-{
+EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+ const EGLAttrib* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
- result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
- }
- return result;
+ return cnx->platform.eglCreateImage(dpy, ctx, target, buffer, attrib_list);
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroyImageKHR(dpy, img);
+}
+
+EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroyImage(dpy, img);
}
// ----------------------------------------------------------------------------
// EGL_EGLEXT_VERSION 5
// ----------------------------------------------------------------------------
-
-EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
-{
+EGLSyncKHR eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_SYNC_KHR;
-
- EGLSyncKHR result = EGL_NO_SYNC_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
- result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
- }
- return result;
+ return cnx->platform.eglCreateSync(dpy, type, attrib_list);
}
-EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
- result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
- }
- return result;
+ return cnx->platform.eglCreateSyncKHR(dpy, type, attrib_list);
+}
+
+EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroySync(dpy, sync);
+}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroySyncKHR(dpy, sync);
}
EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglSignalSyncKHR) {
- result = cnx->egl.eglSignalSyncKHR(
- dp->disp.dpy, sync, mode);
- }
- return result;
+ return cnx->platform.eglSignalSyncKHR(dpy, sync, mode);
}
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
- EGLint flags, EGLTimeKHR timeout)
-{
+EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLint result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
- result = cnx->egl.eglClientWaitSyncKHR(
- dp->disp.dpy, sync, flags, timeout);
- }
- return result;
+ return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
}
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
- EGLint attribute, EGLint *value)
-{
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
- result = cnx->egl.eglGetSyncAttribKHR(
- dp->disp.dpy, sync, attribute, value);
- }
- return result;
+ return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
}
-EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list)
-{
+EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_STREAM_KHR;
-
- EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
- result = cnx->egl.eglCreateStreamKHR(
- dp->disp.dpy, attrib_list);
- }
- return result;
+ return cnx->platform.eglGetSyncAttrib(dpy, sync, attribute, value);
}
-EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
- result = cnx->egl.eglDestroyStreamKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value);
}
-EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint value)
-{
+EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
- result = cnx->egl.eglStreamAttribKHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglCreateStreamKHR(dpy, attrib_list);
}
-EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint *value)
-{
+EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
- result = cnx->egl.eglQueryStreamKHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglDestroyStreamKHR(dpy, stream);
}
-EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLuint64KHR *value)
-{
+EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
- result = cnx->egl.eglQueryStreamu64KHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value);
}
-EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLTimeKHR *value)
-{
+EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
- result = cnx->egl.eglQueryStreamTimeKHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value);
}
-EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config,
- EGLStreamKHR stream, const EGLint *attrib_list)
-{
+EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLuint64KHR* value) {
clearError();
- egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_SURFACE;
-
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
- EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
- dp->disp.dpy, config, stream, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface,
- EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
- return s;
- }
- }
- return EGL_NO_SURFACE;
+ return cnx->platform.eglQueryStreamu64KHR(dpy, stream, attribute, value);
}
-EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
+EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLTimeKHR* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
- result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value);
}
-EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
+EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream,
+ const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
- result = cnx->egl.eglStreamConsumerAcquireKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list);
}
-EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
- result = cnx->egl.eglStreamConsumerReleaseKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream);
}
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
- EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
-
- EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
- result = cnx->egl.eglGetStreamFileDescriptorKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglStreamConsumerAcquireKHR(dpy, stream);
}
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
- EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
+EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_STREAM_KHR;
-
- EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
- result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
- dp->disp.dpy, file_descriptor);
- }
- return result;
+ return cnx->platform.eglStreamConsumerReleaseKHR(dpy, stream);
}
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 15
-// ----------------------------------------------------------------------------
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream);
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy,
+ EGLNativeFileDescriptorKHR file_descriptor) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor);
+}
EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
- EGLint result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglWaitSyncKHR) {
- result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags);
- }
- return result;
+ return cnx->platform.eglWaitSyncKHR(dpy, sync, flags);
}
-// ----------------------------------------------------------------------------
-// ANDROID extensions
-// ----------------------------------------------------------------------------
-
-EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) {
clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
- EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
- result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
- }
- return result;
+ return cnx->platform.eglWaitSync(dpy, sync, flags);
}
-EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLnsecsANDROID time)
-{
+EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return EGL_FALSE;
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
- }
-
- egl_surface_t const * const s = get_surface(surface);
- native_window_set_buffers_timestamp(s->getNativeWindow(), time);
-
- return EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync);
}
-EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
+EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) {
clearError();
- // 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)0);
- return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-#endif
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time);
}
-// ----------------------------------------------------------------------------
-// NVIDIA extensions
-// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNV()
-{
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) {
+ clearError();
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetNativeClientBufferANDROID(buffer);
+}
+
+EGLuint64NV eglGetSystemTimeFrequencyNV() {
clearError();
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
}
- EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
-
- if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
- return cnx->egl.eglGetSystemTimeFrequencyNV();
- }
-
- return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+ return cnx->platform.eglGetSystemTimeFrequencyNV();
}
-EGLuint64NV eglGetSystemTimeNV()
-{
+EGLuint64NV eglGetSystemTimeNV() {
clearError();
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
}
- EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
-
- if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
- return cnx->egl.eglGetSystemTimeNV();
- }
-
- return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+ return cnx->platform.eglGetSystemTimeNV();
}
-// ----------------------------------------------------------------------------
-// Partial update extension
-// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface,
- EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+ EGLint n_rects) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- setError(EGL_BAD_DISPLAY, EGL_FALSE);
- return EGL_FALSE;
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
- }
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglSetDamageRegionKHR) {
- return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- }
-
- return EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglSetDamageRegionKHR(dpy, surface, rects, n_rects);
}
-EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR *frameId) {
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- uint64_t nextFrameId = 0;
- int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
-
- if (ret != 0) {
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetNextFrameId: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
-
- *frameId = nextFrameId;
- return EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetNextFrameIdANDROID(dpy, surface, frameId);
}
-EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps,
+ const EGLint* names, EGLnsecsANDROID* values) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- nsecs_t* compositeDeadline = nullptr;
- nsecs_t* compositeInterval = nullptr;
- nsecs_t* compositeToPresentLatency = nullptr;
-
- for (int i = 0; i < numTimestamps; i++) {
- switch (names[i]) {
- case EGL_COMPOSITE_DEADLINE_ANDROID:
- compositeDeadline = &values[i];
- break;
- case EGL_COMPOSITE_INTERVAL_ANDROID:
- compositeInterval = &values[i];
- break;
- case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
- compositeToPresentLatency = &values[i];
- break;
- default:
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
- }
-
- int ret = native_window_get_compositor_timing(s->getNativeWindow(),
- compositeDeadline, compositeInterval, compositeToPresentLatency);
-
- switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetCompositorTiming: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCompositorTimingANDROID(dpy, surface, numTimestamps, names, values);
}
-EGLBoolean eglGetCompositorTimingSupportedANDROID(
- EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
+EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- ANativeWindow* window = s->getNativeWindow();
- if (!window) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- switch (name) {
- case EGL_COMPOSITE_DEADLINE_ANDROID:
- case EGL_COMPOSITE_INTERVAL_ANDROID:
- case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
- return EGL_TRUE;
- default:
- return EGL_FALSE;
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCompositorTimingSupportedANDROID(dpy, surface, name);
}
-EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
- EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId,
+ EGLint numTimestamps, const EGLint* timestamps,
+ EGLnsecsANDROID* values) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- nsecs_t* requestedPresentTime = nullptr;
- nsecs_t* acquireTime = nullptr;
- nsecs_t* latchTime = nullptr;
- nsecs_t* firstRefreshStartTime = nullptr;
- nsecs_t* gpuCompositionDoneTime = nullptr;
- nsecs_t* lastRefreshStartTime = nullptr;
- nsecs_t* displayPresentTime = nullptr;
- nsecs_t* dequeueReadyTime = nullptr;
- nsecs_t* releaseTime = nullptr;
-
- for (int i = 0; i < numTimestamps; i++) {
- switch (timestamps[i]) {
- case EGL_REQUESTED_PRESENT_TIME_ANDROID:
- requestedPresentTime = &values[i];
- break;
- case EGL_RENDERING_COMPLETE_TIME_ANDROID:
- acquireTime = &values[i];
- break;
- case EGL_COMPOSITION_LATCH_TIME_ANDROID:
- latchTime = &values[i];
- break;
- case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
- firstRefreshStartTime = &values[i];
- break;
- case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
- lastRefreshStartTime = &values[i];
- break;
- case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
- gpuCompositionDoneTime = &values[i];
- break;
- case EGL_DISPLAY_PRESENT_TIME_ANDROID:
- displayPresentTime = &values[i];
- break;
- case EGL_DEQUEUE_READY_TIME_ANDROID:
- dequeueReadyTime = &values[i];
- break;
- case EGL_READS_DONE_TIME_ANDROID:
- releaseTime = &values[i];
- break;
- default:
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
- }
-
- int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
- requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
- lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
- dequeueReadyTime, releaseTime);
-
- switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOENT:
- return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- case -EINVAL:
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetFrameTimestamps: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetFrameTimestampsANDROID(dpy, surface, frameId, numTimestamps,
+ timestamps, values);
}
-EGLBoolean eglGetFrameTimestampSupportedANDROID(
- EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
+EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLint timestamp) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- ANativeWindow* window = s->getNativeWindow();
- if (!window) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- switch (timestamp) {
- case EGL_COMPOSITE_DEADLINE_ANDROID:
- case EGL_COMPOSITE_INTERVAL_ANDROID:
- case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
- case EGL_REQUESTED_PRESENT_TIME_ANDROID:
- case EGL_RENDERING_COMPLETE_TIME_ANDROID:
- case EGL_COMPOSITION_LATCH_TIME_ANDROID:
- case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
- case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
- case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
- case EGL_DEQUEUE_READY_TIME_ANDROID:
- case EGL_READS_DONE_TIME_ANDROID:
- return EGL_TRUE;
- case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
- int value = 0;
- window->query(window,
- NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
- return value == 0 ? EGL_FALSE : EGL_TRUE;
- }
- default:
- return EGL_FALSE;
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetFrameTimestampSupportedANDROID(dpy, surface, timestamp);
}
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
new file mode 100644
index 0000000..00caff2
--- /dev/null
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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 defined(__ANDROID__)
+
+#include <cutils/properties.h>
+#include "Loader.h"
+#include "egl_angle_platform.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <EGL/Platform.h>
+#pragma GCC diagnostic pop
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <time.h>
+#include <log/log.h>
+
+namespace angle {
+
+static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
+static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
+
+static time_t startTime = time(nullptr);
+
+static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/,
+ const char* /*categoryName*/) {
+ // Returning ptr to 'g' (non-zero) to ALWAYS enable tracing initially.
+ // This ptr is what will be passed into "category_group_enabled" of addTraceEvent
+ static const unsigned char traceEnabled = 'g';
+ return &traceEnabled;
+}
+
+static double monotonicallyIncreasingTime(PlatformMethods* /*platform*/) {
+ return difftime(time(nullptr), startTime);
+}
+
+static void logError(PlatformMethods* /*platform*/, const char* errorMessage) {
+ ALOGE("ANGLE Error:%s", errorMessage);
+}
+
+static void logWarning(PlatformMethods* /*platform*/, const char* warningMessage) {
+ ALOGW("ANGLE Warn:%s", warningMessage);
+}
+
+static void logInfo(PlatformMethods* /*platform*/, const char* infoMessage) {
+ ALOGD("ANGLE Info:%s", infoMessage);
+}
+
+static TraceEventHandle addTraceEvent(
+ PlatformMethods* /**platform*/, char phase, const unsigned char* /*category_group_enabled*/,
+ const char* name, unsigned long long /*id*/, double /*timestamp*/, int /*num_args*/,
+ const char** /*arg_names*/, const unsigned char* /*arg_types*/,
+ const unsigned long long* /*arg_values*/, unsigned char /*flags*/) {
+ switch (phase) {
+ case 'B': {
+ ATRACE_BEGIN(name);
+ break;
+ }
+ case 'E': {
+ ATRACE_END();
+ break;
+ }
+ case 'I': {
+ ATRACE_NAME(name);
+ break;
+ }
+ default:
+ // Could handle other event types here
+ break;
+ }
+ // Return any non-zero handle to avoid assert in ANGLE
+ TraceEventHandle result = 1.0;
+ return result;
+}
+
+static void assignAnglePlatformMethods(PlatformMethods* platformMethods) {
+ platformMethods->addTraceEvent = addTraceEvent;
+ platformMethods->getTraceCategoryEnabledFlag = getTraceCategoryEnabledFlag;
+ platformMethods->monotonicallyIncreasingTime = monotonicallyIncreasingTime;
+ platformMethods->logError = logError;
+ platformMethods->logWarning = logWarning;
+ platformMethods->logInfo = logInfo;
+}
+
+// Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace
+bool initializeAnglePlatform(EGLDisplay dpy) {
+ // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = ns,
+ };
+ void* so = android_dlopen_ext("libGLESv2_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ angleGetDisplayPlatform =
+ reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
+
+ if (!angleGetDisplayPlatform) {
+ ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!");
+ return false;
+ }
+
+ angleResetDisplayPlatform =
+ reinterpret_cast<ResetDisplayPlatformFunc>(
+ eglGetProcAddress("ANGLEResetDisplayPlatform"));
+
+ PlatformMethods* platformMethods = nullptr;
+ if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames,
+ g_NumPlatformMethods, nullptr,
+ &platformMethods))) {
+ ALOGE("ANGLEGetDisplayPlatform call failed!");
+ return false;
+ }
+ if (platformMethods) {
+ assignAnglePlatformMethods(platformMethods);
+ } else {
+ ALOGE("In initializeAnglePlatform() platformMethods struct ptr is NULL. Not assigning "
+ "tracing function ptrs!");
+ }
+ return true;
+}
+
+void resetAnglePlatform(EGLDisplay dpy) {
+ if (angleResetDisplayPlatform) {
+ angleResetDisplayPlatform(dpy);
+ }
+}
+
+}; // namespace angle
+
+#endif // __ANDROID__
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/opengl/libs/EGL/egl_angle_platform.h
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to opengl/libs/EGL/egl_angle_platform.h
index e6ac6bf..6c24aa5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.h
@@ -14,14 +14,20 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
-namespace android {
-namespace mock {
+#if defined(__ANDROID__)
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+#include "egldefs.h"
-} // namespace mock
-} // namespace android
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "egl_trace.h"
+
+namespace angle {
+
+bool initializeAnglePlatform(EGLDisplay dpy);
+void resetAnglePlatform(EGLDisplay dpy);
+
+}; // namespace angle
+
+#endif // __ANDROID__
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index ec548f3..bcf4961 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -95,7 +95,7 @@
reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>(
cnx->egl.eglGetProcAddress(
"eglSetBlobCacheFuncsANDROID"));
- if (eglSetBlobCacheFuncsANDROID == NULL) {
+ if (eglSetBlobCacheFuncsANDROID == nullptr) {
ALOGE("EGL_ANDROID_blob_cache advertised, "
"but unable to get eglSetBlobCacheFuncsANDROID");
return;
@@ -119,7 +119,7 @@
if (mBlobCache) {
mBlobCache->writeToFile();
}
- mBlobCache = NULL;
+ mBlobCache = nullptr;
}
void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 2aec249..c100db7 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -21,14 +21,19 @@
#include "../egl_impl.h"
+#include <EGL/eglext_angle.h>
#include <private/EGL/display.h>
+#include <cutils/properties.h>
+#include "Loader.h"
+#include "egl_angle_platform.h"
#include "egl_cache.h"
#include "egl_object.h"
#include "egl_tls.h"
-#include "egl_trace.h"
-#include "Loader.h"
-#include <cutils/properties.h>
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
@@ -41,7 +46,8 @@
// ----------------------------------------------------------------------------
static char const * const sVendorString = "Android";
-static char const * const sVersionString = "1.4 Android META-EGL";
+static char const* const sVersionString14 = "1.4 Android META-EGL";
+static char const* const sVersionString15 = "1.5 Android META-EGL";
static char const * const sClientApiString = "OpenGL_ES";
extern char const * const gBuiltinExtensionString;
@@ -114,15 +120,91 @@
return false;
}
-EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
+EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
+ const EGLAttrib* attrib_list) {
if (uintptr_t(disp) >= NUM_DISPLAYS)
- return NULL;
+ return nullptr;
- return sDisplay[uintptr_t(disp)].getDisplay(disp);
+ return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
}
-EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
+static bool addAnglePlatformAttributes(egl_connection_t* const cnx,
+ std::vector<EGLAttrib>& attrs) {
+ intptr_t vendorEGL = (intptr_t)cnx->vendorEGL;
+ attrs.reserve(4 * 2);
+
+ attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
+ attrs.push_back(cnx->angleBackend);
+
+ switch (cnx->angleBackend) {
+ case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
+ ALOGV("%s: Requesting Vulkan ANGLE back-end", __FUNCTION__);
+ 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));
+ break;
+ case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
+ ALOGV("%s: Requesting Default ANGLE back-end", __FUNCTION__);
+ break;
+ case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
+ ALOGV("%s: Requesting OpenGL ES ANGLE back-end", __FUNCTION__);
+ // NOTE: This is only valid if the backend is OpenGL
+ attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE);
+ attrs.push_back(vendorEGL);
+ break;
+ default:
+ ALOGV("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
+ break;
+ }
+ attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
+ attrs.push_back(EGL_FALSE);
+
+ return true;
+}
+
+static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
+ const EGLAttrib* attrib_list, EGLint* error) {
+ EGLDisplay dpy = EGL_NO_DISPLAY;
+ *error = EGL_NONE;
+
+ if (cnx->egl.eglGetPlatformDisplay) {
+ std::vector<EGLAttrib> attrs;
+ if (attrib_list) {
+ for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
+ attrs.push_back(attr[0]);
+ attrs.push_back(attr[1]);
+ }
+ }
+
+ if (!addAnglePlatformAttributes(cnx, attrs)) {
+ ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display);
+ *error = EGL_BAD_PARAMETER;
+ return EGL_NO_DISPLAY;
+ }
+ attrs.push_back(EGL_NONE);
+
+ dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
+ reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY),
+ attrs.data());
+ if (dpy == EGL_NO_DISPLAY) {
+ ALOGE("eglGetPlatformDisplay failed!");
+ } else {
+ if (!angle::initializeAnglePlatform(dpy)) {
+ ALOGE("initializeAnglePlatform failed!");
+ }
+ }
+ } else {
+ ALOGE("eglGetDisplay(%p) failed: Unable to look up eglGetPlatformDisplay from ANGLE",
+ display);
+ }
+
+ return dpy;
+}
+
+EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list) {
std::lock_guard<std::mutex> _l(lock);
ATRACE_CALL();
@@ -131,11 +213,37 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {
- EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
+ EGLDisplay dpy = EGL_NO_DISPLAY;
+
+ if (cnx->useAngle) {
+ EGLint error;
+ dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
+ if (error != EGL_NONE) {
+ return setError(error, dpy);
+ }
+ }
+ if (dpy == EGL_NO_DISPLAY) {
+ // NOTE: eglGetPlatformDisplay with a empty attribute list
+ // behaves the same as eglGetDisplay
+ if (cnx->egl.eglGetPlatformDisplay) {
+ dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display,
+ attrib_list);
+ }
+
+ // It is possible that eglGetPlatformDisplay does not have a
+ // working implementation for Android platform; in that case,
+ // one last fallback to eglGetDisplay
+ if(dpy == EGL_NO_DISPLAY) {
+ if (attrib_list) {
+ ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
+ }
+ dpy = cnx->egl.eglGetDisplay(display);
+ }
+ }
+
disp.dpy = dpy;
if (dpy == EGL_NO_DISPLAY) {
- loader.close(cnx->dso);
- cnx->dso = NULL;
+ loader.close(cnx);
}
}
@@ -148,13 +256,20 @@
std::unique_lock<std::mutex> _l(refLock);
refs++;
if (refs > 1) {
- if (major != NULL)
- *major = VERSION_MAJOR;
- if (minor != NULL)
- *minor = VERSION_MINOR;
+ // We don't know what to report until we know what the
+ // driver supports. Make sure we are initialized before
+ // returning the version info.
while(!eglIsInitialized) {
refCond.wait(_l);
}
+ egl_connection_t* const cnx = &gEGLImpl;
+
+ // 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
+ // version 1.4. May need to check that revision is valid
+ // before using cnx->major & cnx->minor
+ if (major != nullptr) *major = cnx->major;
+ if (minor != nullptr) *minor = cnx->minor;
return EGL_TRUE;
}
while(eglIsInitialized) {
@@ -201,7 +316,52 @@
// the query strings are per-display
mVendorString = sVendorString;
- mVersionString = sVersionString;
+ mVersionString.clear();
+ cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0);
+ if ((cnx->major == 1) && (cnx->minor == 5)) {
+ mVersionString = sVersionString15;
+ cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
+ } else if ((cnx->major == 1) && (cnx->minor == 4)) {
+ mVersionString = sVersionString14;
+ // Extensions needed for an EGL 1.4 implementation to be
+ // able to support EGL 1.5 functionality
+ std::vector<const char*> egl15extensions = {
+ "EGL_EXT_client_extensions",
+ // "EGL_EXT_platform_base", // implemented by EGL runtime
+ "EGL_KHR_image_base",
+ "EGL_KHR_fence_sync",
+ "EGL_KHR_wait_sync",
+ "EGL_KHR_create_context",
+ "EGL_EXT_create_context_robustness",
+ "EGL_KHR_gl_colorspace",
+ "EGL_ANDROID_native_fence_sync",
+ };
+ bool extensionsFound = true;
+ for (const auto& name : egl15extensions) {
+ extensionsFound &= findExtension(disp.queryString.extensions, name);
+ ALOGV("Extension %s: %s", name,
+ findExtension(disp.queryString.extensions, name) ? "Found" : "Missing");
+ }
+ // NOTE: From the spec:
+ // Creation of fence sync objects requires support from the bound
+ // client API, and will not succeed unless the client API satisfies:
+ // client API is OpenGL ES, and either the OpenGL ES version is 3.0
+ // or greater, or the GL_OES_EGL_sync extension is supported.
+ // We don't have a way to check the GL_EXTENSIONS string at this
+ // point in the code, assume that GL_OES_EGL_sync is supported
+ // because EGL_KHR_fence_sync is supported (as verified above).
+ if (extensionsFound) {
+ // Have everything needed to emulate EGL 1.5 so report EGL 1.5
+ // to the application.
+ mVersionString = sVersionString15;
+ cnx->major = 1;
+ cnx->minor = 5;
+ }
+ }
+ if (mVersionString.empty()) {
+ ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor);
+ mVersionString = sVersionString14;
+ }
mClientApiString = sClientApiString;
mExtensionString = gBuiltinExtensionString;
@@ -218,7 +378,8 @@
if (wideColorBoardConfig && hasColorSpaceSupport) {
mExtensionString.append(
"EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear "
- "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 ");
+ "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "
+ "EGL_EXT_gl_colorspace_display_p3_passthrough ");
}
bool hasHdrBoardConfig =
@@ -240,12 +401,6 @@
if (len) {
// NOTE: we could avoid the copy if we had strnstr.
const std::string ext(start, len);
- // Temporary hack: Adreno 530 driver exposes this extension under the draft
- // KHR name, but during Khronos review it was decided to demote it to EXT.
- if (ext == "EGL_EXT_image_gl_colorspace" &&
- findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
- mExtensionString.append("EGL_EXT_image_gl_colorspace ");
- }
if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
mExtensionString.append(ext + " ");
}
@@ -268,10 +423,12 @@
traceGpuCompletion = true;
}
- if (major != NULL)
- *major = VERSION_MAJOR;
- if (minor != NULL)
- *minor = VERSION_MINOR;
+ // 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
+ // version 1.4. May need to check that revision is valid
+ // before using cnx->major & cnx->minor
+ if (major != nullptr) *major = cnx->major;
+ if (minor != nullptr) *minor = cnx->minor;
}
{ // scope for refLock
@@ -311,6 +468,10 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
+ // If we're using ANGLE reset any custom DisplayPlatform
+ if (cnx->useAngle) {
+ angle::resetAnglePlatform(disp.dpy);
+ }
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
@@ -361,8 +522,8 @@
// by construction, these are either 0 or valid (possibly terminated)
// it should be impossible for these to be invalid
ContextRef _cur_c(cur_c);
- SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
- SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+ SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr);
+ SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr);
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
@@ -387,8 +548,8 @@
// by construction, these are either 0 or valid (possibly terminated)
// it should be impossible for these to be invalid
ContextRef _cur_c(cur_c);
- SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
- SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+ SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr);
+ SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr);
{ // scope for the lock
std::lock_guard<std::mutex> _l(lock);
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 79a9f08..36856b7 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -49,6 +49,7 @@
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static egl_display_t sDisplay[NUM_DISPLAYS];
EGLDisplay getDisplay(EGLNativeDisplayType display);
+ EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
void loseCurrentImpl(egl_context_t * cur_c);
public:
@@ -72,7 +73,7 @@
bool getObject(egl_object_t* object) const;
static egl_display_t* get(EGLDisplay dpy);
- static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
+ static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
EGLSurface draw, EGLSurface read, EGLContext ctx,
@@ -160,7 +161,7 @@
const egl_display_t* get() const { return mDpy; }
egl_display_t* get() { return mDpy; }
- operator bool() const { return mDpy != NULL; }
+ operator bool() const { return mDpy != nullptr; }
private:
egl_display_t* mDpy;
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index b587a16..2921d51 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -44,6 +44,18 @@
/* EGL 1.4 */
+/* EGL 1.5 */
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSyncKHR, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSync, EGLint, EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+
/* EGL_EGLEXT_VERSION 3 */
EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint *)
@@ -75,6 +87,10 @@
EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR)
EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint)
+/* EGL_EGLEXT_VERSION 20170627 */
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+
/* ANDROID extensions */
EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
new file mode 100644
index 0000000..f936ac0
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -0,0 +1,436 @@
+/*
+ ** Copyright 2018, 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 "egl_layers.h"
+
+#include <EGL/egl.h>
+#include <android-base/file.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>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+#include <sys/prctl.h>
+
+namespace android {
+
+// GLES Layers
+//
+// - Layer discovery -
+// 1. Check for debug layer list from GraphicsEnv
+// 2. If none enabled, check system properties
+//
+// - Layer initializing -
+// TODO: ADD DETAIL ABOUT NEW INTERFACES
+// - InitializeLayer (provided by layer, called by loader)
+// - GetLayerProcAddress (provided by layer, called by loader)
+// - getNextLayerProcAddress (provided by loader, called by layer)
+//
+// 1. Walk through defs for egl and each gl version
+// 2. Call GetLayerProcAddress passing the name and the target hook entry point
+// - This tells the layer the next point in the chain it should call
+// 3. Replace the hook with the layer's entry point
+// - All entryoints will be present, anything unsupported by the driver will
+// have gl_unimplemented
+//
+// - Extension layering -
+// Not all functions are known to Android, so libEGL handles extensions.
+// They are looked up by applications using eglGetProcAddress
+// Layers can look them up with getNextLayerProcAddress
+
+const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) +
+ sizeof(gl_hooks_t) / sizeof(char*);
+
+typedef struct FunctionTable {
+ EGLFuncPointer x[kFuncCount];
+ EGLFuncPointer& operator[](int i) { return x[i]; }
+} FunctionTable;
+
+// TODO: Move these to class
+std::unordered_map<std::string, int> func_indices;
+// func_indices.reserve(kFuncCount);
+
+std::unordered_map<int, std::string> func_names;
+// func_names.reserve(kFuncCount);
+
+std::vector<FunctionTable> layer_functions;
+
+const void* getNextLayerProcAddress(void* layer_id, const char* name) {
+ // Use layer_id to find funcs for layer below current
+ // This is the same key provided in InitializeLayer
+ auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
+ EGLFuncPointer val;
+
+ if (func_indices.find(name) == func_indices.end()) {
+ // No entry for this function - it is an extension
+ // call down the GPA chain directly to the impl
+ ALOGV("getNextLayerProcAddress servicing %s", name);
+
+ // Look up which GPA we should use
+ int gpaIndex = func_indices["eglGetProcAddress"];
+ EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+
+ ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name);
+
+ // Call it for the requested function
+ typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
+ PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
+
+ val = reinterpret_cast<EGLFuncPointer>(next(name));
+ ALOGV("Got back %llu for %s", (unsigned long long)val, name);
+
+ // We should store it now, but to do that, we need to move func_idx to the class so we can
+ // increment it separately
+ // TODO: Move func_idx to class and store the result of GPA
+ return reinterpret_cast<void*>(val);
+ }
+
+ // int index = func_indices[name];
+ // val = (*next_layer_funcs)[index];
+ // return reinterpret_cast<void*>(val);
+ return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]);
+}
+
+void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
+ int& func_idx) {
+ while (*entries) {
+ const char* name = *entries;
+
+ // Some names overlap, only fill with initial entry
+ // This does mean that some indices will not be used
+ if (func_indices.find(name) == func_indices.end()) {
+ func_names[func_idx] = name;
+ func_indices[name] = func_idx;
+ }
+
+ // Populate layer_functions once with initial value
+ // These values will arrive in priority order, starting with platform entries
+ if (functions[func_idx] == nullptr) {
+ functions[func_idx] = *curr;
+ }
+
+ entries++;
+ curr++;
+ func_idx++;
+ }
+}
+
+LayerLoader& LayerLoader::getInstance() {
+ // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl
+ static LayerLoader layer_loader;
+
+ if (!layer_loader.layers_loaded_) layer_loader.LoadLayers();
+
+ return layer_loader;
+}
+
+const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
+
+std::string LayerLoader::GetDebugLayers() {
+ // Layers can be specified at the Java level in GraphicsEnvironemnt
+ // gpu_debug_layers_gles = layer1:layer2:layerN
+ std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES();
+
+ 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;
+ }
+
+ return debug_layers;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name,
+ EGLFuncPointer next) {
+ // Walk through our list of LayerSetup functions (they will already be in reverse order) to
+ // build up a call chain from the driver
+
+ EGLFuncPointer layer_entry = next;
+
+ layer_entry = layer_setup(name, layer_entry);
+
+ if (next != layer_entry) {
+ ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s",
+ (unsigned long long)next, (unsigned long long)layer_entry, name);
+ }
+
+ return layer_entry;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) {
+ if (!layers_loaded_ || layer_setup_.empty()) return next;
+
+ ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name,
+ (unsigned long long)next, current_layer_);
+
+ EGLFuncPointer val = next;
+
+ // Only ApplyLayers for layers that have been setup, not all layers yet
+ for (unsigned i = 0; i < current_layer_; i++) {
+ ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name,
+ (unsigned long long)next);
+ val = ApplyLayer(layer_setup_[i], name, val);
+ }
+
+ ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name);
+
+ return val;
+}
+
+void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+ char const* const* entries) {
+ while (*entries) {
+ char const* name = *entries;
+
+ EGLFuncPointer prev = *curr;
+
+ // Pass the existing entry point into the layer, replace the call with return value
+ *curr = ApplyLayer(layer_setup, name, *curr);
+
+ if (prev != *curr) {
+ ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s",
+ (unsigned long long)prev, (unsigned long long)*curr, name);
+ } else {
+ ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not "
+ "intercept",
+ (unsigned long long)prev, name);
+ }
+
+ curr++;
+ entries++;
+ }
+}
+
+void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+ char const* const* entries) {
+ while (*entries) {
+ char const* name = *entries;
+ EGLFuncPointer prev = *curr;
+
+ // Only apply layers to driver entries if not handled by the platform
+ if (FindPlatformImplAddr(name) == nullptr) {
+ // Pass the existing entry point into the layer, replace the call with return value
+ *curr = ApplyLayer(layer_setup, name, *prev);
+
+ if (prev != *curr) {
+ ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s",
+ (unsigned long long)prev, (unsigned long long)*curr, name);
+ }
+
+ } else {
+ ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name);
+ }
+
+ curr++;
+ entries++;
+ }
+}
+
+bool LayerLoader::Initialized() {
+ return initialized_;
+}
+
+void LayerLoader::InitLayers(egl_connection_t* cnx) {
+ if (!layers_loaded_) return;
+
+ if (initialized_) return;
+
+ if (layer_setup_.empty()) {
+ initialized_ = true;
+ return;
+ }
+
+ // Include the driver in layer_functions
+ layer_functions.resize(layer_setup_.size() + 1);
+
+ // Walk through the initial lists and create layer_functions[0]
+ int func_idx = 0;
+ char const* const* entries;
+ EGLFuncPointer* curr;
+
+ entries = platform_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+ SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+ ALOGV("InitLayers: func_idx after platform_names: %i", func_idx);
+
+ entries = egl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+ SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+ ALOGV("InitLayers: func_idx after egl_names: %i", func_idx);
+
+ entries = gl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+ SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+ ALOGV("InitLayers: func_idx after gl_names: %i", func_idx);
+
+ // Walk through each layer's entry points per API, starting just above the driver
+ for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) {
+ // Init the layer with a key that points to layer just below it
+ layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]),
+ reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>(
+ getNextLayerProcAddress));
+
+ // Check functions implemented by the platform
+ func_idx = 0;
+ entries = platform_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+ LayerPlatformEntries(layer_setup_[current_layer_], curr, entries);
+
+ // Populate next function table after layers have been applied
+ SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+ // EGL
+ entries = egl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+ LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+ // Populate next function table after layers have been applied
+ SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+ // GLES 2+
+ // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x
+ // If it were added in the future, a different layer initialization model would be needed,
+ // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase
+ // initialization.
+ entries = gl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+ LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+ // Populate next function table after layers have been applied
+ SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+ }
+
+ // We only want to apply layers once
+ initialized_ = true;
+}
+
+void LayerLoader::LoadLayers() {
+ std::string debug_layers = GetDebugLayers();
+
+ // If no layers are specified, we're done
+ if (debug_layers.empty()) return;
+
+ // Only enable the system search path for non-user builds
+ std::string system_path;
+ if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ system_path = kSystemLayerLibraryDir;
+ }
+
+ ALOGI("Debug layer list: %s", debug_layers.c_str());
+ std::vector<std::string> layers = android::base::Split(debug_layers, ":");
+
+ // Load the layers in reverse order so we start with the driver's entrypoint and work our way up
+ for (int32_t i = layers.size() - 1; i >= 0; i--) {
+ // Check each layer path for the layer
+ std::vector<std::string> paths =
+ android::base::Split(android::GraphicsEnv::getInstance().getLayerPaths().c_str(),
+ ":");
+
+ if (!system_path.empty()) {
+ // Prepend the system paths so they override other layers
+ auto it = paths.begin();
+ paths.insert(it, system_path);
+ }
+
+ bool layer_found = false;
+ for (uint32_t j = 0; j < paths.size() && !layer_found; j++) {
+ std::string layer;
+
+ ALOGI("Searching %s for GLES layers", paths[j].c_str());
+
+ // Realpath will return null for non-existent files
+ android::base::Realpath(paths[j] + "/" + layers[i], &layer);
+
+ if (!layer.empty()) {
+ layer_found = true;
+ ALOGI("GLES layer found: %s", layer.c_str());
+
+ // Load the layer
+ //
+ // TODO: This code is common with Vulkan loader, refactor
+ //
+ // Libraries in the system layer library dir can't be loaded into
+ // the application namespace. That causes compatibility problems, since
+ // any symbol dependencies will be resolved by system libraries. They
+ // can't safely use libc++_shared, for example. Which is one reason
+ // (among several) we only allow them in non-user builds.
+ void* handle = nullptr;
+ auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
+ if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
+ bool native_bridge = false;
+ char* error_message = nullptr;
+ handle = OpenNativeLibraryInNamespace(
+ app_namespace, layer.c_str(), &native_bridge, &error_message);
+ if (!handle) {
+ ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
+ error_message);
+ android::NativeLoaderFreeErrorMessage(error_message);
+ return;
+ }
+
+ } else {
+ handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
+ }
+
+ if (handle) {
+ ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle,
+ layers[i].c_str());
+ } else {
+ // If the layer is found but can't be loaded, try setenforce 0
+ const char* dlsym_error = dlerror();
+ ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error);
+ return;
+ }
+
+ // Find the layer's Initialize function
+ std::string init_func = "InitializeLayer";
+ ALOGV("Looking for entrypoint %s", init_func.c_str());
+
+ layer_init_func LayerInit =
+ reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str()));
+ if (LayerInit) {
+ ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
+ layer_init_.push_back(LayerInit);
+ } else {
+ ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str());
+ return;
+ }
+
+ // Find the layer's setup function
+ std::string setup_func = "GetLayerProcAddress";
+ ALOGV("Looking for entrypoint %s", setup_func.c_str());
+
+ layer_setup_func LayerSetup =
+ reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str()));
+ if (LayerSetup) {
+ ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
+ layer_setup_.push_back(LayerSetup);
+ } else {
+ ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str());
+ return;
+ }
+ }
+ }
+ }
+ // Track this so we only attempt to load these once
+ layers_loaded_ = true;
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
new file mode 100644
index 0000000..e401b44
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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_EGL_LAYERS_H
+#define ANDROID_EGL_LAYERS_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <EGL/egldefs.h>
+
+#include "egl_platform_entries.h"
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
+
+namespace android {
+
+class LayerLoader {
+public:
+ static LayerLoader& getInstance();
+ ~LayerLoader(){};
+
+ typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+ typedef EGLFuncPointer (*layer_init_func)(
+ const void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address);
+ typedef EGLFuncPointer (*layer_setup_func)(const char* name, EGLFuncPointer next);
+
+ void LoadLayers();
+ void InitLayers(egl_connection_t*);
+ void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ bool Initialized();
+ std::string GetDebugLayers();
+
+ EGLFuncPointer GetGpaNext(unsigned i);
+ EGLFuncPointer ApplyLayer(layer_setup_func layer_setup, const char* name, EGLFuncPointer next);
+ EGLFuncPointer ApplyLayers(const char* name, EGLFuncPointer next);
+
+ std::vector<layer_init_func> layer_init_;
+ std::vector<layer_setup_func> layer_setup_;
+
+private:
+ LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){};
+ bool layers_loaded_;
+ bool initialized_;
+ unsigned current_layer_;
+};
+
+}; // namespace android
+
+#endif // ANDROID_EGL_LAYERS_H
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index f879254..ff4fe2d 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -81,14 +81,14 @@
}
egl_surface_t::~egl_surface_t() {
- if (win != NULL) {
+ if (win != nullptr) {
disconnect();
win->decStrong(this);
}
}
void egl_surface_t::disconnect() {
- if (win != NULL && connected) {
+ if (win != nullptr && connected) {
native_window_set_buffers_format(win, 0);
if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
ALOGW("EGLNativeWindowType %p disconnect failed", win);
@@ -281,12 +281,12 @@
egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
egl_connection_t const* cnx, int version) :
egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
- config(config), read(0), draw(0), cnx(cnx), version(version) {
+ config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) {
}
void egl_context_t::onLooseCurrent() {
- read = NULL;
- draw = NULL;
+ read = nullptr;
+ draw = nullptr;
}
void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 4e1de5c..fb2bdf4 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -67,7 +67,7 @@
public:
~LocalRef();
explicit LocalRef(egl_object_t* rhs);
- explicit LocalRef(egl_display_t const* display, T o) : ref(0) {
+ explicit LocalRef(egl_display_t const* display, T o) : ref(nullptr) {
egl_object_t* native = reinterpret_cast<N*>(o);
if (o && egl_object_t::get(display, native)) {
ref = native;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
new file mode 100644
index 0000000..872631f
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -0,0 +1,2715 @@
+/*
+ ** Copyright 2007, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "egl_platform_entries.h"
+
+#include <ctype.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/gralloc1.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 "../egl_impl.h"
+
+#include "egl_display.h"
+#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_tls.h"
+#include "egl_trace.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+using nsecs_t = int64_t;
+
+struct extention_map_t {
+ const char* name;
+ __eglMustCastToProperFunctionPointerType address;
+};
+
+/*
+ * This is the list of EGL extensions exposed to applications.
+ *
+ * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
+ * wrapper and are always available.
+ *
+ * The rest (gExtensionString) depend on support in the EGL driver, and are
+ * only available if the driver supports them. However, some of these must be
+ * supported because they are used by the Android system itself; these are
+ * listed as mandatory below and are required by the CDD. The system *assumes*
+ * the mandatory extensions are present and may not function properly if some
+ * are missing.
+ *
+ * NOTE: Both strings MUST have a single space as the last character.
+ */
+
+extern char const * const gBuiltinExtensionString;
+extern char const * const gExtensionString;
+
+// clang-format off
+// Extensions implemented by the EGL wrapper.
+char const * const gBuiltinExtensionString =
+ "EGL_KHR_get_all_proc_addresses "
+ "EGL_ANDROID_presentation_time "
+ "EGL_KHR_swap_buffers_with_damage "
+ "EGL_ANDROID_get_native_client_buffer "
+ "EGL_ANDROID_front_buffer_auto_refresh "
+ "EGL_ANDROID_get_frame_timestamps "
+ "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_EXT_surface_CTA861_3_metadata "
+ ;
+
+// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+char const * const gExtensionString =
+ "EGL_KHR_image " // mandatory
+ "EGL_KHR_image_base " // mandatory
+ "EGL_EXT_image_gl_colorspace "
+ "EGL_KHR_image_pixmap "
+ "EGL_KHR_lock_surface "
+ "EGL_KHR_gl_colorspace "
+ "EGL_KHR_gl_texture_2D_image "
+ "EGL_KHR_gl_texture_3D_image "
+ "EGL_KHR_gl_texture_cubemap_image "
+ "EGL_KHR_gl_renderbuffer_image "
+ "EGL_KHR_reusable_sync "
+ "EGL_KHR_fence_sync "
+ "EGL_KHR_create_context "
+ "EGL_KHR_config_attribs "
+ "EGL_KHR_surfaceless_context "
+ "EGL_KHR_stream "
+ "EGL_KHR_stream_fifo "
+ "EGL_KHR_stream_producer_eglsurface "
+ "EGL_KHR_stream_consumer_gltexture "
+ "EGL_KHR_stream_cross_process_fd "
+ "EGL_EXT_create_context_robustness "
+ "EGL_NV_system_time "
+ "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_KHR_wait_sync " // strongly recommended
+ "EGL_ANDROID_recordable " // mandatory
+ "EGL_KHR_partial_update " // strongly recommended
+ "EGL_EXT_pixel_format_float "
+ "EGL_EXT_buffer_age " // strongly recommended with partial_update
+ "EGL_KHR_create_context_no_error "
+ "EGL_KHR_mutable_render_buffer "
+ "EGL_EXT_yuv_surface "
+ "EGL_EXT_protected_content "
+ "EGL_IMG_context_priority "
+ "EGL_KHR_no_config_context "
+ ;
+
+char const * const gClientExtensionString =
+ "EGL_EXT_client_extensions "
+ "EGL_KHR_platform_android "
+ "EGL_ANGLE_platform_angle";
+// clang-format on
+
+// extensions not exposed to applications but used by the ANDROID system
+// "EGL_ANDROID_blob_cache " // strongly recommended
+// "EGL_IMG_hibernate_process " // optional
+// "EGL_ANDROID_native_fence_sync " // strongly recommended
+// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
+
+/*
+ * EGL Extensions entry-points exposed to 3rd party applications
+ * (keep in sync with gExtensionString above)
+ *
+ */
+static const extention_map_t sExtensionMap[] = {
+ // EGL_KHR_lock_surface
+ { "eglLockSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+ { "eglUnlockSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+
+ // EGL_KHR_image, EGL_KHR_image_base
+ { "eglCreateImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+
+ // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
+ { "eglCreateSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+ { "eglDestroySyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+ { "eglClientWaitSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+ { "eglSignalSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+ { "eglGetSyncAttribKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+
+ // EGL_NV_system_time
+ { "eglGetSystemTimeFrequencyNV",
+ (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+ { "eglGetSystemTimeNV",
+ (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+
+ // EGL_KHR_wait_sync
+ { "eglWaitSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+
+ // EGL_ANDROID_presentation_time
+ { "eglPresentationTimeANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+
+ // EGL_KHR_swap_buffers_with_damage
+ { "eglSwapBuffersWithDamageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+
+ // EGL_ANDROID_get_native_client_buffer
+ { "eglGetNativeClientBufferANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+
+ // EGL_KHR_partial_update
+ { "eglSetDamageRegionKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+
+ { "eglCreateStreamKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+ { "eglDestroyStreamKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+ { "eglStreamAttribKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+ { "eglQueryStreamKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+ { "eglQueryStreamu64KHR",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+ { "eglQueryStreamTimeKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+ { "eglCreateStreamProducerSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+ { "eglStreamConsumerGLTextureExternalKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+ { "eglStreamConsumerAcquireKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+ { "eglStreamConsumerReleaseKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+ { "eglGetStreamFileDescriptorKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+ { "eglCreateStreamFromFileDescriptorKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+ // EGL_ANDROID_get_frame_timestamps
+ { "eglGetNextFrameIdANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+ { "eglGetCompositorTimingANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+ { "eglGetCompositorTimingSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+ { "eglGetFrameTimestampsANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglGetFrameTimestampSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+
+ // EGL_ANDROID_native_fence_sync
+ { "eglDupNativeFenceFDANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+};
+
+/*
+ * These extensions entry-points should not be exposed to applications.
+ * They're used internally by the Android EGL layer.
+ */
+#define FILTER_EXTENSIONS(procname) \
+ (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
+ !strcmp((procname), "eglHibernateProcessIMG") || \
+ !strcmp((procname), "eglAwakenProcessIMG"))
+
+// accesses protected by sExtensionMapMutex
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+
+static int sGLExtentionSlot = 0;
+static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void(*findProcAddress(const char* name,
+ const extention_map_t* map, size_t n))() {
+ for (uint32_t i=0 ; i<n ; i++) {
+ if (!strcmp(name, map[i].name)) {
+ return map[i].address;
+ }
+ }
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern EGLBoolean egl_init_drivers();
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern gl_hooks_t gHooksTrace;
+
+// ----------------------------------------------------------------------------
+
+static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+
+// ----------------------------------------------------------------------------
+
+static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list) {
+ if (platform != EGL_PLATFORM_ANDROID_KHR) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+ }
+
+ uintptr_t index = reinterpret_cast<uintptr_t>(display);
+ if (index >= NUM_DISPLAYS) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+ }
+
+ EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display, attrib_list);
+ return dpy;
+}
+
+EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) {
+ return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr);
+}
+
+EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display,
+ const EGLAttrib* attrib_list) {
+ return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display),
+ attrib_list);
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
+{
+ egl_display_ptr dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+ EGLBoolean res = dp->initialize(major, minor);
+
+ return res;
+}
+
+EGLBoolean eglTerminateImpl(EGLDisplay dpy)
+{
+ // NOTE: don't unload the drivers b/c some APIs can be called
+ // after eglTerminate() has been called. eglTerminate() only
+ // terminates an EGLDisplay, not a EGL itself.
+
+ egl_display_ptr dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+ EGLBoolean res = dp->terminate();
+
+ return res;
+}
+
+// ----------------------------------------------------------------------------
+// configuration
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
+ EGLConfig *configs,
+ EGLint config_size, EGLint *num_config)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ if (num_config==nullptr) {
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+
+ EGLBoolean res = EGL_FALSE;
+ *num_config = 0;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso) {
+ res = cnx->egl.eglGetConfigs(
+ dp->disp.dpy, configs, config_size, num_config);
+ }
+
+ return res;
+}
+
+EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
+ EGLConfig *configs, EGLint config_size,
+ EGLint *num_config)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ if (num_config==nullptr) {
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+
+ EGLBoolean res = EGL_FALSE;
+ *num_config = 0;
+
+ 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")) {
+ size_t attribCount = 0;
+ EGLint attrib = attrib_list[0];
+
+ // Only enable MSAA if the context is OpenGL ES 2.0 and
+ // if no caveat is requested
+ const EGLint *attribRendererable = nullptr;
+ const EGLint *attribCaveat = nullptr;
+
+ // Count the number of attributes and look for
+ // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+ while (attrib != EGL_NONE) {
+ attrib = attrib_list[attribCount];
+ switch (attrib) {
+ case EGL_RENDERABLE_TYPE:
+ attribRendererable = &attrib_list[attribCount];
+ break;
+ case EGL_CONFIG_CAVEAT:
+ attribCaveat = &attrib_list[attribCount];
+ break;
+ default:
+ break;
+ }
+ attribCount++;
+ }
+
+ if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+ (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+
+ // Insert 2 extra attributes to force-enable MSAA 4x
+ EGLint aaAttribs[attribCount + 4];
+ aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+ aaAttribs[1] = 1;
+ aaAttribs[2] = EGL_SAMPLES;
+ aaAttribs[3] = 4;
+
+ memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+ EGLint numConfigAA;
+ EGLBoolean resAA = cnx->egl.eglChooseConfig(
+ dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
+
+ if (resAA == EGL_TRUE && numConfigAA > 0) {
+ ALOGD("Enabling MSAA 4x");
+ *num_config = numConfigAA;
+ return resAA;
+ }
+ }
+ }
+ }
+
+ res = cnx->egl.eglChooseConfig(
+ dp->disp.dpy, attrib_list, configs, config_size, num_config);
+ }
+ return res;
+}
+
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
+ EGLint attribute, EGLint *value)
+{
+ egl_connection_t* cnx = nullptr;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (!dp) return EGL_FALSE;
+
+ return cnx->egl.eglGetConfigAttrib(
+ dp->disp.dpy, config, attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// surfaces
+// ----------------------------------------------------------------------------
+
+// Translates EGL color spaces to Android data spaces.
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
+ if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
+ return HAL_DATASPACE_UNKNOWN;
+ } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
+ return HAL_DATASPACE_V0_SRGB;
+ } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+ return HAL_DATASPACE_DISPLAY_P3;
+ } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
+ return HAL_DATASPACE_DISPLAY_P3_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT) {
+ return HAL_DATASPACE_DISPLAY_P3;
+ } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
+ return HAL_DATASPACE_V0_SCRGB;
+ } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
+ return HAL_DATASPACE_V0_SCRGB_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
+ return HAL_DATASPACE_BT2020_PQ;
+ }
+ return HAL_DATASPACE_UNKNOWN;
+}
+
+// Get the colorspace value that should be reported from queries. When the colorspace
+// is unknown (no attribute passed), default to reporting LINEAR.
+static EGLint getReportedColorSpace(EGLint colorspace) {
+ return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
+}
+
+// Returns a list of color spaces understood by the vendor EGL driver.
+static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) {
+ std::vector<EGLint> colorSpaces;
+
+ // sRGB and linear are always supported when color space support is present.
+ colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+ colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_pq")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
+ }
+ return colorSpaces;
+}
+
+// Cleans up color space related parameters that the driver does not understand.
+// If there is no color space attribute in attrib_list, colorSpace is left
+// unmodified.
+template <typename AttrType>
+static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+ const AttrType* attrib_list, EGLint* colorSpace,
+ std::vector<AttrType>* strippedAttribList) {
+ for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+ bool copyAttribute = true;
+ if (attr[0] == EGL_GL_COLORSPACE_KHR) {
+ switch (attr[1]) {
+ case EGL_GL_COLORSPACE_LINEAR_KHR:
+ case EGL_GL_COLORSPACE_SRGB_KHR:
+ case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
+ case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
+ case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
+ case EGL_GL_COLORSPACE_SCRGB_EXT:
+ case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
+ case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
+ case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
+ // Fail immediately if the driver doesn't have color space support at all.
+ if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+ break;
+ default:
+ // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_*
+ return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+ }
+ *colorSpace = static_cast<EGLint>(attr[1]);
+
+ // Strip the attribute if the driver doesn't understand it.
+ copyAttribute = false;
+ std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp);
+ for (auto driverColorSpace : driverColorSpaces) {
+ if (static_cast<EGLint>(attr[1]) == driverColorSpace) {
+ copyAttribute = true;
+ break;
+ }
+ }
+
+ // If the driver doesn't understand it, we should map sRGB-encoded P3 to
+ // sRGB rather than just dropping the colorspace on the floor.
+ // For this format, the driver is expected to apply the sRGB
+ // transfer function during framebuffer operations.
+ if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+ strippedAttribList->push_back(attr[0]);
+ strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+ }
+ }
+ if (copyAttribute) {
+ strippedAttribList->push_back(attr[0]);
+ strippedAttribList->push_back(attr[1]);
+ }
+ }
+ // Terminate the attribute list.
+ strippedAttribList->push_back(EGL_NONE);
+
+ // If the passed color space has wide color gamut, check whether the target native window
+ // supports wide color.
+ const bool colorSpaceIsNarrow = *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
+ *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || *colorSpace == EGL_UNKNOWN;
+ if (window && !colorSpaceIsNarrow) {
+ bool windowSupportsWideColor = true;
+ // Ordinarily we'd put a call to native_window_get_wide_color_support
+ // at the beginning of the function so that we'll have the
+ // result when needed elsewhere in the function.
+ // However, because eglCreateWindowSurface is called by SurfaceFlinger and
+ // SurfaceFlinger is required to answer the call below we would
+ // end up in a deadlock situation. By moving the call to only happen
+ // if the application has specifically asked for wide-color we avoid
+ // the deadlock with SurfaceFlinger since it will not ask for a
+ // wide-color surface.
+ int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
+
+ if (err) {
+ ALOGE("processAttributes: invalid window (win=%p) "
+ "failed (%#x) (already connected to another API?)",
+ window, err);
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE);
+ }
+ if (!windowSupportsWideColor) {
+ // Application has asked for a wide-color colorspace but
+ // wide-color support isn't available on the display the window is on.
+ return setError(EGL_BAD_MATCH, EGL_FALSE);
+ }
+ }
+ return true;
+}
+
+// Note: This only works for existing GLenum's that are all 32bits.
+// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this.
+void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) {
+ for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) {
+ newList.push_back(static_cast<EGLint>(attr[0]));
+ newList.push_back(static_cast<EGLint>(attr[1]));
+ }
+ newList.push_back(EGL_NONE);
+}
+
+// Gets the native pixel format corrsponding to the passed EGLConfig.
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
+ android_pixel_format* format) {
+ // Set the native window's buffers format to match what this config requests.
+ // Whether to use sRGB gamma is not part of the EGLconfig, but is part
+ // of our native format. So if sRGB gamma is requested, we have to
+ // modify the EGLconfig's format before setting the native window's
+ // format.
+
+ EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
+
+ EGLint a = 0;
+ EGLint r, g, b;
+ r = g = b = 0;
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+ EGLint colorDepth = r + g + b;
+
+ // Today, the driver only understands sRGB and linear on 888X
+ // formats. Strip other colorspaces from the attribute list and
+ // only use them to set the dataspace via
+ // native_window_set_buffers_dataspace
+ // if pixel format is RGBX 8888
+ // TBD: Can test for future extensions that indicate that driver
+ // handles requested color space and we can let it through.
+ // allow SRGB and LINEAR. All others need to be stripped.
+ // else if 565, 4444
+ // TBD: Can we assume these are supported if 8888 is?
+ // else if FP16 or 1010102
+ // strip colorspace from attribs.
+ // endif
+ if (a == 0) {
+ if (colorDepth <= 16) {
+ *format = HAL_PIXEL_FORMAT_RGB_565;
+ } else {
+ if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+ if (colorDepth > 24) {
+ *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBX_8888;
+ }
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ }
+ }
+ } else {
+ if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+ if (colorDepth > 24) {
+ *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBA_8888;
+ }
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ }
+ }
+}
+
+EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
+ android_smpte2086_metadata smpteMetadata;
+ if (s->getSmpte2086Metadata(smpteMetadata)) {
+ int err =
+ native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
+ s->resetSmpte2086Metadata();
+ if (err != 0) {
+ ALOGE("error setting native window smpte2086 metadata: %s (%d)", strerror(-err), err);
+ return EGL_FALSE;
+ }
+ }
+ android_cta861_3_metadata cta8613Metadata;
+ if (s->getCta8613Metadata(cta8613Metadata)) {
+ int err =
+ native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
+ s->resetCta8613Metadata();
+ if (err != 0) {
+ ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", strerror(-err), err);
+ return EGL_FALSE;
+ }
+ }
+ return EGL_TRUE;
+}
+
+template <typename AttrType, typename CreateFuncType>
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+ ANativeWindow* window, const AttrType* attrib_list,
+ CreateFuncType createWindowSurfaceFunc) {
+ const AttrType* origAttribList = attrib_list;
+
+ if (!window) {
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
+ int value = 0;
+ window->query(window, NATIVE_WINDOW_IS_VALID, &value);
+ if (!value) {
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
+ // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
+ // native_window_* calls, so don't do them here.
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ if (result < 0) {
+ ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
+ "failed (%#x) (already connected to another API?)",
+ window, result);
+ return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+ }
+ }
+
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
+
+ // now select correct colorspace and dataspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<AttrType> strippedAttribList;
+ if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ }
+ return EGL_NO_SURFACE;
+ }
+ attrib_list = strippedAttribList.data();
+
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ int err = native_window_set_buffers_format(window, format);
+ if (err != 0) {
+ ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
+ android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+ // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN.
+ // HAL_DATASPACE_UNKNOWN is the default value, but it may have changed
+ // at this point.
+ err = native_window_set_buffers_data_space(window, dataSpace);
+ if (err != 0) {
+ ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err), err);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+ }
+
+ // the EGL spec requires that a new EGLSurface default to swap interval
+ // 1, so explicitly set that on the window here.
+ window->setSwapInterval(window, 1);
+
+ EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
+ if (surface != EGL_NO_SURFACE) {
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+ getReportedColorSpace(colorSpace), cnx);
+ return s;
+ }
+
+ // EGLSurface creation failed
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ native_window_set_buffers_format(window, 0);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ }
+ return EGL_NO_SURFACE;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config,
+ NativeWindowType window,
+ const EGLint* attrib_list);
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)(
+ EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list);
+
+EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+ const EGLint* attrib_list) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ return eglCreateWindowSurfaceTmpl<
+ EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
+ cnx->egl.eglCreateWindowSurface);
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
+ const EGLAttrib* attrib_list) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglCreatePlatformWindowSurface) {
+ return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>(
+ dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list,
+ cnx->egl.eglCreatePlatformWindowSurface);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have "
+ "eglCreatePlatformWindowSurface");
+ return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+ }
+
+ std::vector<EGLint> convertedAttribs;
+ convertAttribs(attrib_list, convertedAttribs);
+ if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) {
+ return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+ dp, cnx, config, static_cast<ANativeWindow*>(native_window),
+ convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT);
+ } else {
+ return eglCreateWindowSurfaceTmpl<
+ EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config,
+ static_cast<ANativeWindow*>(
+ native_window),
+ convertedAttribs.data(),
+ cnx->egl.eglCreateWindowSurface);
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+ void* /*native_pixmap*/,
+ const EGLAttrib* /*attrib_list*/) {
+ // Per EGL_KHR_platform_android:
+ // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that
+ // belongs to the Android platform. Any such call fails and generates
+ // an EGL_BAD_PARAMETER error.
+
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+ NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
+ egl_connection_t* cnx = nullptr;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config,
+ const EGLint* attrib_list) {
+ egl_connection_t* cnx = nullptr;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
+
+ // Select correct colorspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<EGLint> strippedAttribList;
+ if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ return EGL_NO_SURFACE;
+ }
+ attrib_list = strippedAttribList.data();
+
+ EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list);
+ if (surface != EGL_NO_SURFACE) {
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+ getReportedColorSpace(colorSpace), cnx);
+ return s;
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t* const s = get_surface(surface);
+ EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
+ if (result == EGL_TRUE) {
+ _s.terminate();
+ }
+ return result;
+}
+
+EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute,
+ EGLint* value) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const* const s = get_surface(surface);
+ if (s->getColorSpaceAttribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->getSmpte2086Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->getCta8613Attribute(attribute, value)) {
+ return EGL_TRUE;
+ }
+ return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+}
+
+void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Contexts
+// ----------------------------------------------------------------------------
+
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
+ EGLContext share_list, const EGLint *attrib_list)
+{
+ egl_connection_t* cnx = nullptr;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ if (share_list != EGL_NO_CONTEXT) {
+ if (!ContextRef(dp.get(), share_list).get()) {
+ return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
+ }
+ egl_context_t* const c = get_context(share_list);
+ share_list = c->context;
+ }
+ // b/111083885 - If we are presenting EGL 1.4 interface to apps
+ // error out on robust access attributes that are invalid
+ // in EGL 1.4 as the driver may be fine with them but dEQP expects
+ // tests to fail according to spec.
+ if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
+ const EGLint* attrib_ptr = attrib_list;
+ while (*attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint value = *attrib_ptr++;
+ if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
+ // We are GL ES context with EGL 1.4, this is an invalid
+ // attribute
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
+ };
+ }
+ EGLContext context = cnx->egl.eglCreateContext(
+ dp->disp.dpy, config, share_list, attrib_list);
+ if (context != EGL_NO_CONTEXT) {
+ // figure out if it's a GLESv1 or GLESv2
+ int version = 0;
+ if (attrib_list) {
+ while (*attrib_list != EGL_NONE) {
+ GLint attr = *attrib_list++;
+ GLint value = *attrib_list++;
+ if (attr == EGL_CONTEXT_CLIENT_VERSION) {
+ if (value == 1) {
+ version = egl_connection_t::GLESv1_INDEX;
+ } else if (value == 2 || value == 3) {
+ version = egl_connection_t::GLESv2_INDEX;
+ }
+ }
+ };
+ }
+ egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
+ version);
+ return c;
+ }
+ }
+ return EGL_NO_CONTEXT;
+}
+
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp)
+ return EGL_FALSE;
+
+ ContextRef _c(dp.get(), ctx);
+ if (!_c.get())
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ egl_context_t * const c = get_context(ctx);
+ EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
+ if (result == EGL_TRUE) {
+ _c.terminate();
+ }
+ return result;
+}
+
+EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw,
+ EGLSurface read, EGLContext ctx)
+{
+ egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+ // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
+ // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
+ // a valid but uninitialized display.
+ if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
+ (draw != EGL_NO_SURFACE) ) {
+ if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+
+ // get a reference to the object passed in
+ ContextRef _c(dp.get(), ctx);
+ SurfaceRef _d(dp.get(), draw);
+ SurfaceRef _r(dp.get(), read);
+
+ // validate the context (if not EGL_NO_CONTEXT)
+ if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
+ // EGL_NO_CONTEXT is valid
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ }
+
+ // these are the underlying implementation's object
+ EGLContext impl_ctx = EGL_NO_CONTEXT;
+ EGLSurface impl_draw = EGL_NO_SURFACE;
+ EGLSurface impl_read = EGL_NO_SURFACE;
+
+ // these are our objects structs passed in
+ egl_context_t * c = nullptr;
+ egl_surface_t const * d = nullptr;
+ egl_surface_t const * r = nullptr;
+
+ // these are the current objects structs
+ egl_context_t * cur_c = get_context(getContext());
+
+ if (ctx != EGL_NO_CONTEXT) {
+ c = get_context(ctx);
+ impl_ctx = c->context;
+ } else {
+ // no context given, use the implementation of the current context
+ if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
+ // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
+ return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
+ }
+ if (cur_c == nullptr) {
+ // no current context
+ // not an error, there is just no current context.
+ return EGL_TRUE;
+ }
+ }
+
+ // retrieve the underlying implementation's draw EGLSurface
+ if (draw != EGL_NO_SURFACE) {
+ if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ d = get_surface(draw);
+ impl_draw = d->surface;
+ }
+
+ // retrieve the underlying implementation's read EGLSurface
+ if (read != EGL_NO_SURFACE) {
+ if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ r = get_surface(read);
+ impl_read = r->surface;
+ }
+
+
+ EGLBoolean result = dp->makeCurrent(c, cur_c,
+ draw, read, ctx,
+ impl_draw, impl_read, impl_ctx);
+
+ if (result == EGL_TRUE) {
+ if (c) {
+ setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
+ egl_tls_t::setContext(ctx);
+ _c.acquire();
+ _r.acquire();
+ _d.acquire();
+ } else {
+ setGLHooksThreadSpecific(&gHooksNoContext);
+ egl_tls_t::setContext(EGL_NO_CONTEXT);
+ }
+ } else {
+ // this will ALOGE the error
+ egl_connection_t* const cnx = &gEGLImpl;
+ result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
+ EGLint attribute, EGLint *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ ContextRef _c(dp.get(), ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ egl_context_t * const c = get_context(ctx);
+ return c->cnx->egl.eglQueryContext(
+ dp->disp.dpy, c->context, attribute, value);
+
+}
+
+EGLContext eglGetCurrentContextImpl(void)
+{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_CONTEXT.
+ EGLContext ctx = getContext();
+ return ctx;
+}
+
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
+{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_SURFACE.
+
+ EGLContext ctx = getContext();
+ if (ctx) {
+ egl_context_t const * const c = get_context(ctx);
+ if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+ switch (readdraw) {
+ case EGL_READ: return c->read;
+ case EGL_DRAW: return c->draw;
+ default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLDisplay eglGetCurrentDisplayImpl(void)
+{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_DISPLAY.
+
+ EGLContext ctx = getContext();
+ if (ctx) {
+ egl_context_t const * const c = get_context(ctx);
+ if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+ return c->dpy;
+ }
+ return EGL_NO_DISPLAY;
+}
+
+EGLBoolean eglWaitGLImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->dso)
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ return cnx->egl.eglWaitGL();
+}
+
+EGLBoolean eglWaitNativeImpl(EGLint engine)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->dso)
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ return cnx->egl.eglWaitNative(engine);
+}
+
+EGLint eglGetErrorImpl(void)
+{
+ EGLint err = EGL_SUCCESS;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso) {
+ err = cnx->egl.eglGetError();
+ }
+ if (err == EGL_SUCCESS) {
+ err = egl_tls_t::getError();
+ }
+ return err;
+}
+
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
+ const char* procname) {
+ const egl_connection_t* cnx = &gEGLImpl;
+ void* proc = nullptr;
+
+ proc = dlsym(cnx->libEgl, procname);
+ if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+ proc = dlsym(cnx->libGles2, procname);
+ if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+ proc = dlsym(cnx->libGles1, procname);
+ if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+ return nullptr;
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
+{
+ if (FILTER_EXTENSIONS(procname)) {
+ return nullptr;
+ }
+
+ __eglMustCastToProperFunctionPointerType addr;
+ addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
+ if (addr) return addr;
+
+ addr = findBuiltinWrapper(procname);
+ if (addr) return addr;
+
+ // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+ pthread_mutex_lock(&sExtensionMapMutex);
+
+ /*
+ * Since eglGetProcAddress() is not associated to anything, it needs
+ * to return a function pointer that "works" regardless of what
+ * the current context is.
+ *
+ * For this reason, we return a "forwarder", a small stub that takes
+ * care of calling the function associated with the context
+ * currently bound.
+ *
+ * We first look for extensions we've already resolved, if we're seeing
+ * this extension for the first time, we go through all our
+ * implementations and call eglGetProcAddress() and record the
+ * result in the appropriate implementation hooks and return the
+ * address of the forwarder corresponding to that hook set.
+ *
+ */
+
+ const std::string name(procname);
+
+ auto& extentionMap = sGLExtentionMap;
+ auto pos = extentionMap.find(name);
+ addr = (pos != extentionMap.end()) ? pos->second : nullptr;
+ const int slot = sGLExtentionSlot;
+
+ ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
+ "no more slots for eglGetProcAddress(\"%s\")",
+ procname);
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ LayerLoader& layer_loader(LayerLoader::getInstance());
+
+ if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+
+ if (cnx->dso && cnx->egl.eglGetProcAddress) {
+
+ // Extensions are independent of the bound context
+ addr = cnx->egl.eglGetProcAddress(procname);
+ if (addr) {
+
+ // purposefully track the bottom of the stack in extensionMap
+ extentionMap[name] = addr;
+
+ // Apply layers
+ addr = layer_loader.ApplyLayers(procname, addr);
+
+ // Track the top most entry point
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ addr = gExtensionForwarders[slot];
+ sGLExtentionSlot++;
+ }
+ }
+
+ } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+
+ // We've seen this func before, but we tracked the bottom, so re-apply layers
+ // More layers might have been enabled
+ addr = layer_loader.ApplyLayers(procname, addr);
+
+ // Track the top most entry point
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ addr = gExtensionForwarders[slot];
+ }
+
+ pthread_mutex_unlock(&sExtensionMapMutex);
+ return addr;
+}
+
+class FrameCompletionThread {
+public:
+
+ static void queueSync(EGLSyncKHR sync) {
+ static FrameCompletionThread thread;
+
+ char name[64];
+
+ std::lock_guard<std::mutex> lock(thread.mMutex);
+ snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
+ ATRACE_NAME(name);
+
+ thread.mQueue.push_back(sync);
+ thread.mCondition.notify_one();
+ thread.mFramesQueued++;
+ ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
+ }
+
+private:
+
+ FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
+ std::thread thread(&FrameCompletionThread::loop, this);
+ thread.detach();
+ }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+ void loop() {
+ while (true) {
+ threadLoop();
+ }
+ }
+#pragma clang diagnostic pop
+
+ void threadLoop() {
+ EGLSyncKHR sync;
+ uint32_t frameNum;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+ sync = mQueue[0];
+ frameNum = mFramesCompleted;
+ }
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ {
+ char name[64];
+ snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
+ ATRACE_NAME(name);
+
+ EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+ if (result == EGL_FALSE) {
+ ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("FrameCompletion: timeout waiting for fence");
+ }
+ eglDestroySyncKHR(dpy, sync);
+ }
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.pop_front();
+ mFramesCompleted++;
+ ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
+ }
+ }
+
+ uint32_t mFramesQueued;
+ uint32_t mFramesCompleted;
+ std::deque<EGLSyncKHR> mQueue;
+ std::condition_variable mCondition;
+ std::mutex mMutex;
+};
+
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
+ EGLint *rects, EGLint n_rects)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), draw);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t* const s = get_surface(draw);
+
+ if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+ if (sync != EGL_NO_SYNC_KHR) {
+ FrameCompletionThread::queueSync(sync);
+ }
+ }
+
+ if (CC_UNLIKELY(dp->finishOnSwap)) {
+ uint32_t pixel;
+ egl_context_t * const c = get_context( egl_tls_t::getContext() );
+ if (c) {
+ // glReadPixels() ensures that the frame is complete
+ s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
+ GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+ }
+ }
+
+ if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ if (!sendSurfaceMetadata(s)) {
+ native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
+ return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
+ }
+ }
+
+ if (n_rects == 0) {
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ }
+
+ std::vector<android_native_rect_t> androidRects((size_t)n_rects);
+ for (int r = 0; r < n_rects; ++r) {
+ int offset = r * 4;
+ int x = rects[offset];
+ int y = rects[offset + 1];
+ int width = rects[offset + 2];
+ int height = rects[offset + 3];
+ android_native_rect_t androidRect;
+ androidRect.left = x;
+ androidRect.top = y + height;
+ androidRect.right = x + width;
+ androidRect.bottom = y;
+ androidRects.push_back(androidRect);
+ }
+ if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
+ androidRects.size());
+ }
+
+ if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
+ return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
+ rects, n_rects);
+ } else {
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ }
+}
+
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
+{
+ return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
+}
+
+EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface,
+ NativePixmapType target)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
+}
+
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
+{
+ if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
+ // Return list of client extensions
+ return gClientExtensionString;
+ }
+
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return (const char *) nullptr;
+
+ switch (name) {
+ case EGL_VENDOR:
+ return dp->getVendorString();
+ case EGL_VERSION:
+ return dp->getVersionString();
+ case EGL_EXTENSIONS:
+ return dp->getExtensionString();
+ case EGL_CLIENT_APIS:
+ return dp->getClientApiString();
+ default:
+ break;
+ }
+ return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return (const char *) nullptr;
+
+ switch (name) {
+ case EGL_VENDOR:
+ return dp->disp.queryString.vendor;
+ case EGL_VERSION:
+ return dp->disp.queryString.version;
+ case EGL_EXTENSIONS:
+ return dp->disp.queryString.extensions;
+ case EGL_CLIENT_APIS:
+ return dp->disp.queryString.clientApi;
+ default:
+ break;
+ }
+ return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+// ----------------------------------------------------------------------------
+// EGL 1.1
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSurfaceAttribImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t * const s = get_surface(surface);
+
+ if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+ if (!s->getNativeWindow()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+ int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
+ return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ if (attribute == EGL_TIMESTAMPS_ANDROID) {
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+ int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
+ return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ if (s->setSmpte2086Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->setCta8613Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->cnx->egl.eglSurfaceAttrib) {
+ return s->cnx->egl.eglSurfaceAttrib(
+ dp->disp.dpy, s->surface, attribute, value);
+ }
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglBindTexImageImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglBindTexImage) {
+ return s->cnx->egl.eglBindTexImage(
+ dp->disp.dpy, s->surface, buffer);
+ }
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglReleaseTexImageImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglReleaseTexImage) {
+ return s->cnx->egl.eglReleaseTexImage(
+ dp->disp.dpy, s->surface, buffer);
+ }
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean res = EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglSwapInterval) {
+ res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
+ }
+
+ return res;
+}
+
+
+// ----------------------------------------------------------------------------
+// EGL 1.2
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglWaitClientImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->dso)
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ EGLBoolean res;
+ if (cnx->egl.eglWaitClient) {
+ res = cnx->egl.eglWaitClient();
+ } else {
+ res = cnx->egl.eglWaitGL();
+ }
+ return res;
+}
+
+EGLBoolean eglBindAPIImpl(EGLenum api)
+{
+ // bind this API on all EGLs
+ EGLBoolean res = EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglBindAPI) {
+ res = cnx->egl.eglBindAPI(api);
+ }
+ return res;
+}
+
+EGLenum eglQueryAPIImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryAPI) {
+ return cnx->egl.eglQueryAPI();
+ }
+
+ // or, it can only be OpenGL ES
+ return EGL_OPENGL_ES_API;
+}
+
+EGLBoolean eglReleaseThreadImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglReleaseThread) {
+ cnx->egl.eglReleaseThread();
+ }
+
+ // If there is context bound to the thread, release it
+ egl_display_t::loseCurrent(get_context(getContext()));
+
+ egl_tls_t::clearTLS();
+ return EGL_TRUE;
+}
+
+EGLSurface eglCreatePbufferFromClientBufferImpl(
+ EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+ EGLConfig config, const EGLint *attrib_list)
+{
+ egl_connection_t* cnx = nullptr;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (!dp) return EGL_FALSE;
+ if (cnx->egl.eglCreatePbufferFromClientBuffer) {
+ return cnx->egl.eglCreatePbufferFromClientBuffer(
+ dp->disp.dpy, buftype, buffer, config, attrib_list);
+ }
+ return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 3
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
+ const EGLint *attrib_list)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglLockSurfaceKHR) {
+ return s->cnx->egl.eglLockSurfaceKHR(
+ dp->disp.dpy, s->surface, attrib_list);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglUnlockSurfaceKHR) {
+ return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+// Note: EGLImageKHR and EGLImage are the same thing so no need
+// to templatize that.
+template <typename AttrType, typename FuncType>
+EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer, const AttrType* attrib_list,
+ FuncType eglCreateImageFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_IMAGE_KHR;
+
+ ContextRef _c(dp.get(), ctx);
+ egl_context_t* const c = _c.get();
+
+ EGLImageKHR result = EGL_NO_IMAGE_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglCreateImageFunc) {
+ result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
+ attrib_list);
+ }
+ return result;
+}
+
+typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer,
+ const EGLAttrib* attrib_list);
+
+EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer, const EGLint* attrib_list) {
+ return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+ attrib_list,
+ gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+ const EGLAttrib* attrib_list) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglCreateImage) {
+ return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer,
+ attrib_list,
+ cnx->egl.eglCreateImage);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage");
+ return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE);
+ }
+
+ std::vector<EGLint> convertedAttribs;
+ convertAttribs(attrib_list, convertedAttribs);
+ return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+ convertedAttribs.data(),
+ gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
+ PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && destroyImageFunc) {
+ result = destroyImageFunc(dp->disp.dpy, img);
+ }
+ return result;
+}
+
+EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) {
+ return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglDestroyImage) {
+ return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage");
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize
+template <typename AttrType, typename FuncType>
+EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
+ FuncType eglCreateSyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_SYNC_KHR;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ EGLSyncKHR result = EGL_NO_SYNC_KHR;
+ if (cnx->dso && eglCreateSyncFunc) {
+ result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list);
+ }
+ return result;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type,
+ const EGLAttrib* attrib_list);
+
+EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
+ return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list,
+ gEGLImpl.egl.eglCreateSyncKHR);
+}
+
+EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglCreateSync) {
+ return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list,
+ cnx->egl.eglCreateSync);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync");
+ return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC);
+ }
+
+ std::vector<EGLint> convertedAttribs;
+ convertAttribs(attrib_list, convertedAttribs);
+ return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(),
+ cnx->egl.eglCreateSyncKHR);
+}
+
+EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
+ PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglDestroySyncFunc) {
+ result = eglDestroySyncFunc(dp->disp.dpy, sync);
+ }
+ return result;
+}
+
+EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglDestroySync) {
+ return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync);
+ }
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync");
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && gEGLImpl.egl.eglSignalSyncKHR) {
+ result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode);
+ }
+ return result;
+}
+
+EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
+ PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLint result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglClientWaitSyncFunc) {
+ result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout);
+ }
+ return result;
+}
+
+EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglClientWaitSync) {
+ return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync);
+ }
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync");
+ return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+ }
+
+ return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+template <typename AttrType, typename FuncType>
+EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
+ FuncType eglGetSyncAttribFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglGetSyncAttribFunc) {
+ result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value);
+ }
+ return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute,
+ EGLAttrib* value);
+
+EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglGetSyncAttrib) {
+ return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value,
+ cnx->egl.eglGetSyncAttrib);
+ }
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib");
+ return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+ }
+
+ // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib
+ EGLint attribValue;
+ EGLBoolean ret =
+ eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute,
+ &attribValue,
+ gEGLImpl.egl
+ .eglGetSyncAttribKHR);
+ if (ret) {
+ *value = static_cast<EGLAttrib>(attribValue);
+ }
+ return ret;
+}
+
+EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute,
+ EGLint* value) {
+ return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value,
+ gEGLImpl.egl
+ .eglGetSyncAttribKHR);
+}
+
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_STREAM_KHR;
+
+ EGLStreamKHR result = EGL_NO_STREAM_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
+ result = cnx->egl.eglCreateStreamKHR(
+ dp->disp.dpy, attrib_list);
+ }
+ return result;
+}
+
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
+ result = cnx->egl.eglDestroyStreamKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLint value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
+ result = cnx->egl.eglStreamAttribKHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLint *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
+ result = cnx->egl.eglQueryStreamKHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLuint64KHR *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
+ result = cnx->egl.eglQueryStreamu64KHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLTimeKHR *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
+ result = cnx->egl.eglQueryStreamTimeKHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
+ EGLStreamKHR stream, const EGLint *attrib_list)
+{
+ egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_SURFACE;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
+ EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
+ dp->disp.dpy, config, stream, attrib_list);
+ if (surface != EGL_NO_SURFACE) {
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+ EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
+ return s;
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
+ EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
+ result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
+ EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
+ result = cnx->egl.eglStreamConsumerAcquireKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
+ EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
+ result = cnx->egl.eglStreamConsumerReleaseKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
+ EGLDisplay dpy, EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
+
+ EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
+ result = cnx->egl.eglGetStreamFileDescriptorKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
+ EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_STREAM_KHR;
+
+ EGLStreamKHR result = EGL_NO_STREAM_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
+ result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
+ dp->disp.dpy, file_descriptor);
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 15
+// ----------------------------------------------------------------------------
+
+// Need to template function type because return type is different
+template <typename ReturnType, typename FuncType>
+ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
+ FuncType eglWaitSyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+ ReturnType result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglWaitSyncFunc) {
+ result = eglWaitSyncFunc(dp->disp.dpy, sync, flags);
+ }
+ return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags);
+
+EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+ cnx->egl.eglWaitSyncKHR);
+}
+
+EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglWaitSync) {
+ return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags,
+ cnx->egl.eglWaitSync);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+ }
+
+ return static_cast<EGLBoolean>(
+ eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+ cnx->egl.eglWaitSyncKHR));
+}
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
+
+ EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
+ result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
+ }
+ return result;
+}
+
+EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLnsecsANDROID time)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return EGL_FALSE;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+ native_window_set_buffers_timestamp(s->getNativeWindow(), time);
+
+ return EGL_TRUE;
+}
+
+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
+}
+
+// ----------------------------------------------------------------------------
+// NVIDIA extensions
+// ----------------------------------------------------------------------------
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
+{
+ EGLuint64NV ret = 0;
+ egl_connection_t* const cnx = &gEGLImpl;
+
+ if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
+ return cnx->egl.eglGetSystemTimeFrequencyNV();
+ }
+
+ return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+EGLuint64NV eglGetSystemTimeNVImpl()
+{
+ EGLuint64NV ret = 0;
+ egl_connection_t* const cnx = &gEGLImpl;
+
+ if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
+ return cnx->egl.eglGetSystemTimeNV();
+ }
+
+ return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+// ----------------------------------------------------------------------------
+// Partial update extension
+// ----------------------------------------------------------------------------
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint *rects, EGLint n_rects)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglSetDamageRegionKHR) {
+ return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
+ rects, n_rects);
+ }
+
+ return EGL_FALSE;
+}
+
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR *frameId) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ uint64_t nextFrameId = 0;
+ int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
+
+ if (ret != 0) {
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetNextFrameId: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+
+ *frameId = nextFrameId;
+ return EGL_TRUE;
+}
+
+EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ nsecs_t* compositeDeadline = nullptr;
+ nsecs_t* compositeInterval = nullptr;
+ nsecs_t* compositeToPresentLatency = nullptr;
+
+ for (int i = 0; i < numTimestamps; i++) {
+ switch (names[i]) {
+ case EGL_COMPOSITE_DEADLINE_ANDROID:
+ compositeDeadline = &values[i];
+ break;
+ case EGL_COMPOSITE_INTERVAL_ANDROID:
+ compositeInterval = &values[i];
+ break;
+ case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+ compositeToPresentLatency = &values[i];
+ break;
+ default:
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+ }
+
+ int ret = native_window_get_compositor_timing(s->getNativeWindow(),
+ compositeDeadline, compositeInterval, compositeToPresentLatency);
+
+ switch (ret) {
+ case 0:
+ return EGL_TRUE;
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetCompositorTiming: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+}
+
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint name)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ ANativeWindow* window = s->getNativeWindow();
+ if (!window) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ switch (name) {
+ case EGL_COMPOSITE_DEADLINE_ANDROID:
+ case EGL_COMPOSITE_INTERVAL_ANDROID:
+ case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+ return EGL_TRUE;
+ default:
+ return EGL_FALSE;
+ }
+}
+
+EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
+ EGLnsecsANDROID *values)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ nsecs_t* requestedPresentTime = nullptr;
+ nsecs_t* acquireTime = nullptr;
+ nsecs_t* latchTime = nullptr;
+ nsecs_t* firstRefreshStartTime = nullptr;
+ nsecs_t* gpuCompositionDoneTime = nullptr;
+ nsecs_t* lastRefreshStartTime = nullptr;
+ nsecs_t* displayPresentTime = nullptr;
+ nsecs_t* dequeueReadyTime = nullptr;
+ nsecs_t* releaseTime = nullptr;
+
+ for (int i = 0; i < numTimestamps; i++) {
+ switch (timestamps[i]) {
+ case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+ requestedPresentTime = &values[i];
+ break;
+ case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+ acquireTime = &values[i];
+ break;
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ latchTime = &values[i];
+ break;
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ firstRefreshStartTime = &values[i];
+ break;
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ lastRefreshStartTime = &values[i];
+ break;
+ case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+ gpuCompositionDoneTime = &values[i];
+ break;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+ displayPresentTime = &values[i];
+ break;
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
+ dequeueReadyTime = &values[i];
+ break;
+ case EGL_READS_DONE_TIME_ANDROID:
+ releaseTime = &values[i];
+ break;
+ default:
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+ }
+
+ int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
+ requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
+ dequeueReadyTime, releaseTime);
+
+ switch (ret) {
+ case 0:
+ return EGL_TRUE;
+ case -ENOENT:
+ return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ case -EINVAL:
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetFrameTimestamps: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+}
+
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ ANativeWindow* window = s->getNativeWindow();
+ if (!window) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ switch (timestamp) {
+ case EGL_COMPOSITE_DEADLINE_ANDROID:
+ case EGL_COMPOSITE_INTERVAL_ANDROID:
+ case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+ case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+ case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
+ case EGL_READS_DONE_TIME_ANDROID:
+ return EGL_TRUE;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+ int value = 0;
+ window->query(window,
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ return value == 0 ? EGL_FALSE : EGL_TRUE;
+ }
+ default:
+ return EGL_FALSE;
+ }
+}
+
+const GLubyte * glGetStringImpl(GLenum name) {
+ const GLubyte * ret = egl_get_string_for_current_context(name);
+ if (ret == NULL) {
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if(_c) ret = _c->glGetString(name);
+ }
+ return ret;
+}
+
+const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
+ const GLubyte * ret = egl_get_string_for_current_context(name, index);
+ if (ret == NULL) {
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if(_c) ret = _c->glGetStringi(name, index);
+ }
+ return ret;
+}
+
+void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetBooleanv(pname, data);
+}
+
+void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = (GLfloat)num_exts;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetFloatv(pname, data);
+}
+
+void glGetIntegervImpl(GLenum pname, GLint * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = (GLint)num_exts;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetIntegerv(pname, data);
+}
+
+void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = (GLint64)num_exts;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetInteger64v(pname, data);
+}
+
+struct implementation_map_t {
+ const char* name;
+ EGLFuncPointer address;
+};
+
+static const implementation_map_t sPlatformImplMap[] = {
+ // clang-format off
+ { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
+ { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
+ { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
+ { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl },
+ { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl },
+ { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl },
+ { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl },
+ { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl },
+ { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl },
+ { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl },
+ { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl },
+ { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl },
+ { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl },
+ { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl },
+ { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl },
+ { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl },
+ { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl },
+ { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl },
+ { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl },
+ { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl },
+ { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl },
+ { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl },
+ { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl },
+ { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl },
+ { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl },
+ { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl },
+ { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl },
+ { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl },
+ { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl },
+ { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl },
+ { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl },
+ { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl },
+ { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl },
+ { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl },
+ { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl },
+ { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl },
+ { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl },
+ { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl },
+ { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl },
+ { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl },
+ { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl },
+ { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl },
+ { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl },
+ { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl },
+ { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl },
+ { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl },
+ { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl },
+ { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl },
+ { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl },
+ { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl },
+ { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl },
+ { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl },
+ { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl },
+ { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl },
+ { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl },
+ { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl },
+ { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl },
+ { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl },
+ { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl },
+ { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl },
+ { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl },
+ { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl },
+ { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl },
+ { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl },
+ { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl },
+ { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl },
+ { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl },
+ { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl },
+ { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl },
+ { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl },
+ { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl },
+ { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl },
+ { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl },
+ { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl },
+ { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl },
+ { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl },
+ { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl },
+ { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl },
+ { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl },
+ { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl },
+ { "glGetString", (EGLFuncPointer)&glGetStringImpl },
+ { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl },
+ { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl },
+ { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
+ { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
+ { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
+ // clang-format on
+};
+
+EGLFuncPointer FindPlatformImplAddr(const char* name)
+{
+ static const bool DEBUG = false;
+
+ if (name == nullptr) {
+ ALOGV("FindPlatformImplAddr called with null name");
+ return nullptr;
+ }
+
+ for (int i = 0; i < NELEM(sPlatformImplMap); i++) {
+ if (sPlatformImplMap[i].name == nullptr) {
+ ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name);
+ return nullptr;
+ }
+ if (!strcmp(name, sPlatformImplMap[i].name)) {
+ ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+ return sPlatformImplMap[i].address;
+ }
+ }
+
+ ALOGV("FindPlatformImplAddr did not find an entry for %s", name);
+ return nullptr;
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/opengl/libs/EGL/egl_platform_entries.h
similarity index 69%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to opengl/libs/EGL/egl_platform_entries.h
index e6ac6bf..85b1db3 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.h
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#ifndef ANDROID_EGLAPI_H
+#define ANDROID_EGLAPI_H
+
+#include <EGL/egl.h>
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
namespace android {
-namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+EGLint eglGetErrorImpl();
+EGLFuncPointer FindPlatformImplAddr(const char* name);
-} // namespace mock
-} // namespace android
+}; // namespace android
+
+#endif // ANDROID_EGLAPI_H
+
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 8508c5f..aaecb62 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -21,6 +21,7 @@
#include <cutils/properties.h>
#include <log/log.h>
#include "CallStack.h"
+#include "egl_platform_entries.h"
namespace android {
@@ -28,7 +29,7 @@
pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
egl_tls_t::egl_tls_t()
- : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(true) {
+ : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {
}
const char *egl_tls_t::egl_strerror(EGLint err) {
@@ -55,13 +56,38 @@
void egl_tls_t::validateTLSKey()
{
struct TlsKeyInitializer {
- static void create() {
- pthread_key_create(&sKey, (void (*)(void*))&eglReleaseThread);
- }
+ static void create() { pthread_key_create(&sKey, destructTLSData); }
};
pthread_once(&sOnceKey, TlsKeyInitializer::create);
}
+void egl_tls_t::destructTLSData(void* data) {
+ egl_tls_t* tls = static_cast<egl_tls_t*>(data);
+ if (!tls) return;
+
+ // Several things in the call tree of eglReleaseThread expect to be able to get the current
+ // thread state directly from TLS. That's a problem because Bionic has already cleared our
+ // TLS pointer before calling this function (pthread_getspecific(sKey) will return nullptr).
+ // Instead the data is passed as our parameter.
+ //
+ // Ideally we'd refactor this so we have thin wrappers that retrieve thread state from TLS and
+ // then pass it as a parameter (or 'this' pointer) to functions that do the real work without
+ // touching TLS. Then from here we could just call those implementation functions with the the
+ // TLS data we just received as a parameter.
+ //
+ // But that's a fairly invasive refactoring, so to do this robustly in the short term we just
+ // put the data *back* in TLS and call the top-level eglReleaseThread. It and it's call tree
+ // will retrieve the value from TLS, and then finally clear the TLS data. Bionic explicitly
+ // tolerates re-setting the value that it's currently trying to destruct (see
+ // pthread_key_clean_all()). Even if we forgot to clear the restored TLS data, bionic would
+ // call the destructor again, but eventually gives up and just leaks the data rather than
+ // enter an infinite loop.
+ pthread_setspecific(sKey, tls);
+ eglReleaseThread();
+ ALOGE_IF(pthread_getspecific(sKey) != nullptr,
+ "EGL TLS data still exists after eglReleaseThread");
+}
+
void egl_tls_t::setErrorEtcImpl(
const char* caller, int line, EGLint error, bool quiet) {
validateTLSKey();
@@ -92,7 +118,7 @@
egl_tls_t* egl_tls_t::getTLS() {
egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
- if (tls == 0) {
+ if (tls == nullptr) {
tls = new egl_tls_t;
pthread_setspecific(sKey, tls);
}
@@ -103,7 +129,7 @@
if (sKey != TLS_KEY_NOT_INITIALIZED) {
egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey);
if (tls) {
- pthread_setspecific(sKey, 0);
+ pthread_setspecific(sKey, nullptr);
delete tls;
}
}
@@ -112,7 +138,7 @@
void egl_tls_t::clearError() {
// This must clear the error from all the underlying EGL implementations as
// well as the EGL wrapper layer.
- eglGetError();
+ android::eglGetErrorImpl();
}
EGLint egl_tls_t::getError() {
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 9feae68..86a375c 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -38,6 +38,7 @@
egl_tls_t();
static void validateTLSKey();
+ static void destructTLSData(void* data);
static void setErrorEtcImpl(
const char* caller, int line, EGLint error, bool quiet);
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 9858276..9e112cc 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -18,9 +18,13 @@
#define ANDROID_EGLDEFS_H
#include "../hooks.h"
+#include "egl_platform_entries.h"
+
+#include <log/log.h>
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
+#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
// ----------------------------------------------------------------------------
namespace android {
@@ -31,23 +35,54 @@
// ----------------------------------------------------------------------------
+extern char const * const platform_names[];
+
+// clang-format off
struct egl_connection_t {
enum {
GLESv1_INDEX = 0,
GLESv2_INDEX = 1
};
- inline egl_connection_t() : dso(0) { }
+ inline egl_connection_t() : dso(nullptr) {
+
+ char const* const* entries = platform_names;
+ EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
+ while (*entries) {
+ const char* name = *entries;
+ EGLFuncPointer f = FindPlatformImplAddr(name);
+
+ if (f == nullptr) {
+ // If no entry found, update the lookup table: sPlatformImplMap
+ ALOGE("No entry found in platform lookup table for %s", name);
+ }
+
+ *curr++ = f;
+ entries++;
+ }
+ }
+
void * dso;
gl_hooks_t * hooks[2];
EGLint major;
EGLint minor;
+ EGLint driverVersion;
egl_t egl;
+ // Functions implemented or redirected by platform libraries
+ platform_impl_t platform;
+
void* libEgl;
void* libGles1;
void* libGles2;
+
+ bool shouldUseAngle; // Should we attempt to load ANGLE
+ bool angleDecided; // Have we tried to load ANGLE
+ bool useAngle; // Was ANGLE successfully loaded
+ EGLint angleBackend;
+ void* vendorEGL;
};
+// clang-format on
// ----------------------------------------------------------------------------
@@ -58,6 +93,7 @@
extern "C" void gl_noop();
extern char const * const gl_names[];
+extern char const * const gl_names_1[];
extern char const * const egl_names[];
extern egl_connection_t gEGLImpl;
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index f7fde96..65f50f5 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -301,71 +301,31 @@
}
const GLubyte * glGetString(GLenum name) {
- const GLubyte * ret = egl_get_string_for_current_context(name);
- if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetString(name);
- }
- return ret;
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetString(name);
}
const GLubyte * glGetStringi(GLenum name, GLuint index) {
- const GLubyte * ret = egl_get_string_for_current_context(name, index);
- if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetStringi(name, index);
- }
- return ret;
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetStringi(name, index);
}
void glGetBooleanv(GLenum pname, GLboolean * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetBooleanv(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetBooleanv(pname, data);
}
void glGetFloatv(GLenum pname, GLfloat * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = (GLfloat)num_exts;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetFloatv(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetFloatv(pname, data);
}
void glGetIntegerv(GLenum pname, GLint * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = (GLint)num_exts;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetIntegerv(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetIntegerv(pname, data);
}
void glGetInteger64v(GLenum pname, GLint64 * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = (GLint64)num_exts;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetInteger64v(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetInteger64v(pname, data);
}
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a8855ef..0af0501 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -21,15 +21,18 @@
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
+#include "EGL/egldefs.h"
#include "hooks.h"
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
+
EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
EGLAPI GLint egl_get_num_extensions_for_current_context();
+EGLAPI egl_connection_t* egl_get_connection();
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libs/entries_gles1.in b/opengl/libs/entries_gles1.in
new file mode 100644
index 0000000..c9be593
--- /dev/null
+++ b/opengl/libs/entries_gles1.in
@@ -0,0 +1,298 @@
+GL_ENTRY(void, glActiveTexture, GLenum texture)
+GL_ENTRY(void, glAlphaFunc, GLenum func, GLfloat ref)
+GL_ENTRY(void, glAlphaFuncx, GLenum func, GLfixed ref)
+GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLfixed ref)
+GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer)
+GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer)
+GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer)
+GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture)
+GL_ENTRY(void, glBindVertexArrayOES, GLuint array)
+GL_ENTRY(void, glBlendEquationOES, GLenum mode)
+GL_ENTRY(void, glBlendEquationSeparateOES, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor)
+GL_ENTRY(void, glBlendFuncSeparateOES, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage)
+GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data)
+GL_ENTRY(GLenum, glCheckFramebufferStatusOES, GLenum target)
+GL_ENTRY(void, glClear, GLbitfield mask)
+GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+GL_ENTRY(void, glClearColorx, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glClearColorxOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glClearDepthf, GLfloat d)
+GL_ENTRY(void, glClearDepthfOES, GLclampf depth)
+GL_ENTRY(void, glClearDepthx, GLfixed depth)
+GL_ENTRY(void, glClearDepthxOES, GLfixed depth)
+GL_ENTRY(void, glClearStencil, GLint s)
+GL_ENTRY(void, glClientActiveTexture, GLenum texture)
+GL_ENTRY(GLenum, glClientWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout)
+GL_ENTRY(void, glClipPlanef, GLenum p, const GLfloat *eqn)
+GL_ENTRY(void, glClipPlanefIMG, GLenum p, const GLfloat *eqn)
+GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation)
+GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed *equation)
+GL_ENTRY(void, glClipPlanexIMG, GLenum p, const GLfixed *eqn)
+GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed *equation)
+GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)
+GL_ENTRY(void, glColor4x, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glColor4xOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha)
+GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
+GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glCopyTextureLevelsAPPLE, GLuint destinationTexture, GLuint sourceTexture, GLint sourceBaseLevel, GLsizei sourceLevelCount)
+GL_ENTRY(void, glCullFace, GLenum mode)
+GL_ENTRY(void, glCurrentPaletteMatrixOES, GLuint matrixpaletteindex)
+GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers)
+GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences)
+GL_ENTRY(void, glDeleteFramebuffersOES, GLsizei n, const GLuint *framebuffers)
+GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint *renderbuffers)
+GL_ENTRY(void, glDeleteSyncAPPLE, GLsync sync)
+GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures)
+GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays)
+GL_ENTRY(void, glDepthFunc, GLenum func)
+GL_ENTRY(void, glDepthMask, GLboolean flag)
+GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f)
+GL_ENTRY(void, glDepthRangefOES, GLclampf n, GLclampf f)
+GL_ENTRY(void, glDepthRangex, GLfixed n, GLfixed f)
+GL_ENTRY(void, glDepthRangexOES, GLfixed n, GLfixed f)
+GL_ENTRY(void, glDisable, GLenum cap)
+GL_ENTRY(void, glDisableClientState, GLenum array)
+GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl)
+GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments)
+GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count)
+GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices)
+GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height)
+GL_ENTRY(void, glDrawTexfvOES, const GLfloat *coords)
+GL_ENTRY(void, glDrawTexiOES, GLint x, GLint y, GLint z, GLint width, GLint height)
+GL_ENTRY(void, glDrawTexivOES, const GLint *coords)
+GL_ENTRY(void, glDrawTexsOES, GLshort x, GLshort y, GLshort z, GLshort width, GLshort height)
+GL_ENTRY(void, glDrawTexsvOES, const GLshort *coords)
+GL_ENTRY(void, glDrawTexxOES, GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height)
+GL_ENTRY(void, glDrawTexxvOES, const GLfixed *coords)
+GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glEnable, GLenum cap)
+GL_ENTRY(void, glEnableClientState, GLenum array)
+GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl)
+GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask)
+GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, void **params)
+GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers)
+GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers)
+GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length)
+GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms)
+GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers)
+GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders)
+GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params)
+GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, void *texels)
+GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures)
+GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program)
+GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(GLsync, glFenceSyncAPPLE, GLenum condition, GLbitfield flags)
+GL_ENTRY(void, glFinish, void)
+GL_ENTRY(void, glFinishFenceNV, GLuint fence)
+GL_ENTRY(void, glFlush, void)
+GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length)
+GL_ENTRY(void, glFogf, GLenum pname, GLfloat param)
+GL_ENTRY(void, glFogfv, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glFogx, GLenum pname, GLfixed param)
+GL_ENTRY(void, glFogxOES, GLenum pname, GLfixed param)
+GL_ENTRY(void, glFogxv, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
+GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
+GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+GL_ENTRY(void, glFrontFace, GLenum mode)
+GL_ENTRY(void, glFrustumf, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glFrustumfOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glFrustumx, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glFrustumxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers)
+GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences)
+GL_ENTRY(void, glGenFramebuffersOES, GLsizei n, GLuint *framebuffers)
+GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint *renderbuffers)
+GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures)
+GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays)
+GL_ENTRY(void, glGenerateMipmapOES, GLenum target)
+GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data)
+GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params)
+GL_ENTRY(void, glGetClipPlanef, GLenum plane, GLfloat *equation)
+GL_ENTRY(void, glGetClipPlanefOES, GLenum plane, GLfloat *equation)
+GL_ENTRY(void, glGetClipPlanex, GLenum plane, GLfixed *equation)
+GL_ENTRY(void, glGetClipPlanexOES, GLenum plane, GLfixed *equation)
+GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString)
+GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls)
+GL_ENTRY(GLenum, glGetError, void)
+GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetFixedv, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetFixedvOES, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data)
+GL_ENTRY(void, glGetFramebufferAttachmentParameterivOES, GLenum target, GLenum attachment, GLenum pname, GLint *params)
+GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void)
+GL_ENTRY(void, glGetInteger64vAPPLE, GLenum pname, GLint64 *params)
+GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data)
+GL_ENTRY(void, glGetLightfv, GLenum light, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetLightxv, GLenum light, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetLightxvOES, GLenum light, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetPointerv, GLenum pname, void **params)
+GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(const GLubyte *, glGetString, GLenum name)
+GL_ENTRY(void, glGetSyncivAPPLE, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values)
+GL_ENTRY(void, glGetTexEnvfv, GLenum target, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexEnviv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexEnvxv, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexEnvxvOES, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexGenfvOES, GLenum coord, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexGenivOES, GLenum coord, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexGenxvOES, GLenum coord, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params)
+GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params)
+GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params)
+GL_ENTRY(void, glHint, GLenum target, GLenum mode)
+GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker)
+GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer)
+GL_ENTRY(GLboolean, glIsEnabled, GLenum cap)
+GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence)
+GL_ENTRY(GLboolean, glIsFramebufferOES, GLuint framebuffer)
+GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer)
+GL_ENTRY(GLboolean, glIsSyncAPPLE, GLsync sync)
+GL_ENTRY(GLboolean, glIsTexture, GLuint texture)
+GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array)
+GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param)
+GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightModelxOES, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightModelxv, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glLightModelxvOES, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glLightf, GLenum light, GLenum pname, GLfloat param)
+GL_ENTRY(void, glLightfv, GLenum light, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glLightx, GLenum light, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightxOES, GLenum light, GLenum pname, GLfixed param)
+GL_ENTRY(void, glLightxv, GLenum light, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glLightxvOES, GLenum light, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glLineWidth, GLfloat width)
+GL_ENTRY(void, glLineWidthx, GLfixed width)
+GL_ENTRY(void, glLineWidthxOES, GLfixed width)
+GL_ENTRY(void, glLoadIdentity, void)
+GL_ENTRY(void, glLoadMatrixf, const GLfloat *m)
+GL_ENTRY(void, glLoadMatrixx, const GLfixed *m)
+GL_ENTRY(void, glLoadMatrixxOES, const GLfixed *m)
+GL_ENTRY(void, glLoadPaletteFromModelViewMatrixOES, void)
+GL_ENTRY(void, glLogicOp, GLenum opcode)
+GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access)
+GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
+GL_ENTRY(void, glMaterialf, GLenum face, GLenum pname, GLfloat param)
+GL_ENTRY(void, glMaterialfv, GLenum face, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glMaterialx, GLenum face, GLenum pname, GLfixed param)
+GL_ENTRY(void, glMaterialxOES, GLenum face, GLenum pname, GLfixed param)
+GL_ENTRY(void, glMaterialxv, GLenum face, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glMaterialxvOES, GLenum face, GLenum pname, const GLfixed *param)
+GL_ENTRY(void, glMatrixIndexPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glMatrixMode, GLenum mode)
+GL_ENTRY(void, glMultMatrixf, const GLfloat *m)
+GL_ENTRY(void, glMultMatrixx, const GLfixed *m)
+GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m)
+GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
+GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount)
+GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
+GL_ENTRY(void, glMultiTexCoord4x, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
+GL_ENTRY(void, glMultiTexCoord4xOES, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
+GL_ENTRY(void, glNormal3f, GLfloat nx, GLfloat ny, GLfloat nz)
+GL_ENTRY(void, glNormal3x, GLfixed nx, GLfixed ny, GLfixed nz)
+GL_ENTRY(void, glNormal3xOES, GLfixed nx, GLfixed ny, GLfixed nz)
+GL_ENTRY(void, glNormalPointer, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glOrthof, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glOrthofOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f)
+GL_ENTRY(void, glOrthox, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glOrthoxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f)
+GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param)
+GL_ENTRY(void, glPointParameterf, GLenum pname, GLfloat param)
+GL_ENTRY(void, glPointParameterfv, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glPointParameterx, GLenum pname, GLfixed param)
+GL_ENTRY(void, glPointParameterxOES, GLenum pname, GLfixed param)
+GL_ENTRY(void, glPointParameterxv, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glPointParameterxvOES, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glPointSize, GLfloat size)
+GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glPointSizex, GLfixed size)
+GL_ENTRY(void, glPointSizexOES, GLfixed size)
+GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units)
+GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units)
+GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units)
+GL_ENTRY(void, glPopGroupMarkerEXT, void)
+GL_ENTRY(void, glPopMatrix, void)
+GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker)
+GL_ENTRY(void, glPushMatrix, void)
+GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed *mantissa, GLint *exponent)
+GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)
+GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data)
+GL_ENTRY(void, glRenderbufferStorageMultisampleAPPLE, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glResolveMultisampleFramebufferAPPLE, void)
+GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glRotatexOES, GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert)
+GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert)
+GL_ENTRY(void, glSampleCoveragexOES, GLclampx value, GLboolean invert)
+GL_ENTRY(void, glScalef, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glScalex, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glScalexOES, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition)
+GL_ENTRY(void, glShadeModel, GLenum mode)
+GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask)
+GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilMask, GLuint mask)
+GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass)
+GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence)
+GL_ENTRY(void, glTexCoordPointer, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glTexEnvf, GLenum target, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexEnvfv, GLenum target, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexEnvi, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glTexEnviv, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexEnvx, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexEnvxOES, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexEnvxv, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexEnvxvOES, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexGenfOES, GLenum coord, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexGenfvOES, GLenum coord, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param)
+GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexParameterx, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param)
+GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params)
+GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
+GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
+GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z)
+GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target)
+GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout)
+GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer)
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 81dbe0e..63a0e14 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -59,6 +59,10 @@
#define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
#define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
+struct platform_impl_t {
+ #include "platform_entries.in"
+};
+
struct egl_t {
#include "EGL/egl_entries.in"
};
diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt
index fa26e33..b2d7957 100644
--- a/opengl/libs/libEGL.map.txt
+++ b/opengl/libs/libEGL.map.txt
@@ -3,23 +3,30 @@
eglBindAPI;
eglBindTexImage;
eglChooseConfig;
+ eglClientWaitSync; # introduced=29
eglClientWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
eglCopyBuffers;
eglCreateContext;
+ eglCreateImage; # introduced=29
eglCreateImageKHR;
eglCreateNativeClientBufferANDROID; # introduced=24
eglCreatePbufferFromClientBuffer;
eglCreatePbufferSurface;
eglCreatePixmapSurface;
+ eglCreatePlatformPixmapSurface; # introduced=29
+ eglCreatePlatformWindowSurface; # introduced=29
eglCreateStreamFromFileDescriptorKHR; # introduced=23
eglCreateStreamKHR; # introduced=23
eglCreateStreamProducerSurfaceKHR; # introduced=23
+ eglCreateSync; # introduced=29
eglCreateSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
eglCreateWindowSurface;
eglDestroyContext;
+ eglDestroyImage; # introduced=29
eglDestroyImageKHR;
eglDestroyStreamKHR; # introduced=23
eglDestroySurface;
+ eglDestroySync; # introduced=29
eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
eglDupNativeFenceFDANDROID; # vndk
eglGetConfigAttrib;
@@ -30,8 +37,10 @@
eglGetDisplay;
eglGetError;
eglGetNativeClientBufferANDROID; # introduced=26
+ eglGetPlatformDisplay; # introduced=29
eglGetProcAddress;
eglGetStreamFileDescriptorKHR; # introduced=23
+ eglGetSyncAttrib; # introduced=29
eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
eglGetSystemTimeFrequencyNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
eglGetSystemTimeNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
@@ -64,6 +73,7 @@
eglWaitClient;
eglWaitGL;
eglWaitNative;
+ eglWaitSync; # introduced=29
eglWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
local:
*;
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
new file mode 100644
index 0000000..4673411
--- /dev/null
+++ b/opengl/libs/platform_entries.in
@@ -0,0 +1,86 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, EGLNativeDisplayType, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
+EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
+EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint*)
+EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint*)
+EGL_ENTRY(void, eglBeginFrame, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
+EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
+EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint*)
+EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
+EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
+EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
+EGL_ENTRY(EGLBoolean, eglWaitGL, void)
+EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
+EGL_ENTRY(EGLint, eglGetError, void)
+EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
+EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
+EGL_ENTRY(const char*, eglQueryStringImplementationANDROID, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
+EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglWaitClient, void)
+EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
+EGL_ENTRY(EGLenum, eglQueryAPI, void)
+EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
+EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
+EGL_ENTRY(EGLSync, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSyncKHR, EGLint, EGLAttrib*)
+EGL_ENTRY(EGLSyncKHR, eglCreateSyncKHR, EGLDisplay, EGLenum, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySyncKHR, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglSignalSyncKHR, EGLDisplay, EGLSyncKHR, EGLenum)
+EGL_ENTRY(EGLint, eglClientWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttribKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLint*)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamKHR, EGLDisplay, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyStreamKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamAttribKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint)
+EGL_ENTRY(EGLBoolean, eglQueryStreamKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamu64KHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamTimeKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLTimeKHR*)
+EGL_ENTRY(EGLSurface, eglCreateStreamProducerSurfaceKHR, EGLDisplay, EGLConfig, EGLStreamKHR, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerGLTextureExternalKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerAcquireKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerReleaseKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR)
+EGL_ENTRY(EGLint, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint)
+EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglPresentationTimeANDROID, EGLDisplay, EGLSurface, EGLnsecsANDROID)
+EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer*)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
+EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetNextFrameIdANDROID, EGLDisplay, EGLSurface, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingANDROID, EGLDisplay, EGLSurface, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampsANDROID, EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+GL_ENTRY(const GLubyte*, glGetString, GLenum)
+GL_ENTRY(const GLubyte*, glGetStringi, GLenum, GLuint)
+GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean*)
+GL_ENTRY(void, glGetFloatv, GLenum, GLfloat*)
+GL_ENTRY(void, glGetIntegerv, GLenum, GLint*)
+GL_ENTRY(void, glGetInteger64v, GLenum, GLint64*)
diff --git a/opengl/libs/tools/genfiles b/opengl/libs/tools/genfiles
deleted file mode 100755
index feef318..0000000
--- a/opengl/libs/tools/genfiles
+++ /dev/null
@@ -1,50 +0,0 @@
-#! /bin/sh
-#
-# Copyright (C) 2008 Google Inc.
-#
-# 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.
-
-# Force a specific locale for sorting to avoid irrelevant differences
-# in the generated files that could hide real differences.
-export LC_ALL=POSIX
-
-./glapigen ../../include/GLES/gl.h > ../GLES_CM/gl_api.in
-./glapigen ../../include/GLES/glext.h > ../GLES_CM/glext_api.in
-./glapigen ../../include/GLES3/gl3.h > ../GLES2/gl2_api.in
-./glapigen ../../include/GLES2/gl2ext.h > ../GLES2/gl2ext_api.in
-
-./glentrygen ../../include/GLES/gl.h > /tmp/gl_entries.in
-./glentrygen ../../include/GLES/glext.h > /tmp/glext_entries.in
-./glentrygen ../../include/GLES3/gl3.h > /tmp/gl2_entries.in
-./glentrygen ../../include/GLES2/gl2ext.h > /tmp/gl2ext_entries.in
-
-# The awk command removes lines with the same function name as an earlier
-# line, even if the rest of the line differs. Although signatures of
-# functions with the same name should be the same, the different versions
-# have some irrelevant whitespace and parameter name differences.
-cat /tmp/gl_entries.in \
- /tmp/glext_entries.in \
- /tmp/gl2_entries.in \
- /tmp/gl2ext_entries.in \
- | sort -t, -k2 \
- | awk -F, '!_[$2]++' \
- > ../entries.in
-
-cat ../../include/GLES/gl.h \
- ../../include/GLES/glext.h \
- ../../include/GLES2/gl2ext.h \
- ../../include/GLES3/gl3.h \
- | ./glenumsgen \
- | sort \
- > ../enums.in
-
diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen
deleted file mode 100755
index 4d8334f..0000000
--- a/opengl/libs/tools/glapigen
+++ /dev/null
@@ -1,76 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright (C) 2008 Google Inc.
-#
-# 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.
-
-use strict;
-
-sub rtrim($)
-{
- my $string = shift;
- $string =~ s/\s+$//;
- return $string;
-}
-
-while (my $line = <>) {
- next if $line =~ /^\//;
- next if $line =~ /^#/;
- next if $line =~ /^\s*$/;
- if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
- next;
- }
- my $type = rtrim($2);
- my $name = $3;
- my $args = $4;
-
- #printf("%s", $line);
-
- my $prefix = "";
- if ($name eq "glGetString") {
- $prefix = "__";
- }
-
- printf("%s API_ENTRY(%s%s)(%s)", $type, $prefix, $name, $args);
-
- printf(" {\n");
- if ($type eq "void") {
- printf(" CALL_GL_API(%s", $name);
- } else {
- printf(" CALL_GL_API_RETURN(%s", $name);
- }
- my @args = split ',', $args;
- my $len = scalar(@args);
- for (my $num = 0; $num < $len; $num++) {
- if ($args[$num] ne "void") {
- print ", ";
- #
- # extract the name from the parameter
- # type name
- # const type *name
- # type *name
- # type name[4]
- #
- if ($args[$num] =~ /(\S+\s)+\**\s*([\w]+)/) {
- printf("%s", $2);
- }
- }
- }
- printf(");\n");
- printf("}\n");
-}
-
-
-
-
-
diff --git a/opengl/libs/tools/glentrygen b/opengl/libs/tools/glentrygen
deleted file mode 100755
index 170f041..0000000
--- a/opengl/libs/tools/glentrygen
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright (C) 2008 Google Inc.
-#
-# 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.
-
-use strict;
-
-sub rtrim($)
-{
- my $string = shift;
- $string =~ s/\s+$//;
- return $string;
-}
-
-while (my $line = <>) {
- next if $line =~ /^\//;
- next if $line =~ /^#/;
- next if $line =~ /^\s*$/;
- if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
- next;
- }
- my $type = rtrim($2);
- my $name = $3;
- my $args = $4;
-
- printf("GL_ENTRY(%s, %s, %s)\n", $type, $name, $args);
-}
diff --git a/opengl/libs/tools/glenumsgen b/opengl/libs/tools/glenumsgen
deleted file mode 100755
index 2ae5fbf..0000000
--- a/opengl/libs/tools/glenumsgen
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /usr/bin/perl
-#
-# Copyright (C) 2010 Google Inc.
-#
-# 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.
-
-use strict;
-
-my %enumHash = ();
-
-while (my $line = <STDIN>) {
- next if $line =~ /^\//;
- # Skip bitfield definitions.
- next if $line =~ /_BIT(\d+_|\s+)/;
- if ($line !~ /^#define\s+(\S+)\s+(0x\S+)/) {
- next;
- }
- my $enumName = $1;
- my $enumValue = $2;
- next if exists($enumHash { $enumValue });
- $enumHash { $enumValue } = $enumName;
- printf("GL_ENUM(%s,%s)\n", $enumValue, $enumName);
-}
-
-
-
-
-
diff --git a/opengl/specs/README b/opengl/specs/README
index fdafb1b..6d597d5 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -19,10 +19,7 @@
0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync)
0x3147 EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target)
-0x3148 EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop)
-0x3149 EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop)
-0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
-0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
+0x3148 - 0x314B previously used by the undocumented, deprecated extension EGL_ANDROID_image_crop
0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
0x314D EGL_GL_COLORSPACE_DEFAULT_EXT (EGL_EXT_image_gl_colorspace)
0x314E - 0x314F (unused)
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 459b135..cca91c3 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -217,6 +217,7 @@
// Test that display-p3 extensions exist
ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
// Use 8-bit to keep forcus on Display-P3 aspect
EGLint attrs[] = {
@@ -289,6 +290,59 @@
EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
}
+TEST_F(EGLTest, EGLDisplayP3Passthrough) {
+ EGLConfig config;
+ EGLBoolean success;
+
+ if (!hasWideColorDisplay) {
+ // skip this test if device does not have wide-color display
+ std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl;
+ return;
+ }
+
+ // Test that display-p3 extensions exist
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
+
+ get8BitConfig(config);
+
+ struct DummyConsumer : public BnConsumerListener {
+ void onFrameAvailable(const BufferItem& /* item */) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+ };
+
+ // Create a EGLSurface
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ consumer->consumerConnect(new DummyConsumer, false);
+ sp<Surface> mSTC = new Surface(producer);
+ sp<ANativeWindow> mANW = mSTC;
+ EGLint winAttrs[] = {
+ // clang-format off
+ EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT,
+ EGL_NONE, EGL_NONE
+ // clang-format on
+ };
+
+ EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+ android_dataspace dataspace =
+ static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(mANW.get()));
+ ASSERT_EQ(dataspace, HAL_DATASPACE_DISPLAY_P3);
+
+ EGLint value;
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, value);
+
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
TEST_F(EGLTest, EGLDisplayP31010102) {
EGLint numConfigs;
EGLConfig config;
@@ -303,6 +357,7 @@
// Test that display-p3 extensions exist
ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
// Use 8-bit to keep forcus on Display-P3 aspect
EGLint attrs[] = {
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index 2b76279..a0bd4e2 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -34,8 +34,12 @@
}
// Get main display parameters.
- sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain);
+ const auto mainDpy = SurfaceComposerClient::getInternalDisplayToken();
+ if (mainDpy == nullptr) {
+ fprintf(stderr, "ERROR: no display\n");
+ return;
+ }
+
DisplayInfo mainDpyInfo;
err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
if (err != NO_ERROR) {
@@ -57,7 +61,7 @@
sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
String8("Benchmark"), width, height,
PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
- if (sc == NULL || !sc->isValid()) {
+ if (sc == nullptr || !sc->isValid()) {
fprintf(stderr, "Failed to create SurfaceControl\n");
return;
}
diff --git a/opengl/tests/lib/glTestLib.cpp b/opengl/tests/lib/glTestLib.cpp
index 213dffd..290d7a0 100644
--- a/opengl/tests/lib/glTestLib.cpp
+++ b/opengl/tests/lib/glTestLib.cpp
@@ -37,7 +37,7 @@
{
const char *v = (const char *) glGetString(s);
- if (v == NULL) {
+ if (v == nullptr) {
testPrintI("GL %s unknown", name);
} else {
testPrintI("GL %s = %s", name, v);
diff --git a/opengl/tests/lib/include/EGLUtils.h b/opengl/tests/lib/include/EGLUtils.h
index 9dc6bcf..cfa378f 100644
--- a/opengl/tests/lib/include/EGLUtils.h
+++ b/opengl/tests/lib/include/EGLUtils.h
@@ -100,11 +100,11 @@
if (!attrs)
return BAD_VALUE;
- if (outConfig == NULL)
+ if (outConfig == nullptr)
return BAD_VALUE;
// Get all the "potential match" configs...
- if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE)
+ if (eglGetConfigs(dpy, nullptr, 0, &numConfigs) == EGL_FALSE)
return BAD_VALUE;
std::vector<EGLConfig> configs(numConfigs);
@@ -113,7 +113,7 @@
}
int i;
- EGLConfig config = NULL;
+ EGLConfig config = nullptr;
for (i=0 ; i<n ; i++) {
EGLint nativeVisualId = 0;
eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId);
@@ -243,7 +243,7 @@
bool EGLUtils::printEGLConfigurations(EGLDisplay dpy, String8& msg) {
EGLint numConfig = 0;
- EGLint returnVal = eglGetConfigs(dpy, NULL, 0, &numConfig);
+ EGLint returnVal = eglGetConfigs(dpy, nullptr, 0, &numConfig);
msg.append(checkEglError("eglGetConfigs", returnVal));
if (!returnVal) {
return false;
@@ -280,6 +280,8 @@
return String8("EGL_GL_COLORSPACE_SRGB_KHR");
case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
return String8("EGL_GL_COLORSPACE_DISPLAY_P3_EXT");
+ case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
+ return String8("EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT");
case EGL_GL_COLORSPACE_LINEAR_KHR:
return String8("EGL_GL_COLORSPACE_LINEAR_KHR");
default:
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index f9e96ea..da9bb49 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -90,9 +90,14 @@
rm src/*.class
+# Add UnsupportedAppUsage.java to known sources.
+mkdir -p out/android/annotation
+cp ../../../../base/core/java/android/annotation/UnsupportedAppUsage.java out/android/annotation
+
pushd out > /dev/null
mkdir classes
javac -d classes android/opengl/EGL14.java \
+ android/opengl/EGL15.java \
android/opengl/EGLExt.java \
com/google/android/gles_jni/GLImpl.java \
javax/microedition/khronos/opengles/GL10.java \
@@ -108,7 +113,8 @@
android/opengl/GLES30.java \
android/opengl/GLES31.java \
android/opengl/GLES31Ext.java \
- android/opengl/GLES32.java
+ android/opengl/GLES32.java \
+ android/annotation/UnsupportedAppUsage.java
popd > /dev/null
JAVA_RESULT=$?
if [ $JAVA_RESULT -ne 0 ]; then
@@ -141,7 +147,7 @@
echo
SAID_PLEASE=1
fi
- echo " cp $2/$3 $1"
+ echo " cp $2/$3 $1/$3"
echo " (cd $1; git add $3)"
KEEP_GENERATED=1
fi
@@ -155,13 +161,13 @@
compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
done
-for x in EGL14 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
+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
done
-for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface
+for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
do
compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
done
diff --git a/opengl/tools/glgen/specs/egl/EGL15.spec b/opengl/tools/glgen/specs/egl/EGL15.spec
new file mode 100644
index 0000000..e0aad30
--- /dev/null
+++ b/opengl/tools/glgen/specs/egl/EGL15.spec
@@ -0,0 +1,14 @@
+EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+// NOTE: native_display isn't actually an EGLAttrib. Using EGLAttrib
+// so that the generate creates mostly correct code (do not want a buffer)
+// have to manually change cast to (void *) in generated code that calls
+// the native function.
+EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image )
diff --git a/opengl/tools/glgen/specs/egl/checks.spec b/opengl/tools/glgen/specs/egl/checks.spec
index ae531ee..e2bae48 100644
--- a/opengl/tools/glgen/specs/egl/checks.spec
+++ b/opengl/tools/glgen/specs/egl/checks.spec
@@ -11,3 +11,5 @@
//STUB function: eglCreatePbufferFromClientBuffer nullAllowed attrib_list sentinel attrib_list EGL_NONE
eglCreateContext sentinel attrib_list EGL_NONE
eglQueryContext check value 1
+//unsupported: eglCreatePlatformPixmapSurface nullAllowed attrib_list sentinel attrib_list EGL_NONE
+eglCreatePlatformPixmapSurface unsupported
diff --git a/opengl/tools/glgen/src/CType.java b/opengl/tools/glgen/src/CType.java
index aba98af..b1f8e05 100644
--- a/opengl/tools/glgen/src/CType.java
+++ b/opengl/tools/glgen/src/CType.java
@@ -57,7 +57,9 @@
if(baseType.equals("EGLContext")
|| baseType.equals("EGLConfig")
|| baseType.equals("EGLSurface")
- || baseType.equals("EGLDisplay")) {
+ || baseType.equals("EGLDisplay")
+ || baseType.equals("EGLImage")
+ || baseType.equals("EGLSync")) {
return true;
}
return false;
diff --git a/opengl/tools/glgen/src/GenerateEGL.java b/opengl/tools/glgen/src/GenerateEGL.java
index 2ef3970..57958c6 100644
--- a/opengl/tools/glgen/src/GenerateEGL.java
+++ b/opengl/tools/glgen/src/GenerateEGL.java
@@ -84,7 +84,7 @@
ParameterChecker checker = new ParameterChecker(checksReader);
- for(String suffix: new String[] {"EGL14", "EGLExt"}) {
+ for(String suffix: new String[] {"EGL14", "EGL15", "EGLExt"}) {
BufferedReader specReader = new BufferedReader(new FileReader(
"specs/egl/" + suffix + ".spec"));
String egljFilename = "android/opengl/" + suffix + ".java";
diff --git a/opengl/tools/glgen/src/JType.java b/opengl/tools/glgen/src/JType.java
index 7f08503..0b4401a 100644
--- a/opengl/tools/glgen/src/JType.java
+++ b/opengl/tools/glgen/src/JType.java
@@ -60,12 +60,16 @@
typeMapping.put(new CType("EGLNativeDisplayType"), new JType("long"));
typeMapping.put(new CType("EGLClientBuffer"), new JType("long"));
typeMapping.put(new CType("EGLnsecsANDROID"), new JType("long"));
+ typeMapping.put(new CType("EGLAttrib"), new JType("long"));
+ typeMapping.put(new CType("EGLTime"), new JType("long"));
// EGL nonprimitive types
typeMapping.put(new CType("EGLConfig"), new JType("EGLConfig", true, false));
typeMapping.put(new CType("EGLContext"), new JType("EGLContext", true, false));
typeMapping.put(new CType("EGLDisplay"), new JType("EGLDisplay", true, false));
typeMapping.put(new CType("EGLSurface"), new JType("EGLSurface", true, false));
+ typeMapping.put(new CType("EGLImage"), new JType("EGLImage", true, false));
+ typeMapping.put(new CType("EGLSync"), new JType("EGLSync", true, false));
// Untyped pointers map to untyped Buffers
@@ -139,6 +143,8 @@
arrayTypeMapping.put(new CType("EGLint", true, true), new JType("int", false, true));
arrayTypeMapping.put(new CType("EGLConfig", false, true), new JType("EGLConfig", true, true));
arrayTypeMapping.put(new CType("EGLConfig", true, true), new JType("EGLConfig", true, true));
+ arrayTypeMapping.put(new CType("EGLAttrib", false, true), new JType("long", false, true));
+ arrayTypeMapping.put(new CType("EGLAttrib", true, true), new JType("long", false, true));
}
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index e8691bb..9c80212 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -103,6 +103,12 @@
if (cfunc.hasEGLHandleArg()) {
return;
}
+ // eglGetPlatformDisplay does not have any EGLHandleArgs
+ // but we do not want to create IOBuffers of this, so
+ // exit
+ if (cfunc.getName().equals("eglGetPlatformDisplay")) {
+ return;
+ }
}
jfunc = JFunc.convert(cfunc, false);
@@ -769,6 +775,19 @@
}
}
+ String getJniDefaultReturn(JType jType) {
+ if (jType.isPrimitive()) {
+ String baseType = jType.getBaseType();
+ if (baseType.equals("boolean")) {
+ return "JNI_FALSE";
+ } else {
+ return "(" + getJniType(jType) + ")0";
+ }
+ } else {
+ return "nullptr";
+ }
+ }
+
String getJniMangledName(String name) {
name = name.replaceAll("_", "_1");
name = name.replaceAll(";", "_2");
@@ -937,15 +956,15 @@
"jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
out.println(indent +
" \"" + cfunc.getName() + "\");");
- if (!isVoid) {
- String retval = getErrorReturnValue(cfunc);
+ if (isVoid) {
+ out.println(indent + "return;");
+ } else {
if (cfunc.getType().isEGLHandle()) {
String baseType = cfunc.getType().getBaseType().toLowerCase();
- out.println(indent +
- "return toEGLHandle(_env, " + baseType + "Class, " +
- baseType + "Constructor, " + retval + ");");
+ out.println(indent + indent + "return nullptr;");
} else {
- out.println(indent + "return " + retval + ";");
+ out.println(indent + indent + "return " +
+ getJniDefaultReturn(jfunc.getType()) + ";");
}
}
out.println("}");
@@ -1589,8 +1608,17 @@
out.println(indent + "if (_exception) {");
out.println(indent + indent +
"jniThrowException(_env, _exceptionType, _exceptionMessage);");
- out.println(indent + "}");
+ if (!isVoid) {
+ if (cfunc.getType().isEGLHandle()) {
+ String baseType = cfunc.getType().getBaseType().toLowerCase();
+ out.println(indent + indent + "return nullptr;");
+ } else {
+ out.println(indent + indent + "return " +
+ getJniDefaultReturn(jfunc.getType()) + ";");
+ }
+ }
+ out.println(indent + "}");
}
diff --git a/opengl/tools/glgen/static/egl/EGLImage.java b/opengl/tools/glgen/static/egl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, 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.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+ private EGLImage(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLImage)) return false;
+
+ EGLImage that = (EGLImage) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/opengl/tools/glgen/static/egl/EGLSync.java b/opengl/tools/glgen/static/egl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, 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.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+ private EGLSync(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLSync)) return false;
+
+ EGLSync that = (EGLSync) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index f3bf220..12728f5 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -18,6 +18,7 @@
package android.opengl;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import android.view.SurfaceView;
diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
index f90e3ec..93203fd 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
@@ -35,8 +35,6 @@
#include <ui/ANativeObjectBase.h>
-static int initialized = 0;
-
static jclass egldisplayClass;
static jclass eglcontextClass;
static jclass eglsurfaceClass;
@@ -107,6 +105,7 @@
if (obj == NULL){
jniThrowException(_env, "java/lang/IllegalArgumentException",
"Object is set to null.");
+ return nullptr;
}
jlong handle = _env->CallLongMethod(obj, mid);
diff --git a/opengl/tools/glgen/stubs/egl/EGL15Header.java-if b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
new file mode 100644
index 0000000..859380f
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
@@ -0,0 +1,78 @@
+/*
+** Copyright 2018, 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.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public final class EGL15 {
+
+ private EGL15() {};
+
+ public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 0x00000001;
+ public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 0x00000002;
+ public static final int EGL_OPENGL_ES3_BIT = 0x00000040;
+ public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 0x0001;
+ public static final int EGL_GL_COLORSPACE_SRGB = 0x3089;
+ public static final int EGL_GL_COLORSPACE_LINEAR = 0x308A;
+ public static final int EGL_CONTEXT_MAJOR_VERSION = 0x3098;
+ public static final int EGL_CL_EVENT_HANDLE = 0x309C;
+ public static final int EGL_GL_COLORSPACE = 0x309D;
+ public static final int EGL_GL_TEXTURE_2D = 0x30B1;
+ public static final int EGL_GL_TEXTURE_3D = 0x30B2;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x30B3;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x30B4;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x30B5;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x30B6;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x30B7;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x30B8;
+ public static final int EGL_GL_RENDERBUFFER = 0x30B9;
+ public static final int EGL_GL_TEXTURE_LEVEL = 0x30BC;
+ public static final int EGL_GL_TEXTURE_ZOFFSET = 0x30BD;
+ public static final int EGL_IMAGE_PRESERVED = 0x30D2;
+ public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 0x30F0;
+ public static final int EGL_SYNC_STATUS = 0x30F1;
+ public static final int EGL_SIGNALED = 0x30F2;
+ public static final int EGL_UNSIGNALED = 0x30F3;
+ public static final int EGL_TIMEOUT_EXPIRED = 0x30F5;
+ public static final int EGL_CONDITION_SATISFIED = 0x30F6;
+ public static final int EGL_SYNC_TYPE = 0x30F7;
+ public static final int EGL_SYNC_CONDITION = 0x30F8;
+ public static final int EGL_SYNC_FENCE = 0x30F9;
+ public static final int EGL_CONTEXT_MINOR_VERSION = 0x30FB;
+ public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 0x30FD;
+ public static final int EGL_SYNC_CL_EVENT = 0x30FE;
+ public static final int EGL_SYNC_CL_EVENT_COMPLETE = 0x30FF;
+ public static final int EGL_CONTEXT_OPENGL_DEBUG = 0x31B0;
+ public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 0x31B1;
+ public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 0x31B2;
+ public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+ public static final int EGL_NO_RESET_NOTIFICATION = 0x31BE;
+ public static final int EGL_LOSE_CONTEXT_ON_RESET = 0x31BF;
+ public static final int EGL_PLATFORM_ANDROID_KHR = 0x3141;
+ public static final long EGL_FOREVER = 0xFFFFFFFFFFFFFFFFL;
+ public static final EGLImage EGL_NO_IMAGE = null;
+ public static final EGLSync EGL_NO_SYNC = null;
+ public static final EGLContext EGL_NO_CONTEXT = null;
+ public static final EGLDisplay EGL_NO_DISPLAY = null;
+ public static final EGLSurface EGL_NO_SURFACE = null;
+
+ native private static void _nativeClassInit();
+ static {
+ _nativeClassInit();
+ }
diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
new file mode 100644
index 0000000..1c53c9e
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -0,0 +1,209 @@
+/*
+** Copyright 2018, 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 source file is automatically generated
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/misc.h>
+
+#include <assert.h>
+#include <EGL/egl.h>
+
+#include <ui/ANativeObjectBase.h>
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
+{
+ // EGL 1.4 Init
+ jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+ eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal);
+ jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+ eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal);
+ jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+ egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal);
+ jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+ eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
+
+ eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+ eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+ egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+ eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+ eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+ eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+ egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+ eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+ jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+ eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+ jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+ eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+ jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
+ eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+ jclass eglClass = _env->FindClass("android/opengl/EGL15");
+ jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+ _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+ jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+ _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+ jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+ _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+ // EGL 1.5 init
+ jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+ nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
+
+ jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+ bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
+
+ getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
+ "getBasePointer", "(Ljava/nio/Buffer;)J");
+ getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
+ "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+ getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
+ "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+ positionID = _env->GetFieldID(bufferClass, "position", "I");
+ limitID = _env->GetFieldID(bufferClass, "limit", "I");
+ elementSizeShiftID =
+ _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+ jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+ eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal);
+ jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+ eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
+
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+ eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+ jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+ _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+ jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+ _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *
+getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset)
+{
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+
+ position = _env->GetIntField(buffer, positionID);
+ limit = _env->GetIntField(buffer, limitID);
+ elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+ *remaining = (limit - position) << elementSizeShift;
+ pointer = _env->CallStaticLongMethod(nioAccessClass,
+ getBasePointerID, buffer);
+ if (pointer != 0L) {
+ *array = NULL;
+ return reinterpret_cast<void*>(pointer);
+ }
+
+ *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
+ getBaseArrayID, buffer);
+ *offset = _env->CallStaticIntMethod(nioAccessClass,
+ getBaseArrayOffsetID, buffer);
+
+ return NULL;
+}
+
+static void
+releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
+{
+ _env->ReleasePrimitiveArrayCritical(array, data,
+ commit ? 0 : JNI_ABORT);
+}
+
+static void *
+fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+ if (obj == NULL) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Object is set to null.");
+ return nullptr;
+ }
+
+ jlong handle = _env->CallLongMethod(obj, mid);
+ return reinterpret_cast<void*>(handle);
+}
+
+static jobject
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) {
+ if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) {
+ return eglNoImageObject;
+ }
+
+ return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
index 12b96f4..b3b0690 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
@@ -36,8 +36,6 @@
#include <ui/ANativeObjectBase.h>
-static int initialized = 0;
-
static jclass egldisplayClass;
static jclass eglcontextClass;
static jclass eglsurfaceClass;
@@ -104,6 +102,7 @@
if (obj == NULL){
jniThrowException(_env, "java/lang/IllegalArgumentException",
"Object is set to null.");
+ return nullptr;
}
return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid));
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp
index 497d284..f229860 100755
--- a/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp
@@ -54,6 +54,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return nullptr;
}
return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
}
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
index 3eacf3c..2e146a8 100644
--- a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
@@ -4,6 +4,6 @@
(JNIEnv *_env, jobject _this, jobject dpy, jobject config, jint pixmap, jintArray attrib_list_ref, jint offset) {
jniThrowException(_env, "java/lang/UnsupportedOperationException",
"eglCreatePixmapSurface");
- return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
+ return nullptr;
}
diff --git a/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp
index 355c4b0..7c255ed 100644
--- a/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp
+++ b/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp
@@ -67,6 +67,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return nullptr;
}
return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
}
@@ -149,6 +150,7 @@
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return nullptr;
}
return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
}
diff --git a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java
index 7532abf..85f743d 100755
--- a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java
+++ b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java
@@ -7,6 +7,7 @@
/**
* {@hide}
*/
+ @UnsupportedAppUsage
public static native EGLDisplay eglGetDisplay(
long display_id
);
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
new file mode 100644
index 0000000..3a6176f
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
@@ -0,0 +1,47 @@
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+ (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLDisplay _returnValue = (EGLDisplay) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ _returnValue = eglGetPlatformDisplay(
+ (EGLenum)platform,
+ (void *)native_display,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return nullptr;
+ }
+ return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
new file mode 100644
index 0000000..28945e8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
@@ -0,0 +1,9 @@
+ // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+ public static native EGLDisplay eglGetPlatformDisplay(
+ int platform,
+ long native_display,
+ long[] attrib_list,
+ int offset
+ );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
new file mode 100644
index 0000000..8a309bf
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
@@ -0,0 +1 @@
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
index 9ce6728..c2711aa 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
+++ b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
@@ -19,6 +19,8 @@
package android.opengl;
+import android.annotation.UnsupportedAppUsage;
+
/** OpenGL ES 2.0
*/
public class GLES20 {
diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp
index 2163d76..51e62ed 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -4,8 +4,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
diff --git a/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java b/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java
index d66200f..b297b7a 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java
+++ b/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java
@@ -17,6 +17,7 @@
// C function void glGetActiveAttrib ( GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, char *name )
/** @hide Method is broken, but used to be public (b/6006380) */
+ @UnsupportedAppUsage
public static native void glGetActiveAttrib(
int program,
int index,
diff --git a/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java b/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java
index 8c8d5a2..f211440 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java
+++ b/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java
@@ -17,6 +17,7 @@
// C function void glGetActiveUniform ( GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, char *name )
/** @hide Method is broken, but used to be public (b/6006380) */
+ @UnsupportedAppUsage
public static native void glGetActiveUniform(
int program,
int index,
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index 29296ff..c808fe9 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -64,8 +64,6 @@
GLsizei stride, const GLvoid *pointer, GLsizei count);
}
-static int initialized = 0;
-
static jclass nioAccessClass;
static jclass bufferClass;
static jclass G11ImplClass;
diff --git a/opengl/tools/glgen2/glgen.py b/opengl/tools/glgen2/glgen.py
index fa981a8..86fa28f 100755
--- a/opengl/tools/glgen2/glgen.py
+++ b/opengl/tools/glgen2/glgen.py
@@ -250,6 +250,27 @@
for opts in TRAMPOLINE_OPTIONS:
registry.apiGen(opts)
+ # Generate a GLESv1_CM entries separately to avoid extra driver loading time
+ apigen = ApiGenerator()
+ registry.setGenerator(apigen)
+ API_OPTIONS = [
+ # Generate non-extension versions of each API first, then extensions,
+ # so that if an extension enum was later standardized, we see the non-
+ # suffixed version first.
+ reg.GeneratorOptions(
+ apiname = 'gles1',
+ profile = 'common'),
+ reg.GeneratorOptions(
+ apiname = 'gles1',
+ profile = 'common',
+ emitversions = None,
+ defaultExtensions = 'gles1')]
+ for opts in API_OPTIONS:
+ registry.apiGen(opts)
+ apigen.finish()
+ with open('../../libs/entries_gles1.in', 'w') as f:
+ apigen.writeEntries(f)
+
apigen = ApiGenerator()
registry.setGenerator(apigen)
API_OPTIONS = [
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index e422e96..8d661ae 100644
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -801,7 +801,9 @@
<enum value="0x3361" name="EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT"/>
<enum value="0x3362" name="EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT"/>
<enum value="0x3363" name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
- <unused start="0x3364" end="0x339F"/>
+ <enum value="0x3364" name="EGL_SYNC_CLIENT_EXT"/>
+ <enum value="0x3365" name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+ <unused start="0x3366" end="0x339F"/>
</enums>
<enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -887,6 +889,15 @@
<enum value="0x3471" name="EGL_IMPORT_IMPLICIT_SYNC_EXT"/>
<enum value="0x3472" name="EGL_IMPORT_EXPLICIT_SYNC_EXT"/>
</enums>
+ <enums namespace="EGL" start="0x3480" end="0x348F" vendor="ANGLE" comment="Reserved for Courtney Goeltzenleuchter - ANGLE (gitlab EGL bug 7)">
+ <enum value="0x3480" name="EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE"/>
+ <unused start="0x3481" end="0x348F"/>
+ </enums>
+
+ <enums namespace="EGL" start="0x3490" end="0x349F" vendor="EXT" comment="Reserved for Courtney Goeltzenleuchter - Android (gitlab EGL bug 69)">
+ <enum value="0x3490" name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/>
+ <unused start="0x3491" end="0x349F"/>
+ </enums>
<!-- Please remember that new enumerant allocations must be obtained by
request to the Khronos API registrar (see comments at the top of this
@@ -897,8 +908,8 @@
<!-- Reservable for future use. To generate a new range, allocate multiples
of 16 starting at the lowest available point in this block. -->
- <enums namespace="EGL" start="0x3480" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
- <unused start="0x3480" end="0x3FFF"/>
+ <enums namespace="EGL" start="0x34A0" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
+ <unused start="0x34A0" end="0x3FFF"/>
</enums>
<enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -930,6 +941,12 @@
<param><ptype>EGLint</ptype> *<name>num_config</name></param>
</command>
<command>
+ <proto><ptype>EGLBoolean</ptype> <name>eglClientSignalSyncEXT</name></proto>
+ <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+ <param><ptype>EGLSync</ptype> <name>sync</name></param>
+ <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+ </command>
+ <command>
<proto><ptype>EGLint</ptype> <name>eglClientWaitSync</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
<param><ptype>EGLSync</ptype> <name>sync</name></param>
@@ -1647,6 +1664,11 @@
<param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
</command>
<command>
+ <proto><ptype>EGLBoolean</ptype> <name>eglStreamFlushNV</name></proto>
+ <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+ <param><ptype>EGLStreamKHR</ptype> <name>stream</name></param>
+ </command>
+ <command>
<proto><ptype>EGLBoolean</ptype> <name>eglSurfaceAttrib</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
<param><ptype>EGLSurface</ptype> <name>surface</name></param>
@@ -1701,6 +1723,12 @@
<param><ptype>EGLSurface</ptype> <name>surface</name></param>
</command>
<command>
+ <proto><ptype>EGLBoolean</ptype> <name>eglUnsignalSyncEXT</name></proto>
+ <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+ <param><ptype>EGLSync</ptype> <name>sync</name></param>
+ <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+ </command>
+ <command>
<proto><ptype>EGLBoolean</ptype> <name>eglWaitClient</name></proto>
</command>
<command>
@@ -2146,6 +2174,13 @@
</require>
</extension>
<extension name="EGL_EXT_client_extensions" supported="egl"/>
+ <extension name="EGL_EXT_client_sync" supported="egl">
+ <require>
+ <enum name="EGL_SYNC_CLIENT_EXT"/>
+ <enum name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+ <command name="eglClientSignalSyncEXT"/>
+ </require>
+ </extension>
<extension name="EGL_EXT_create_context_robustness" supported="egl">
<require>
<enum name="EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT"/>
@@ -2220,6 +2255,11 @@
<enum name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
</require>
</extension>
+ <extension name="EGL_EXT_gl_colorspace_display_p3_passthrough" supported="egl">
+ <require>
+ <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/>
+ </require>
+ </extension>
<extension name="EGL_EXT_image_dma_buf_import" supported="egl">
<require>
<enum name="EGL_LINUX_DMA_BUF_EXT"/>
@@ -2371,6 +2411,11 @@
<command name="eglSwapBuffersWithDamageEXT"/>
</require>
</extension>
+ <extension name="EGL_EXT_sync_reuse" supported="egl">
+ <require>
+ <command name="eglUnsignalSyncEXT"/>
+ </require>
+ </extension>
<extension name="EGL_EXT_yuv_surface" supported="egl">
<require>
<enum name="EGL_YUV_ORDER_EXT"/>
@@ -2932,6 +2977,11 @@
<enum name="EGL_STREAM_FIFO_SYNCHRONOUS_NV"/>
</require>
</extension>
+ <extension name="EGL_NV_stream_flush" supported="egl">
+ <require>
+ <command name="eglStreamFlushNV"/>
+ </require>
+ </extension>
<extension name="EGL_NV_stream_frame_limits" supported="egl">
<require>
<enum name="EGL_PRODUCER_MAX_FRAME_HINT_NV"/>
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
new file mode 100644
index 0000000..a4b81fe
--- /dev/null
+++ b/services/bufferhub/Android.bp
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2018 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: "libbufferhubservice",
+ cflags: [
+ "-DLOG_TAG=\"libbufferhubservice\"",
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ srcs: [
+ "BufferClient.cpp",
+ "BufferHubIdGenerator.cpp",
+ "BufferHubService.cpp",
+ "BufferNode.cpp",
+ ],
+ header_libs: [
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libcrypto",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+ export_include_dirs: [
+ "include"
+ ],
+}
+
+cc_binary {
+ name: "android.frameworks.bufferhub@1.0-service",
+ relative_install_path: "hw",
+ srcs: [
+ "main_bufferhub.cpp"
+ ],
+ header_libs: [
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libbufferhubservice",
+ "libcrypto",
+ "libcutils",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"bufferhub\"",
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ init_rc: ["android.frameworks.bufferhub@1.0-service.rc"],
+ vintf_fragments: ["android.frameworks.bufferhub@1.0-service.xml"],
+}
diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
new file mode 100644
index 0000000..ec7e535
--- /dev/null
+++ b/services/bufferhub/BufferClient.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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 <bufferhub/BufferClient.h>
+#include <bufferhub/BufferHubService.h>
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::Void;
+
+BufferClient* BufferClient::create(BufferHubService* service,
+ const std::shared_ptr<BufferNode>& node) {
+ if (!service) {
+ ALOGE("%s: service cannot be nullptr.", __FUNCTION__);
+ return nullptr;
+ } else if (!node) {
+ ALOGE("%s: node cannot be nullptr.", __FUNCTION__);
+ return nullptr;
+ }
+ return new BufferClient(service, node);
+}
+
+BufferClient::~BufferClient() {
+ {
+ std::lock_guard<std::mutex> lock(mClosedMutex);
+ if (!mClosed) {
+ ALOGW("%s: client of buffer #%d destroyed without close. Closing it now.", __FUNCTION__,
+ mBufferNode->id());
+ }
+ }
+
+ close();
+}
+
+Return<BufferHubStatus> BufferClient::close() {
+ std::lock_guard<std::mutex> lock(mClosedMutex);
+ if (mClosed) {
+ return BufferHubStatus::CLIENT_CLOSED;
+ }
+
+ getService()->onClientClosed(this);
+ mBufferNode.reset();
+ mClosed = true;
+ return BufferHubStatus::NO_ERROR;
+}
+
+Return<void> BufferClient::duplicate(duplicate_cb _hidl_cb) {
+ std::lock_guard<std::mutex> lock(mClosedMutex);
+ if (mClosed) {
+ _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::CLIENT_CLOSED);
+ return Void();
+ }
+
+ if (!mBufferNode) {
+ // Should never happen
+ ALOGE("%s: node is missing.", __FUNCTION__);
+ _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::BUFFER_FREED);
+ return Void();
+ }
+
+ const hidl_handle token = getService()->registerToken(this);
+ _hidl_cb(/*token=*/token, /*status=*/BufferHubStatus::NO_ERROR);
+ return Void();
+}
+
+sp<BufferHubService> BufferClient::getService() {
+ sp<BufferHubService> service = mService.promote();
+ if (service == nullptr) {
+ // Should never happen. Kill the process.
+ LOG_FATAL("%s: service died.", __FUNCTION__);
+ }
+
+ return service;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/BufferHubIdGenerator.cpp b/services/bufferhub/BufferHubIdGenerator.cpp
new file mode 100644
index 0000000..2c12f0e
--- /dev/null
+++ b/services/bufferhub/BufferHubIdGenerator.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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 <bufferhub/BufferHubIdGenerator.h>
+#include <log/log.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+BufferHubIdGenerator& BufferHubIdGenerator::getInstance() {
+ static BufferHubIdGenerator generator;
+
+ return generator;
+}
+
+int BufferHubIdGenerator::getId() {
+ std::lock_guard<std::mutex> lock(mIdsInUseMutex);
+
+ do {
+ if (++mLastId >= std::numeric_limits<int>::max()) {
+ mLastId = 0;
+ }
+ } while (mIdsInUse.find(mLastId) != mIdsInUse.end());
+
+ mIdsInUse.insert(mLastId);
+ return mLastId;
+}
+
+void BufferHubIdGenerator::freeId(int id) {
+ std::lock_guard<std::mutex> lock(mIdsInUseMutex);
+ auto iter = mIdsInUse.find(id);
+ if (iter != mIdsInUse.end()) {
+ mIdsInUse.erase(iter);
+ } else {
+ ALOGW("%s: Cannot free nonexistent id #%d", __FUNCTION__, id);
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
new file mode 100644
index 0000000..7a3472f
--- /dev/null
+++ b/services/bufferhub/BufferHubService.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2018 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 <array>
+#include <iomanip>
+#include <random>
+#include <sstream>
+
+#include <android/hardware_buffer.h>
+#include <bufferhub/BufferHubService.h>
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <system/graphics-base.h>
+#include <ui/BufferHubDefs.h>
+
+using ::android::BufferHubDefs::MetadataHeader;
+using ::android::hardware::Void;
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+BufferHubService::BufferHubService() {
+ std::mt19937_64 randomEngine;
+ randomEngine.seed(time(nullptr));
+
+ mKey = randomEngine();
+}
+
+Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& description,
+ const uint32_t userMetadataSize,
+ allocateBuffer_cb _hidl_cb) {
+ AHardwareBuffer_Desc desc;
+ memcpy(&desc, &description, sizeof(AHardwareBuffer_Desc));
+
+ std::shared_ptr<BufferNode> node =
+ std::make_shared<BufferNode>(desc.width, desc.height, desc.layers, desc.format,
+ desc.usage, userMetadataSize,
+ BufferHubIdGenerator::getInstance().getId());
+ if (node == nullptr || !node->isValid()) {
+ ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
+ _hidl_cb(/*status=*/BufferHubStatus::ALLOCATION_FAILED, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ sp<BufferClient> client = BufferClient::create(this, node);
+ // Add it to list for bookkeeping and dumpsys.
+ std::lock_guard<std::mutex> lock(mClientSetMutex);
+ mClientSet.emplace(client);
+
+ // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+ // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+ NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+ BufferHubDefs::kBufferInfoNumInts);
+ hidl_handle bufferInfo =
+ buildBufferInfo(bufferInfoStorage, node->id(), node->addNewActiveClientsBitToMask(),
+ node->userMetadataSize(), node->metadata().ashmemFd(),
+ node->eventFd().get());
+ // During the gralloc allocation carried out by BufferNode, gralloc allocator will populate the
+ // fields of its HardwareBufferDescription (i.e. strides) according to the actual
+ // gralloc implementation. We need to read those fields back and send them to the client via
+ // BufferTraits.
+ HardwareBufferDescription allocatedBufferDesc;
+ memcpy(&allocatedBufferDesc, &node->bufferDesc(), sizeof(AHardwareBuffer_Desc));
+ BufferTraits bufferTraits = {/*bufferDesc=*/allocatedBufferDesc,
+ /*bufferHandle=*/hidl_handle(node->bufferHandle()),
+ /*bufferInfo=*/std::move(bufferInfo)};
+
+ _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
+ /*bufferTraits=*/std::move(bufferTraits));
+ return Void();
+}
+
+Return<void> BufferHubService::importBuffer(const hidl_handle& tokenHandle,
+ importBuffer_cb _hidl_cb) {
+ if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts <= 1) {
+ // nullptr handle or wrong format
+ _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ int tokenId = tokenHandle->data[0];
+
+ wp<BufferClient> originClientWp;
+ {
+ std::lock_guard<std::mutex> lock(mTokenMutex);
+ auto iter = mTokenMap.find(tokenId);
+ if (iter == mTokenMap.end()) {
+ // Token Id not exist
+ ALOGD("%s: token #%d not found.", __FUNCTION__, tokenId);
+ _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ const std::vector<uint8_t>& tokenHMAC = iter->second.first;
+
+ int numIntsForHMAC = (int)ceil(tokenHMAC.size() * sizeof(uint8_t) / (double)sizeof(int));
+ if (tokenHandle->numInts - 1 != numIntsForHMAC) {
+ // HMAC size not match
+ ALOGD("%s: token #%d HMAC size not match. Expected: %d Actual: %d", __FUNCTION__,
+ tokenId, numIntsForHMAC, tokenHandle->numInts - 1);
+ _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ size_t hmacSize = tokenHMAC.size() * sizeof(uint8_t);
+ if (memcmp(tokenHMAC.data(), &tokenHandle->data[1], hmacSize) != 0) {
+ // HMAC not match
+ ALOGD("%s: token #%d HMAC not match.", __FUNCTION__, tokenId);
+ _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ originClientWp = iter->second.second;
+ mTokenMap.erase(iter);
+ }
+
+ // Check if original client is dead
+ sp<BufferClient> originClient = originClientWp.promote();
+ if (!originClient) {
+ // Should not happen since token should be removed if already gone
+ ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
+ _hidl_cb(/*status=*/BufferHubStatus::BUFFER_FREED, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ sp<BufferClient> client = new BufferClient(*originClient);
+ uint32_t clientStateMask = client->getBufferNode()->addNewActiveClientsBitToMask();
+ if (clientStateMask == 0U) {
+ // Reach max client count
+ ALOGE("%s: import failed, BufferNode#%u reached maximum clients.", __FUNCTION__,
+ client->getBufferNode()->id());
+ _hidl_cb(/*status=*/BufferHubStatus::MAX_CLIENT, /*bufferClient=*/nullptr,
+ /*bufferTraits=*/{});
+ return Void();
+ }
+
+ std::lock_guard<std::mutex> lock(mClientSetMutex);
+ mClientSet.emplace(client);
+
+ std::shared_ptr<BufferNode> node = client->getBufferNode();
+
+ HardwareBufferDescription bufferDesc;
+ memcpy(&bufferDesc, &node->bufferDesc(), sizeof(HardwareBufferDescription));
+
+ // Allocate memory for bufferInfo of type hidl_handle on the stack. See
+ // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
+ NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+ BufferHubDefs::kBufferInfoNumInts);
+ hidl_handle bufferInfo = buildBufferInfo(bufferInfoStorage, node->id(), clientStateMask,
+ node->userMetadataSize(), node->metadata().ashmemFd(),
+ node->eventFd().get());
+ BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
+ /*bufferHandle=*/hidl_handle(node->bufferHandle()),
+ /*bufferInfo=*/std::move(bufferInfo)};
+
+ _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
+ /*bufferTraits=*/std::move(bufferTraits));
+ return Void();
+}
+
+Return<void> BufferHubService::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) {
+ if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
+ ALOGE("%s: missing fd for writing.", __FUNCTION__);
+ return Void();
+ }
+
+ FILE* out = fdopen(dup(fd->data[0]), "w");
+
+ if (args.size() != 0) {
+ fprintf(out,
+ "Note: lshal bufferhub currently does not support args. Input arguments are "
+ "ignored.\n");
+ }
+
+ std::ostringstream stream;
+
+ // Get the number of clients of each buffer.
+ // Map from bufferId to bufferNode_clientCount pair.
+ std::map<int, std::pair<const std::shared_ptr<BufferNode>, uint32_t>> clientCount;
+ {
+ std::lock_guard<std::mutex> lock(mClientSetMutex);
+ for (auto iter = mClientSet.begin(); iter != mClientSet.end(); ++iter) {
+ sp<BufferClient> client = iter->promote();
+ if (client != nullptr) {
+ const std::shared_ptr<BufferNode> node = client->getBufferNode();
+ auto mapIter = clientCount.find(node->id());
+ if (mapIter != clientCount.end()) {
+ ++mapIter->second.second;
+ } else {
+ clientCount.emplace(node->id(),
+ std::pair<std::shared_ptr<BufferNode>, uint32_t>(node, 1U));
+ }
+ }
+ }
+ }
+
+ stream << "Active Buffers:\n";
+ stream << std::right;
+ stream << std::setw(6) << "Id";
+ stream << " ";
+ stream << std::setw(9) << "#Clients";
+ stream << " ";
+ stream << std::setw(14) << "Geometry";
+ stream << " ";
+ stream << std::setw(6) << "Format";
+ stream << " ";
+ stream << std::setw(10) << "Usage";
+ stream << " ";
+ stream << std::setw(10) << "State";
+ stream << " ";
+ stream << std::setw(8) << "Index";
+ stream << std::endl;
+
+ for (auto iter = clientCount.begin(); iter != clientCount.end(); ++iter) {
+ const std::shared_ptr<BufferNode> node = std::move(iter->second.first);
+ const uint32_t clientCount = iter->second.second;
+ AHardwareBuffer_Desc desc = node->bufferDesc();
+
+ MetadataHeader* metadataHeader =
+ const_cast<BufferHubMetadata*>(&node->metadata())->metadataHeader();
+ const uint32_t state = metadataHeader->bufferState.load(std::memory_order_acquire);
+ const uint64_t index = metadataHeader->queueIndex;
+
+ stream << std::right;
+ stream << std::setw(6) << /*Id=*/node->id();
+ stream << " ";
+ stream << std::setw(9) << /*#Clients=*/clientCount;
+ stream << " ";
+ if (desc.format == HAL_PIXEL_FORMAT_BLOB) {
+ std::string size = std::to_string(desc.width) + " B";
+ stream << std::setw(14) << /*Geometry=*/size;
+ } else {
+ std::string dimensions = std::to_string(desc.width) + "x" +
+ std::to_string(desc.height) + "x" + std::to_string(desc.layers);
+ stream << std::setw(14) << /*Geometry=*/dimensions;
+ }
+ stream << " ";
+ stream << std::setw(6) << /*Format=*/desc.format;
+ stream << " ";
+ stream << "0x" << std::hex << std::setfill('0');
+ stream << std::setw(8) << /*Usage=*/desc.usage;
+ stream << std::dec << std::setfill(' ');
+ stream << " ";
+ stream << "0x" << std::hex << std::setfill('0');
+ stream << std::setw(8) << /*State=*/state;
+ stream << std::dec << std::setfill(' ');
+ stream << " ";
+ stream << std::setw(8) << /*Index=*/index;
+ stream << std::endl;
+ }
+
+ stream << std::endl;
+
+ // Get the number of tokens of each buffer.
+ // Map from bufferId to tokenCount
+ std::map<int, uint32_t> tokenCount;
+ {
+ std::lock_guard<std::mutex> lock(mTokenMutex);
+ for (auto iter = mTokenMap.begin(); iter != mTokenMap.end(); ++iter) {
+ sp<BufferClient> client = iter->second.second.promote();
+ if (client != nullptr) {
+ const std::shared_ptr<BufferNode> node = client->getBufferNode();
+ auto mapIter = tokenCount.find(node->id());
+ if (mapIter != tokenCount.end()) {
+ ++mapIter->second;
+ } else {
+ tokenCount.emplace(node->id(), 1U);
+ }
+ }
+ }
+ }
+
+ stream << "Unused Tokens:\n";
+ stream << std::right;
+ stream << std::setw(8) << "Buffer Id";
+ stream << " ";
+ stream << std::setw(7) << "#Tokens";
+ stream << std::endl;
+
+ for (auto iter = tokenCount.begin(); iter != tokenCount.end(); ++iter) {
+ stream << std::right;
+ stream << std::setw(8) << /*Buffer Id=*/iter->first;
+ stream << " ";
+ stream << std::setw(7) << /*#Tokens=*/iter->second;
+ stream << std::endl;
+ }
+
+ fprintf(out, "%s", stream.str().c_str());
+
+ fclose(out);
+ return Void();
+}
+
+hidl_handle BufferHubService::registerToken(const wp<BufferClient>& client) {
+ // Find next available token id
+ std::lock_guard<std::mutex> lock(mTokenMutex);
+ do {
+ ++mLastTokenId;
+ } while (mTokenMap.find(mLastTokenId) != mTokenMap.end());
+
+ std::array<uint8_t, EVP_MAX_MD_SIZE> hmac;
+ uint32_t hmacSize = 0U;
+
+ HMAC(/*evp_md=*/EVP_sha256(), /*key=*/&mKey, /*key_len=*/kKeyLen,
+ /*data=*/(uint8_t*)&mLastTokenId, /*data_len=*/mTokenIdSize,
+ /*out=*/hmac.data(), /*out_len=*/&hmacSize);
+
+ int numIntsForHMAC = (int)ceil(hmacSize / (double)sizeof(int));
+ native_handle_t* handle = native_handle_create(/*numFds=*/0, /*numInts=*/1 + numIntsForHMAC);
+ handle->data[0] = mLastTokenId;
+ // Set all the the bits of last int to 0 since it might not be fully overwritten
+ handle->data[numIntsForHMAC] = 0;
+ memcpy(&handle->data[1], hmac.data(), hmacSize);
+
+ // returnToken owns the native_handle_t* thus doing lifecycle management
+ hidl_handle returnToken;
+ returnToken.setTo(handle, /*shoudOwn=*/true);
+
+ std::vector<uint8_t> hmacVec;
+ hmacVec.resize(hmacSize);
+ memcpy(hmacVec.data(), hmac.data(), hmacSize);
+ mTokenMap.emplace(mLastTokenId, std::pair(hmacVec, client));
+
+ return returnToken;
+}
+
+void BufferHubService::onClientClosed(const BufferClient* client) {
+ removeTokenByClient(client);
+
+ std::lock_guard<std::mutex> lock(mClientSetMutex);
+ auto iter = std::find(mClientSet.begin(), mClientSet.end(), client);
+ if (iter != mClientSet.end()) {
+ mClientSet.erase(iter);
+ }
+}
+
+// Implementation of this function should be consistent with the definition of bufferInfo handle in
+// ui/BufferHubDefs.h.
+hidl_handle BufferHubService::buildBufferInfo(char* bufferInfoStorage, int bufferId,
+ uint32_t clientBitMask, uint32_t userMetadataSize,
+ int metadataFd, int eventFd) {
+ native_handle_t* infoHandle =
+ native_handle_init(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
+ BufferHubDefs::kBufferInfoNumInts);
+
+ infoHandle->data[0] = metadataFd;
+ infoHandle->data[1] = eventFd;
+ infoHandle->data[2] = bufferId;
+ // Use memcpy to convert to int without missing digit.
+ // TOOD(b/121345852): use bit_cast to unpack bufferInfo when C++20 becomes available.
+ memcpy(&infoHandle->data[3], &clientBitMask, sizeof(clientBitMask));
+ memcpy(&infoHandle->data[4], &userMetadataSize, sizeof(userMetadataSize));
+
+ hidl_handle bufferInfo;
+ bufferInfo.setTo(infoHandle, /*shouldOwn=*/false);
+
+ return bufferInfo;
+}
+
+void BufferHubService::removeTokenByClient(const BufferClient* client) {
+ std::lock_guard<std::mutex> lock(mTokenMutex);
+ auto iter = mTokenMap.begin();
+ while (iter != mTokenMap.end()) {
+ if (iter->second.second == client) {
+ auto oldIter = iter;
+ ++iter;
+ mTokenMap.erase(oldIter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
new file mode 100644
index 0000000..04ca649
--- /dev/null
+++ b/services/bufferhub/BufferNode.cpp
@@ -0,0 +1,113 @@
+#include <errno.h>
+
+#include <bufferhub/BufferHubService.h>
+#include <bufferhub/BufferNode.h>
+#include <log/log.h>
+#include <ui/GraphicBufferAllocator.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+void BufferNode::initializeMetadata() {
+ // Using placement new here to reuse shared memory instead of new allocation
+ // Initialize the atomic variables to zero.
+ BufferHubDefs::MetadataHeader* metadataHeader = mMetadata.metadataHeader();
+ mBufferState = new (&metadataHeader->bufferState) std::atomic<uint32_t>(0);
+ mFenceState = new (&metadataHeader->fenceState) std::atomic<uint32_t>(0);
+ mActiveClientsBitMask = new (&metadataHeader->activeClientsBitMask) std::atomic<uint32_t>(0);
+ // The C++ standard recommends (but does not require) that lock-free atomic operations are
+ // also address-free, that is, suitable for communication between processes using shared
+ // memory.
+ LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) ||
+ !std::atomic_is_lock_free(mFenceState) ||
+ !std::atomic_is_lock_free(mActiveClientsBitMask),
+ "Atomic variables in ashmen are not lock free.");
+}
+
+// Allocates a new BufferNode.
+BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize, int id)
+ : mId(id) {
+ uint32_t outStride = 0;
+ // graphicBufferId is not used in GraphicBufferAllocator::allocate
+ // TODO(b/112338294) After move to the service folder, stop using the
+ // hardcoded service name "bufferhub".
+ int ret = GraphicBufferAllocator::get().allocate(width, height, format, layerCount, usage,
+ const_cast<const native_handle_t**>(
+ &mBufferHandle),
+ &outStride,
+ /*graphicBufferId=*/0,
+ /*requestor=*/"bufferhub");
+
+ if (ret != OK || mBufferHandle == nullptr) {
+ ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret));
+ return;
+ }
+
+ mBufferDesc.width = width;
+ mBufferDesc.height = height;
+ mBufferDesc.layers = layerCount;
+ mBufferDesc.format = format;
+ mBufferDesc.usage = usage;
+ mBufferDesc.stride = outStride;
+
+ mMetadata = BufferHubMetadata::create(userMetadataSize);
+ if (!mMetadata.isValid()) {
+ ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
+ return;
+ }
+ initializeMetadata();
+}
+
+BufferNode::~BufferNode() {
+ // Free the handle
+ if (mBufferHandle != nullptr) {
+ status_t ret = GraphicBufferAllocator::get().free(mBufferHandle);
+ if (ret != OK) {
+ ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret);
+ }
+ }
+
+ // Free the id, if valid
+ if (mId >= 0) {
+ BufferHubIdGenerator::getInstance().freeId(mId);
+ }
+}
+
+uint32_t BufferNode::getActiveClientsBitMask() const {
+ return mActiveClientsBitMask->load(std::memory_order_acquire);
+}
+
+uint32_t BufferNode::addNewActiveClientsBitToMask() {
+ uint32_t currentActiveClientsBitMask = getActiveClientsBitMask();
+ uint32_t clientStateMask = 0U;
+ uint32_t updatedActiveClientsBitMask = 0U;
+ do {
+ clientStateMask =
+ BufferHubDefs::findNextAvailableClientStateMask(currentActiveClientsBitMask);
+ if (clientStateMask == 0U) {
+ ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__,
+ BufferHubDefs::kMaxNumberOfClients);
+ errno = E2BIG;
+ return 0U;
+ }
+ updatedActiveClientsBitMask = currentActiveClientsBitMask | clientStateMask;
+ } while (!(mActiveClientsBitMask->compare_exchange_weak(currentActiveClientsBitMask,
+ updatedActiveClientsBitMask,
+ std::memory_order_acq_rel,
+ std::memory_order_acquire)));
+ return clientStateMask;
+}
+
+void BufferNode::removeClientsBitFromMask(const uint32_t& value) {
+ mActiveClientsBitMask->fetch_and(~value);
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc
new file mode 100644
index 0000000..36fbede
--- /dev/null
+++ b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc
@@ -0,0 +1,6 @@
+service system_bufferhub /system/bin/hw/android.frameworks.bufferhub@1.0-service
+ class hal animation
+ user system
+ group system graphics
+ onrestart restart surfaceflinger
+ writepid /dev/cpuset/system-background/tasks
diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml b/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml
new file mode 100644
index 0000000..bd958d3
--- /dev/null
+++ b/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.bufferhub</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IBufferHub</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
new file mode 100644
index 0000000..644b403
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
+
+#include <mutex>
+
+#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <bufferhub/BufferNode.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::Return;
+
+// Forward declaration to avoid circular dependency
+class BufferHubService;
+
+class BufferClient : public IBufferClient {
+public:
+ // Creates a server-side buffer client from an existing BufferNode. Note that
+ // this function takes ownership of the shared_ptr.
+ // Returns a raw pointer to the BufferClient on success, nullptr on failure.
+ static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
+
+ // Creates a BufferClient from an existing BufferClient. Will share the same BufferNode.
+ explicit BufferClient(const BufferClient& other)
+ : mService(other.mService), mBufferNode(other.mBufferNode) {}
+ ~BufferClient();
+
+ Return<BufferHubStatus> close() override;
+ Return<void> duplicate(duplicate_cb _hidl_cb) override;
+
+ // Non-binder functions
+ const std::shared_ptr<BufferNode>& getBufferNode() const { return mBufferNode; }
+
+private:
+ BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
+ : mService(service), mBufferNode(node) {}
+
+ sp<BufferHubService> getService();
+
+ wp<BufferHubService> mService;
+
+ std::mutex mClosedMutex;
+ bool mClosed GUARDED_BY(mClosedMutex) = false;
+
+ std::shared_ptr<BufferNode> mBufferNode;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif
diff --git a/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h
new file mode 100644
index 0000000..ef7c077
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
+
+#include <mutex>
+#include <set>
+
+#include <utils/Mutex.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+// A thread-safe, non-negative, incremental, int id generator.
+class BufferHubIdGenerator {
+public:
+ // Get the singleton instance of this class
+ static BufferHubIdGenerator& getInstance();
+
+ // Gets next available id. If next id is greater than std::numeric_limits<int32_t>::max(), it
+ // will try to get an id start from 0 again.
+ int getId();
+
+ // Free a specific id.
+ void freeId(int id);
+
+private:
+ BufferHubIdGenerator() = default;
+ ~BufferHubIdGenerator() = default;
+
+ // Start from -1 so all valid ids will be >= 0
+ int mLastId = -1;
+
+ std::mutex mIdsInUseMutex;
+ std::set<int> mIdsInUse GUARDED_BY(mIdsInUseMutex);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
new file mode 100644
index 0000000..edad20b
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
+
+#include <map>
+#include <mutex>
+#include <set>
+#include <vector>
+
+#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <bufferhub/BufferClient.h>
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <utils/Mutex.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::hidl_string;
+using hardware::hidl_vec;
+using hardware::Return;
+using hardware::graphics::common::V1_2::HardwareBufferDescription;
+
+class BufferHubService : public IBufferHub {
+public:
+ BufferHubService();
+
+ Return<void> allocateBuffer(const HardwareBufferDescription& description,
+ const uint32_t userMetadataSize,
+ allocateBuffer_cb _hidl_cb) override;
+ Return<void> importBuffer(const hidl_handle& tokenHandle, importBuffer_cb _hidl_cb) override;
+
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+ // Non-binder functions
+ // Internal help function for IBufferClient::duplicate.
+ hidl_handle registerToken(const wp<BufferClient>& client);
+
+ void onClientClosed(const BufferClient* client);
+
+private:
+ // Helper function to build BufferTraits.bufferInfo handle
+ hidl_handle buildBufferInfo(char* bufferInfoStorage, int bufferId, uint32_t clientBitMask,
+ uint32_t userMetadataSize, int metadataFd, int eventFd);
+
+ // Helper function to remove all the token belongs to a specific client.
+ void removeTokenByClient(const BufferClient* client);
+
+ // List of active BufferClient for bookkeeping.
+ std::mutex mClientSetMutex;
+ std::set<wp<BufferClient>> mClientSet GUARDED_BY(mClientSetMutex);
+
+ // Token generation related
+ // A random number used as private key for HMAC
+ uint64_t mKey;
+ static constexpr size_t kKeyLen = sizeof(uint64_t);
+
+ std::mutex mTokenMutex;
+ // The first TokenId will be 1. TokenId could be negative.
+ int mLastTokenId GUARDED_BY(mTokenMutex) = 0;
+ static constexpr size_t mTokenIdSize = sizeof(int);
+ // A map from token id to the token-buffer_client pair. Using token id as the key to reduce
+ // looking up time
+ std::map<int, std::pair<std::vector<uint8_t>, const wp<BufferClient>>> mTokenMap
+ GUARDED_BY(mTokenMutex);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
new file mode 100644
index 0000000..62a8d63
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferNode.h
@@ -0,0 +1,100 @@
+#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
+
+#include <android/hardware_buffer.h>
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <cutils/native_handle.h>
+#include <ui/BufferHubEventFd.h>
+#include <ui/BufferHubMetadata.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+class BufferNode {
+public:
+ // Allocates a new BufferNode.
+ BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize, int id = -1);
+
+ ~BufferNode();
+
+ // Returns whether the object holds a valid metadata.
+ bool isValid() const { return mMetadata.isValid(); }
+
+ int id() const { return mId; }
+
+ size_t userMetadataSize() const { return mMetadata.userMetadataSize(); }
+
+ // Accessors of the buffer description and handle
+ const native_handle_t* bufferHandle() const { return mBufferHandle; }
+ const AHardwareBuffer_Desc& bufferDesc() const { return mBufferDesc; }
+
+ // Accessor of event fd.
+ const BufferHubEventFd& eventFd() const { return mEventFd; }
+
+ // Accessors of mMetadata.
+ const BufferHubMetadata& metadata() const { return mMetadata; }
+
+ // Gets the current value of mActiveClientsBitMask in mMetadata with
+ // std::memory_order_acquire, so that all previous releases of
+ // mActiveClientsBitMask from all threads will be returned here.
+ uint32_t getActiveClientsBitMask() const;
+
+ // Find and add a new client state mask to mActiveClientsBitMask in
+ // mMetadata.
+ // Return the new client state mask that is added to mActiveClientsBitMask.
+ // Return 0U if there are already 16 clients of the buffer.
+ uint32_t addNewActiveClientsBitToMask();
+
+ // Removes the value from active_clients_bit_mask in mMetadata with
+ // std::memory_order_release, so that the change will be visible to any
+ // acquire of mActiveClientsBitMask in any threads after the succeed of
+ // this operation.
+ void removeClientsBitFromMask(const uint32_t& value);
+
+private:
+ // Helper method for constructors to initialize atomic metadata header
+ // variables in shared memory.
+ void initializeMetadata();
+
+ // Gralloc buffer handles.
+ native_handle_t* mBufferHandle;
+ AHardwareBuffer_Desc mBufferDesc;
+
+ // Eventfd used for signalling buffer events among the clients of the buffer.
+ BufferHubEventFd mEventFd;
+
+ // Metadata in shared memory.
+ BufferHubMetadata mMetadata;
+
+ // A system-unique id generated by bufferhub from 0 to std::numeric_limits<int>::max().
+ // BufferNodes not created by bufferhub will have id < 0, meaning "not specified".
+ // TODO(b/118891412): remove default id = -1 and update comments after pdx is no longer in use
+ const int mId = -1;
+
+ // The following variables are atomic variables in mMetadata that are visible
+ // to Bn object and Bp objects. Please find more info in
+ // BufferHubDefs::MetadataHeader.
+
+ // mBufferState tracks the state of the buffer. Buffer can be in one of these
+ // four states: gained, posted, acquired, released.
+ std::atomic<uint32_t>* mBufferState = nullptr;
+
+ // TODO(b/112012161): add comments to mFenceState.
+ std::atomic<uint32_t>* mFenceState = nullptr;
+
+ // mActiveClientsBitMask tracks all the bp clients of the buffer. It is the
+ // union of all client_state_mask of all bp clients.
+ std::atomic<uint32_t>* mActiveClientsBitMask = nullptr;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
diff --git a/services/bufferhub/main_bufferhub.cpp b/services/bufferhub/main_bufferhub.cpp
new file mode 100644
index 0000000..084460d
--- /dev/null
+++ b/services/bufferhub/main_bufferhub.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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 <bufferhub/BufferHubService.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
+#include <log/log.h>
+
+using android::sp;
+using android::frameworks::bufferhub::V1_0::IBufferHub;
+using android::frameworks::bufferhub::V1_0::implementation::BufferHubService;
+
+int main(int /*argc*/, char** /*argv*/) {
+ ALOGI("Bootstrap bufferhub HIDL service.");
+
+ android::hardware::configureRpcThreadpool(/*numThreads=*/1, /*willJoin=*/true);
+
+ sp<IBufferHub> service = new BufferHubService();
+ LOG_ALWAYS_FATAL_IF(service->registerAsService() != android::OK, "Failed to register service");
+
+ android::hardware::joinRpcThreadpool();
+
+ return 0;
+}
diff --git a/services/bufferhub/tests/Android.bp b/services/bufferhub/tests/Android.bp
new file mode 100644
index 0000000..3033e70
--- /dev/null
+++ b/services/bufferhub/tests/Android.bp
@@ -0,0 +1,26 @@
+cc_test {
+ name: "BufferHubServer_test",
+ srcs: [
+ "BufferNode_test.cpp",
+ "BufferHubIdGenerator_test.cpp",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"BufferHubServer_test\"",
+ "-DTRACE=0",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ "-Wall",
+ "-Werror",
+ ],
+ compile_multilib: "first",
+ header_libs: [
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ shared_libs: [
+ "libbufferhubservice",
+ "libui",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/bufferhub/tests/BufferHubIdGenerator_test.cpp b/services/bufferhub/tests/BufferHubIdGenerator_test.cpp
new file mode 100644
index 0000000..fb6de0d
--- /dev/null
+++ b/services/bufferhub/tests/BufferHubIdGenerator_test.cpp
@@ -0,0 +1,48 @@
+#include <bufferhub/BufferHubIdGenerator.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+
+class BufferHubIdGeneratorTest : public testing::Test {
+protected:
+ BufferHubIdGenerator* mIdGenerator = &BufferHubIdGenerator::getInstance();
+};
+
+TEST_F(BufferHubIdGeneratorTest, TestGenerateAndFreeID) {
+ int id = mIdGenerator->getId();
+ EXPECT_GE(id, 0);
+
+ mIdGenerator->freeId(id);
+}
+
+TEST_F(BufferHubIdGeneratorTest, TestGenerateUniqueIncrementalID) {
+ // 10 IDs should not overflow the UniqueIdGenerator to cause a roll back to start, so the
+ // resulting IDs should still keep incresing.
+ const int kTestSize = 10;
+ int ids[kTestSize];
+ for (int i = 0; i < kTestSize; ++i) {
+ ids[i] = mIdGenerator->getId();
+ EXPECT_GE(ids[i], 0);
+ if (i >= 1) {
+ EXPECT_GT(ids[i], ids[i - 1]);
+ }
+ }
+
+ for (int i = 0; i < kTestSize; ++i) {
+ mIdGenerator->freeId(ids[i]);
+ }
+}
+
+} // namespace
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/tests/BufferNode_test.cpp b/services/bufferhub/tests/BufferNode_test.cpp
new file mode 100644
index 0000000..2dfd4fc
--- /dev/null
+++ b/services/bufferhub/tests/BufferNode_test.cpp
@@ -0,0 +1,109 @@
+#include <errno.h>
+
+#include <bufferhub/BufferNode.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/BufferHubDefs.h>
+#include <ui/GraphicBufferMapper.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+
+using testing::NotNull;
+
+const uint32_t kWidth = 640;
+const uint32_t kHeight = 480;
+const uint32_t kLayerCount = 1;
+const uint32_t kFormat = 1;
+const uint64_t kUsage = 0;
+const size_t kUserMetadataSize = 0;
+
+class BufferNodeTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mBufferNode =
+ new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+ ASSERT_TRUE(mBufferNode->isValid());
+ }
+
+ void TearDown() override {
+ if (mBufferNode != nullptr) {
+ delete mBufferNode;
+ }
+ }
+
+ BufferNode* mBufferNode = nullptr;
+};
+
+TEST_F(BufferNodeTest, TestCreateBufferNode) {
+ EXPECT_EQ(mBufferNode->userMetadataSize(), kUserMetadataSize);
+ // Test the handle just allocated is good (i.e. able to be imported)
+ GraphicBufferMapper& mapper = GraphicBufferMapper::get();
+ const native_handle_t* outHandle;
+ status_t ret =
+ mapper.importBuffer(mBufferNode->bufferHandle(), mBufferNode->bufferDesc().width,
+ mBufferNode->bufferDesc().height, mBufferNode->bufferDesc().layers,
+ mBufferNode->bufferDesc().format, mBufferNode->bufferDesc().usage,
+ mBufferNode->bufferDesc().stride, &outHandle);
+ EXPECT_EQ(ret, OK);
+ EXPECT_THAT(outHandle, NotNull());
+}
+
+TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_twoNewClients) {
+ uint32_t newClientStateMask1 = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1);
+
+ // Request and add a new client_state_mask again.
+ // Active clients bit mask should be the union of the two new
+ // client_state_masks.
+ uint32_t newClientStateMask2 = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1 | newClientStateMask2);
+}
+
+TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_32NewClients) {
+ uint32_t newClientStateMask = 0U;
+ uint32_t currentMask = 0U;
+ uint32_t expectedMask = 0U;
+
+ for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) {
+ newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_NE(newClientStateMask, 0U);
+ EXPECT_FALSE(newClientStateMask & currentMask);
+ expectedMask = currentMask | newClientStateMask;
+ currentMask = mBufferNode->getActiveClientsBitMask();
+ EXPECT_EQ(currentMask, expectedMask);
+ }
+
+ // Method should fail upon requesting for more than maximum allowable clients.
+ newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(newClientStateMask, 0U);
+ EXPECT_EQ(errno, E2BIG);
+}
+
+TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
+ mBufferNode->addNewActiveClientsBitToMask();
+ uint32_t currentMask = mBufferNode->getActiveClientsBitMask();
+ uint32_t newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_NE(mBufferNode->getActiveClientsBitMask(), currentMask);
+
+ mBufferNode->removeClientsBitFromMask(newClientStateMask);
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
+
+ // Remove the test_mask again to the active client bit mask should not modify
+ // the value of active clients bit mask.
+ mBufferNode->removeClientsBitFromMask(newClientStateMask);
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
+}
+
+} // namespace
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 47bed65..dbb6ba6 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -2,6 +2,7 @@
name: "gpuservice_sources",
srcs: [
"GpuService.cpp",
+ "gpustats/GpuStats.cpp"
],
}
@@ -29,12 +30,16 @@
"frameworks/native/vulkan/include",
],
shared_libs: [
+ "libbase",
"libbinder",
+ "libcutils",
+ "libgraphicsenv",
"liblog",
"libutils",
"libvulkan",
],
static_libs: [
+ "libserviceutils",
"libvkjson",
],
}
@@ -59,6 +64,7 @@
],
shared_libs: [
"libbinder",
+ "libcutils",
"liblog",
"libutils",
],
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 150896c..59fa1c0 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -14,87 +14,129 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "GpuService.h"
+#include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
#include <binder/Parcel.h>
+#include <binder/PermissionCache.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
#include <utils/String8.h>
+#include <utils/Trace.h>
+
#include <vkjson.h>
+#include "gpustats/GpuStats.h"
+
namespace android {
-class BpGpuService : public BpInterface<IGpuService> {
-public:
- explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
-};
-
-IMPLEMENT_META_INTERFACE(GpuService, "android.ui.IGpuService");
-
-status_t BnGpuService::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- status_t status;
- switch (code) {
- case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
- std::vector<String16> args;
- data.readString16Vector(&args);
- sp<IBinder> unusedCallback;
- sp<IResultReceiver> resultReceiver;
- if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK)
- return status;
- if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK)
- return status;
- status = shellCommand(in, out, err, args);
- if (resultReceiver != nullptr)
- resultReceiver->send(status);
- return OK;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
+using base::StringAppendF;
namespace {
- status_t cmd_help(int out);
- status_t cmd_vkjson(int out, int err);
-}
+status_t cmdHelp(int out);
+status_t cmdVkjson(int out, int err);
+void dumpGameDriverInfo(std::string* result);
+} // namespace
+
+const String16 sDump("android.permission.DUMP");
const char* const GpuService::SERVICE_NAME = "gpu";
-GpuService::GpuService() = default;
+GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){};
-status_t GpuService::shellCommand(int /*in*/, int out, int err,
- std::vector<String16>& args) {
- ALOGV("GpuService::shellCommand");
+void GpuService::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, uint64_t driverVersionCode,
+ int64_t driverBuildTime, const std::string& appPackageName,
+ GraphicsEnv::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) {
+ ATRACE_CALL();
+
+ mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
+ appPackageName, driver, isDriverLoaded, driverLoadingTime);
+}
+
+status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
+ ATRACE_CALL();
+
+ mGpuStats->pullGlobalStats(outStats);
+
+ return OK;
+}
+
+status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
+ ATRACE_CALL();
+
+ mGpuStats->pullAppStats(outStats);
+
+ return OK;
+}
+
+status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
+ ATRACE_CALL();
+
+ ALOGV("shellCommand");
for (size_t i = 0, n = args.size(); i < n; i++)
ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string());
if (args.size() >= 1) {
- if (args[0] == String16("vkjson"))
- return cmd_vkjson(out, err);
- if (args[0] == String16("help"))
- return cmd_help(out);
+ if (args[0] == String16("vkjson")) return cmdVkjson(out, err);
+ if (args[0] == String16("help")) return cmdHelp(out);
}
// no command, or unrecognized command
- cmd_help(err);
+ cmdHelp(err);
return BAD_VALUE;
}
+status_t GpuService::doDump(int fd, const Vector<String16>& args, bool /*asProto*/) {
+ std::string result;
+
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+
+ if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
+ StringAppendF(&result, "Permission Denial: can't dump gpu from pid=%d, uid=%d\n", pid, uid);
+ } else {
+ bool dumpAll = true;
+ size_t index = 0;
+ size_t numArgs = args.size();
+
+ if (numArgs) {
+ if ((index < numArgs) && (args[index] == String16("--gpustats"))) {
+ index++;
+ mGpuStats->dump(args, &result);
+ dumpAll = false;
+ }
+ }
+
+ if (dumpAll) {
+ dumpGameDriverInfo(&result);
+ result.append("\n");
+
+ mGpuStats->dump(Vector<String16>(), &result);
+ result.append("\n");
+ }
+ }
+
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
+}
+
namespace {
-status_t cmd_help(int out) {
+status_t cmdHelp(int out) {
FILE* outs = fdopen(out, "w");
if (!outs) {
- ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno),
- errno);
+ ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno), errno);
return BAD_VALUE;
}
fprintf(outs,
- "GPU Service commands:\n"
- " vkjson dump Vulkan properties as JSON\n");
+ "GPU Service commands:\n"
+ " vkjson dump Vulkan properties as JSON\n");
fclose(outs);
return NO_ERROR;
}
@@ -105,7 +147,7 @@
fputc('\n', out);
}
-status_t cmd_vkjson(int out, int /*err*/) {
+status_t cmdVkjson(int out, int /*err*/) {
FILE* outs = fdopen(out, "w");
if (!outs) {
int errnum = errno;
@@ -117,6 +159,18 @@
return NO_ERROR;
}
+void dumpGameDriverInfo(std::string* result) {
+ if (!result) return;
+
+ char stableGameDriver[PROPERTY_VALUE_MAX] = {};
+ property_get("ro.gfx.driver.0", stableGameDriver, "unsupported");
+ StringAppendF(result, "Stable Game Driver: %s\n", stableGameDriver);
+
+ char preReleaseGameDriver[PROPERTY_VALUE_MAX] = {};
+ property_get("ro.gfx.driver.1", preReleaseGameDriver, "unsupported");
+ StringAppendF(result, "Pre-release Game Driver: %s\n", preReleaseGameDriver);
+}
+
} // anonymous namespace
} // namespace android
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index e2b396e..7a9b2d4 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -17,40 +17,61 @@
#ifndef ANDROID_GPUSERVICE_H
#define ANDROID_GPUSERVICE_H
-#include <vector>
-
#include <binder/IInterface.h>
#include <cutils/compiler.h>
+#include <graphicsenv/GpuStatsInfo.h>
+#include <graphicsenv/IGpuService.h>
+#include <serviceutils/PriorityDumper.h>
+
+#include <mutex>
+#include <vector>
namespace android {
-/*
- * This class defines the Binder IPC interface for GPU-related queries and
- * control.
- */
-class IGpuService : public IInterface {
-public:
- DECLARE_META_INTERFACE(GpuService);
-};
+class GpuStats;
-class BnGpuService: public BnInterface<IGpuService> {
-protected:
- virtual status_t shellCommand(int in, int out, int err,
- std::vector<String16>& args) = 0;
-
- status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0) override;
-};
-
-class GpuService : public BnGpuService {
+class GpuService : public BnGpuService, public PriorityDumper {
public:
static const char* const SERVICE_NAME ANDROID_API;
GpuService() ANDROID_API;
protected:
- status_t shellCommand(int in, int out, int err,
- std::vector<String16>& args) override;
+ status_t shellCommand(int in, int out, int err, std::vector<String16>& args) override;
+
+private:
+ /*
+ * IGpuService interface
+ */
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t driverVersionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, GraphicsEnv::Driver driver,
+ bool isDriverLoaded, int64_t driverLoadingTime) override;
+ status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override;
+ status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const override;
+
+ /*
+ * IBinder interface
+ */
+ status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
+
+ /*
+ * Debugging & dumpsys
+ */
+ status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) override {
+ return doDump(fd, Vector<String16>(), asProto);
+ }
+
+ status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) override {
+ return doDump(fd, args, asProto);
+ }
+
+ status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+
+ /*
+ * Attributes
+ */
+ std::unique_ptr<GpuStats> mGpuStats;
};
} // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
new file mode 100644
index 0000000..6185305
--- /dev/null
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright 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.
+ */
+#undef LOG_TAG
+#define LOG_TAG "GpuStats"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GpuStats.h"
+
+#include <unordered_set>
+
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+static bool addLoadingCount(GraphicsEnv::Driver driver, bool isDriverLoaded,
+ GpuStatsGlobalInfo* const outGlobalInfo) {
+ switch (driver) {
+ case GraphicsEnv::Driver::GL:
+ case GraphicsEnv::Driver::GL_UPDATED:
+ outGlobalInfo->glLoadingCount++;
+ if (!isDriverLoaded) outGlobalInfo->glLoadingFailureCount++;
+ break;
+ case GraphicsEnv::Driver::VULKAN:
+ case GraphicsEnv::Driver::VULKAN_UPDATED:
+ outGlobalInfo->vkLoadingCount++;
+ if (!isDriverLoaded) outGlobalInfo->vkLoadingFailureCount++;
+ break;
+ default:
+ // Currently we don't support GraphicsEnv::Driver::ANGLE because the
+ // basic driver package info only belongs to system or updated driver.
+ return false;
+ }
+
+ return true;
+}
+
+static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime,
+ GpuStatsAppInfo* const outAppInfo) {
+ switch (driver) {
+ case GraphicsEnv::Driver::GL:
+ case GraphicsEnv::Driver::GL_UPDATED:
+ if (outAppInfo->glDriverLoadingTime.size() >= GpuStats::MAX_NUM_LOADING_TIMES) break;
+ outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime);
+ break;
+ case GraphicsEnv::Driver::VULKAN:
+ case GraphicsEnv::Driver::VULKAN_UPDATED:
+ if (outAppInfo->vkDriverLoadingTime.size() >= GpuStats::MAX_NUM_LOADING_TIMES) break;
+ outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime);
+ break;
+ default:
+ break;
+ }
+}
+
+void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t driverVersionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, GraphicsEnv::Driver driver,
+ bool isDriverLoaded, int64_t driverLoadingTime) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mLock);
+ ALOGV("Received:\n"
+ "\tdriverPackageName[%s]\n"
+ "\tdriverVersionName[%s]\n"
+ "\tdriverVersionCode[%" PRIu64 "]\n"
+ "\tdriverBuildTime[%" PRId64 "]\n"
+ "\tappPackageName[%s]\n"
+ "\tdriver[%d]\n"
+ "\tisDriverLoaded[%d]\n"
+ "\tdriverLoadingTime[%" PRId64 "]",
+ driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime,
+ appPackageName.c_str(), static_cast<int32_t>(driver), isDriverLoaded, driverLoadingTime);
+
+ if (!mGlobalStats.count(driverVersionCode)) {
+ GpuStatsGlobalInfo globalInfo;
+ if (!addLoadingCount(driver, isDriverLoaded, &globalInfo)) {
+ return;
+ }
+ globalInfo.driverPackageName = driverPackageName;
+ globalInfo.driverVersionName = driverVersionName;
+ globalInfo.driverVersionCode = driverVersionCode;
+ globalInfo.driverBuildTime = driverBuildTime;
+ mGlobalStats.insert({driverVersionCode, globalInfo});
+ } else if (!addLoadingCount(driver, isDriverLoaded, &mGlobalStats[driverVersionCode])) {
+ return;
+ }
+
+ if (mAppStats.size() >= MAX_NUM_APP_RECORDS) {
+ ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats.");
+ return;
+ }
+
+ const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
+ if (!mAppStats.count(appStatsKey)) {
+ GpuStatsAppInfo appInfo;
+ addLoadingTime(driver, driverLoadingTime, &appInfo);
+ appInfo.appPackageName = appPackageName;
+ appInfo.driverVersionCode = driverVersionCode;
+ mAppStats.insert({appStatsKey, appInfo});
+ return;
+ }
+
+ addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
+}
+
+void GpuStats::dump(const Vector<String16>& args, std::string* result) {
+ ATRACE_CALL();
+
+ if (!result) {
+ ALOGE("Dump result shouldn't be nullptr.");
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mLock);
+ bool dumpAll = true;
+
+ std::unordered_set<std::string> argsSet;
+ for (size_t i = 0; i < args.size(); i++) {
+ argsSet.insert(String8(args[i]).c_str());
+ }
+
+ const bool dumpGlobal = argsSet.count("--global") != 0;
+ if (dumpGlobal) {
+ dumpGlobalLocked(result);
+ dumpAll = false;
+ }
+
+ const bool dumpApp = argsSet.count("--app") != 0;
+ if (dumpApp) {
+ dumpAppLocked(result);
+ dumpAll = false;
+ }
+
+ if (argsSet.count("--clear")) {
+ bool clearAll = true;
+
+ if (dumpGlobal) {
+ mGlobalStats.clear();
+ clearAll = false;
+ }
+
+ if (dumpApp) {
+ mAppStats.clear();
+ clearAll = false;
+ }
+
+ if (clearAll) {
+ mGlobalStats.clear();
+ mAppStats.clear();
+ }
+
+ dumpAll = false;
+ }
+
+ if (dumpAll) {
+ dumpGlobalLocked(result);
+ dumpAppLocked(result);
+ }
+}
+
+void GpuStats::dumpGlobalLocked(std::string* result) {
+ for (const auto& ele : mGlobalStats) {
+ result->append(ele.second.toString());
+ result->append("\n");
+ }
+}
+
+void GpuStats::dumpAppLocked(std::string* result) {
+ for (const auto& ele : mAppStats) {
+ result->append(ele.second.toString());
+ result->append("\n");
+ }
+}
+
+void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mLock);
+ outStats->clear();
+ outStats->reserve(mGlobalStats.size());
+
+ for (const auto& ele : mGlobalStats) {
+ outStats->emplace_back(ele.second);
+ }
+
+ mGlobalStats.clear();
+}
+
+void GpuStats::pullAppStats(std::vector<GpuStatsAppInfo>* outStats) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mLock);
+ outStats->clear();
+ outStats->reserve(mAppStats.size());
+
+ for (const auto& ele : mAppStats) {
+ outStats->emplace_back(ele.second);
+ }
+
+ mAppStats.clear();
+}
+
+} // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/GpuStats.h
new file mode 100644
index 0000000..d942154
--- /dev/null
+++ b/services/gpuservice/gpustats/GpuStats.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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 <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include <graphicsenv/GpuStatsInfo.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class GpuStats {
+public:
+ GpuStats() = default;
+ ~GpuStats() = default;
+
+ // Insert new gpu stats into global stats and app stats.
+ void insert(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t driverVersionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, GraphicsEnv::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime);
+ // dumpsys interface
+ void dump(const Vector<String16>& args, std::string* result);
+ // Pull gpu global stats
+ void pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats);
+ // Pull gpu app stats
+ void pullAppStats(std::vector<GpuStatsAppInfo>* outStats);
+
+ // This limits the worst case number of loading times tracked.
+ static const size_t MAX_NUM_LOADING_TIMES = 50;
+
+private:
+ // Dump global stats
+ void dumpGlobalLocked(std::string* result);
+ // Dump app stats
+ void dumpAppLocked(std::string* result);
+
+ // Below limits the memory usage of GpuStats to be less than 10KB. This is
+ // the preferred number for statsd while maintaining nice data quality.
+ static const size_t MAX_NUM_APP_RECORDS = 100;
+ // GpuStats access should be guarded by mLock.
+ std::mutex mLock;
+ // Key is driver version code.
+ std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
+ // Key is <app package name>+<driver version code>.
+ std::unordered_map<std::string, GpuStatsAppInfo> mAppStats;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 8871199..63e759c 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -12,43 +12,138 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-cc_library_shared {
- name: "libinputflinger",
-
- srcs: [
- "EventHub.cpp",
- "InputApplication.cpp",
- "InputDispatcher.cpp",
- "InputListener.cpp",
- "InputManager.cpp",
- "InputReader.cpp",
- "InputWindow.cpp",
- ],
-
- shared_libs: [
- "libbase",
- "libbinder",
- "libcrypto",
- "libcutils",
- "libinput",
- "liblog",
- "libutils",
- "libui",
- "libhardware_legacy",
- ],
-
+cc_defaults {
+ name: "inputflinger_defaults",
cflags: [
"-Wall",
"-Wextra",
"-Werror",
- // Allow implicit fallthroughs in InputReader.cpp until they are fixed.
- "-Wno-error=implicit-fallthrough",
"-Wno-unused-parameter",
- // TODO: Move inputflinger to its own process and mark it hidden
+ "-Wthread-safety",
+ ],
+}
+
+cc_library_shared {
+ name: "libinputflinger",
+ defaults: ["inputflinger_defaults"],
+
+ srcs: [
+ "InputClassifier.cpp",
+ "InputDispatcher.cpp",
+ "InputManager.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.input.classifier@1.0",
+ "libbase",
+ "libinputflinger_base",
+ "libinputreporter",
+ "libinputreader",
+ "libbinder",
+ "libcutils",
+ "libhidlbase",
+ "libinput",
+ "liblog",
+ "libutils",
+ "libui",
+ "server_configurable_flags",
+ ],
+
+ cflags: [
+ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
],
- export_include_dirs: ["."],
+ export_include_dirs: [
+ ".",
+ "include",
+ ],
+
+}
+
+cc_library_headers {
+ name: "libinputflinger_headers",
+ export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+ name: "libinputreader",
+ defaults: ["inputflinger_defaults"],
+
+ srcs: [
+ "EventHub.cpp",
+ "InputReader.cpp",
+ "InputReaderFactory.cpp",
+ "TouchVideoDevice.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libinputflinger_base",
+ "libcrypto",
+ "libcutils",
+ "libinput",
+ "liblog",
+ "libui",
+ "libutils",
+ "libhardware_legacy",
+ "libstatslog",
+ ],
+
+ header_libs: [
+ "libinputflinger_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libinputflinger_headers",
+ ],
+}
+
+cc_library_shared {
+ name: "libinputflinger_base",
+ defaults: ["inputflinger_defaults"],
+
+ srcs: [
+ "InputListener.cpp",
+ "InputReaderBase.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libinput",
+ "liblog",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libinputflinger_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libinputflinger_headers",
+ ],
+}
+
+cc_library_shared {
+ name: "libinputreporter",
+ defaults: ["inputflinger_defaults"],
+
+ srcs: [
+ "InputReporter.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+
+ header_libs: [
+ "libinputflinger_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libinputflinger_headers",
+ ],
}
subdirs = [
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
new file mode 100644
index 0000000..db9f26e
--- /dev/null
+++ b/services/inputflinger/BlockingQueue.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_INPUT_BLOCKING_QUEUE_H
+#define _UI_INPUT_BLOCKING_QUEUE_H
+
+#include "android-base/thread_annotations.h"
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+/**
+ * A FIFO queue that stores up to <i>capacity</i> objects.
+ * Objects can always be added. Objects are added immediately.
+ * If the queue is full, new objects cannot be added.
+ *
+ * The action of retrieving an object will block until an element is available.
+ */
+template <class T>
+class BlockingQueue {
+public:
+ BlockingQueue(size_t capacity) : mCapacity(capacity) {
+ mQueue.reserve(mCapacity);
+ };
+
+ /**
+ * Retrieve and remove the oldest object.
+ * Blocks execution while queue is empty.
+ */
+ T pop() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLock(mLock);
+ mHasElements.wait(lock, [this]{
+ android::base::ScopedLockAssertion assumeLock(mLock);
+ return !this->mQueue.empty();
+ });
+ T t = std::move(mQueue.front());
+ mQueue.erase(mQueue.begin());
+ return t;
+ };
+
+ /**
+ * Add a new object to the queue.
+ * Does not block.
+ * Return true if an element was successfully added.
+ * Return false if the queue is full.
+ */
+ bool push(T&& t) {
+ {
+ std::scoped_lock lock(mLock);
+ if (mQueue.size() == mCapacity) {
+ return false;
+ }
+ mQueue.push_back(std::move(t));
+ }
+ mHasElements.notify_one();
+ return true;
+ };
+
+ void erase(const std::function<bool(const T&)>& lambda) {
+ std::scoped_lock lock(mLock);
+ mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
+ [&lambda](const T& t) { return lambda(t); }), mQueue.end());
+ }
+
+ /**
+ * Remove all elements.
+ * Does not block.
+ */
+ void clear() {
+ std::scoped_lock lock(mLock);
+ mQueue.clear();
+ };
+
+ /**
+ * How many elements are currently stored in the queue.
+ * Primary used for debugging.
+ * Does not block.
+ */
+ size_t size() {
+ std::scoped_lock lock(mLock);
+ return mQueue.size();
+ }
+
+private:
+ const size_t mCapacity;
+ /**
+ * Used to signal that mQueue is non-empty.
+ */
+ std::condition_variable mHasElements;
+ /**
+ * Lock for accessing and waiting on elements.
+ */
+ std::mutex mLock;
+ std::vector<T> mQueue GUARDED_BY(mLock);
+};
+
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index ccc24b9..0544ec1 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -69,23 +69,27 @@
namespace android {
+static constexpr bool DEBUG = false;
+
static const char *WAKE_LOCK_ID = "KeyEvents";
static const char *DEVICE_PATH = "/dev/input";
+// v4l2 devices go directly into /dev
+static const char *VIDEO_DEVICE_PATH = "/dev";
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
-static String8 sha1(const String8& in) {
+static std::string sha1(const std::string& in) {
SHA_CTX ctx;
SHA1_Init(&ctx);
- SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size());
+ SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.c_str()), in.size());
u_char digest[SHA_DIGEST_LENGTH];
SHA1_Final(digest, &ctx);
- String8 out;
+ std::string out;
for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) {
- out.appendFormat("%02x", digest[i]);
+ out += StringPrintf("%02x", digest[i]);
}
return out;
}
@@ -98,6 +102,44 @@
}
}
+/**
+ * Return true if name matches "v4l-touch*"
+ */
+static bool isV4lTouchNode(const char* name) {
+ return strstr(name, "v4l-touch") == name;
+}
+
+/**
+ * Returns true if V4L devices should be scanned.
+ *
+ * The system property ro.input.video_enabled can be used to control whether
+ * EventHub scans and opens V4L devices. As V4L does not support multiple
+ * clients, EventHub effectively blocks access to these devices when it opens
+ * them. This property enables other clients to read these devices for testing
+ * and development.
+ */
+static bool isV4lScanningEnabled() {
+ return property_get_bool("ro.input.video_enabled", true /* default_value */);
+}
+
+static nsecs_t processEventTimestamp(const struct input_event& event) {
+ // Use the time specified in the event instead of the current time
+ // so that downstream code can get more accurate estimates of
+ // event dispatch latency from the time the event is enqueued onto
+ // the evdev client buffer.
+ //
+ // The event's timestamp fortuitously uses the same monotonic clock
+ // time base as the rest of Android. The kernel event device driver
+ // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
+ // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
+ // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
+ // system call that also queries ktime_get_ts().
+
+ const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) +
+ microseconds_to_nanoseconds(event.time.tv_usec);
+ return inputEventTime;
+}
+
// --- Global Functions ---
uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
@@ -141,14 +183,13 @@
// --- EventHub::Device ---
-EventHub::Device::Device(int fd, int32_t id, const String8& path,
+EventHub::Device::Device(int fd, int32_t id, const std::string& path,
const InputDeviceIdentifier& identifier) :
- next(NULL),
+ next(nullptr),
fd(fd), id(id), path(path), identifier(identifier),
- classes(0), configuration(NULL), virtualKeyMap(NULL),
+ classes(0), configuration(nullptr), virtualKeyMap(nullptr),
ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
- timestampOverrideSec(0), timestampOverrideUsec(0), enabled(true),
- isVirtual(fd < 0) {
+ enabled(true), isVirtual(fd < 0) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
@@ -161,7 +202,6 @@
EventHub::Device::~Device() {
close();
delete configuration;
- delete virtualKeyMap;
}
void EventHub::Device::close() {
@@ -172,9 +212,9 @@
}
status_t EventHub::Device::enable() {
- fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+ fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if(fd < 0) {
- ALOGE("could not open %s, %s\n", path.string(), strerror(errno));
+ ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno));
return -errno;
}
enabled = true;
@@ -193,32 +233,37 @@
// --- EventHub ---
-const uint32_t EventHub::EPOLL_ID_INOTIFY;
-const uint32_t EventHub::EPOLL_ID_WAKE;
-const int EventHub::EPOLL_SIZE_HINT;
const int EventHub::EPOLL_MAX_EVENTS;
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
- mOpeningDevices(0), mClosingDevices(0),
+ mOpeningDevices(nullptr), mClosingDevices(nullptr),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
- LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
mINotifyFd = inotify_init();
- int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
- LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
- DEVICE_PATH, errno);
+ mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
+ LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s",
+ DEVICE_PATH, strerror(errno));
+ if (isV4lScanningEnabled()) {
+ mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
+ LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
+ VIDEO_DEVICE_PATH, strerror(errno));
+ } else {
+ mVideoWd = -1;
+ ALOGI("Video device scanning disabled");
+ }
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
- eventItem.data.u32 = EPOLL_ID_INOTIFY;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
+ eventItem.data.fd = mINotifyFd;
+ int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
@@ -236,7 +281,7 @@
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
- eventItem.data.u32 = EPOLL_ID_WAKE;
+ eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
@@ -267,21 +312,21 @@
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == NULL) return InputDeviceIdentifier();
+ if (device == nullptr) return InputDeviceIdentifier();
return device->identifier;
}
uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == NULL) return 0;
+ if (device == nullptr) return 0;
return device->classes;
}
int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == NULL) return 0;
+ if (device == nullptr) return 0;
return device->controllerNumber;
}
@@ -307,7 +352,7 @@
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, device->identifier.name.string(), device->fd, errno);
+ axis, device->identifier.name.c_str(), device->fd, errno);
return -errno;
}
@@ -370,14 +415,14 @@
Device* device = getDeviceLocked(deviceId);
if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
- Vector<int32_t> scanCodes;
+ 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) {
for (size_t i = 0; i < scanCodes.size(); i++) {
- int32_t sc = scanCodes.itemAt(i);
+ int32_t sc = scanCodes[i];
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
return AKEY_STATE_DOWN;
}
@@ -416,7 +461,7 @@
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, device->identifier.name.string(), device->fd, errno);
+ axis, device->identifier.name.c_str(), device->fd, errno);
return -errno;
}
@@ -433,7 +478,7 @@
Device* device = getDeviceLocked(deviceId);
if (device && device->keyMap.haveKeyLayout()) {
- Vector<int32_t> scanCodes;
+ std::vector<int32_t> scanCodes;
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
scanCodes.clear();
@@ -465,7 +510,7 @@
if (device) {
// Check the key character map first.
sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
- if (kcm != NULL) {
+ if (kcm != nullptr) {
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
*outFlags = 0;
status = NO_ERROR;
@@ -474,14 +519,13 @@
// Check the key layout next.
if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {
- if (!device->keyMap.keyLayoutMap->mapKey(
- scanCode, usageCode, outKeycode, outFlags)) {
+ if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) {
status = NO_ERROR;
}
}
if (status == NO_ERROR) {
- if (kcm != NULL) {
+ if (kcm != nullptr) {
kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
} else {
*outMetaState = metaState;
@@ -512,7 +556,7 @@
return NAME_NOT_FOUND;
}
-void EventHub::setExcludedDevices(const Vector<String8>& devices) {
+void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
AutoMutex _l(mLock);
mExcludedDevices = devices;
@@ -565,13 +609,15 @@
}
void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
outVirtualKeys.clear();
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device && device->virtualKeyMap) {
- outVirtualKeys.appendVector(device->virtualKeyMap->getVirtualKeys());
+ const std::vector<VirtualKeyDefinition> virtualKeys =
+ device->virtualKeyMap->getVirtualKeys();
+ outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end());
}
}
@@ -581,7 +627,7 @@
if (device) {
return device->getKeyCharacterMap();
}
- return NULL;
+ return nullptr;
}
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
@@ -599,16 +645,16 @@
return false;
}
-static String8 generateDescriptor(InputDeviceIdentifier& identifier) {
- String8 rawDescriptor;
- rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor,
+static std::string generateDescriptor(InputDeviceIdentifier& identifier) {
+ std::string rawDescriptor;
+ rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor,
identifier.product);
// TODO add handling for USB devices to not uniqueify kbs that show up twice
- if (!identifier.uniqueId.isEmpty()) {
- rawDescriptor.append("uniqueId:");
- rawDescriptor.append(identifier.uniqueId);
+ if (!identifier.uniqueId.empty()) {
+ rawDescriptor += "uniqueId:";
+ rawDescriptor += identifier.uniqueId;
} else if (identifier.nonce != 0) {
- rawDescriptor.appendFormat("nonce:%04x", identifier.nonce);
+ rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce);
}
if (identifier.vendor == 0 && identifier.product == 0) {
@@ -616,12 +662,12 @@
// built-in so we need to rely on other information to uniquely identify
// the input device. Usually we try to avoid relying on the device name or
// location but for built-in input device, they are unlikely to ever change.
- if (!identifier.name.isEmpty()) {
- rawDescriptor.append("name:");
- rawDescriptor.append(identifier.name);
- } else if (!identifier.location.isEmpty()) {
- rawDescriptor.append("location:");
- rawDescriptor.append(identifier.location);
+ if (!identifier.name.empty()) {
+ rawDescriptor += "name:";
+ rawDescriptor += identifier.name;
+ } else if (!identifier.location.empty()) {
+ rawDescriptor += "location:";
+ rawDescriptor += identifier.location;
}
}
identifier.descriptor = sha1(rawDescriptor);
@@ -637,17 +683,17 @@
// Ideally, we also want the descriptor to be short and relatively opaque.
identifier.nonce = 0;
- String8 rawDescriptor = generateDescriptor(identifier);
- if (identifier.uniqueId.isEmpty()) {
+ std::string rawDescriptor = generateDescriptor(identifier);
+ if (identifier.uniqueId.empty()) {
// If it didn't have a unique id check for conflicts and enforce
// uniqueness if necessary.
- while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) {
+ while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
identifier.nonce++;
rawDescriptor = generateDescriptor(identifier);
}
}
- ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
- identifier.descriptor.string());
+ ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(),
+ identifier.descriptor.c_str());
}
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
@@ -664,7 +710,7 @@
effect.replay.delay = 0;
if (ioctl(device->fd, EVIOCSFF, &effect)) {
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
- device->identifier.name.string(), errno);
+ device->identifier.name.c_str(), errno);
return;
}
device->ffEffectId = effect.id;
@@ -677,7 +723,7 @@
ev.value = 1;
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
ALOGW("Could not start force feedback effect on device %s due to error %d.",
- device->identifier.name.string(), errno);
+ device->identifier.name.c_str(), errno);
return;
}
device->ffEffectPlaying = true;
@@ -699,26 +745,26 @@
ev.value = 0;
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
ALOGW("Could not stop force feedback effect on device %s due to error %d.",
- device->identifier.name.string(), errno);
+ device->identifier.name.c_str(), errno);
return;
}
}
}
}
-EventHub::Device* EventHub::getDeviceByDescriptorLocked(String8& descriptor) const {
+EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
size_t size = mDevices.size();
for (size_t i = 0; i < size; i++) {
Device* device = mDevices.valueAt(i);
- if (descriptor.compare(device->identifier.descriptor) == 0) {
+ if (descriptor == device->identifier.descriptor) {
return device;
}
}
- return NULL;
+ return nullptr;
}
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
- if (deviceId == BUILT_IN_KEYBOARD_ID) {
+ if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
}
ssize_t index = mDevices.indexOfKey(deviceId);
@@ -732,7 +778,31 @@
return device;
}
}
- return NULL;
+ return nullptr;
+}
+
+/**
+ * The file descriptor could be either input device, or a video device (associated with a
+ * specific input device). Check both cases here, and return the device that this event
+ * belongs to. Caller can compare the fd's once more to determine event type.
+ * Looks through all input devices, and only attached video devices. Unattached video
+ * devices are ignored.
+ */
+EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ Device* device = mDevices.valueAt(i);
+ if (device->fd == fd) {
+ // This is an input device event
+ return device;
+ }
+ if (device->videoDevice && device->videoDevice->getFd() == fd) {
+ // This is a video device event
+ return device;
+ }
+ }
+ // We do not check mUnattachedVideoDevices here because they should not participate in epoll,
+ // and therefore should never be looked up by fd.
+ return nullptr;
}
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
@@ -763,10 +833,11 @@
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\n",
- device->id, device->path.string());
+ device->id, device->path.c_str());
mClosingDevices = device->next;
event->when = now;
- event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
+ event->deviceId = (device->id == mBuiltInKeyboardId) ?
+ ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
@@ -782,10 +853,10 @@
mNeedToSendFinishedDeviceScan = true;
}
- while (mOpeningDevices != NULL) {
+ while (mOpeningDevices != nullptr) {
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\n",
- device->id, device->path.string());
+ device->id, device->path.c_str());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
@@ -811,7 +882,7 @@
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
- if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
+ if (eventItem.data.fd == mINotifyFd) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
} else {
@@ -820,7 +891,7 @@
continue;
}
- if (eventItem.data.u32 == EPOLL_ID_WAKE) {
+ if (eventItem.data.fd == mWakeReadPipeFd) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
@@ -836,14 +907,34 @@
continue;
}
- ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
- if (deviceIndex < 0) {
- ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
- eventItem.events, eventItem.data.u32);
+ Device* device = getDeviceByFdLocked(eventItem.data.fd);
+ if (!device) {
+ ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.",
+ eventItem.events, eventItem.data.fd);
+ ALOG_ASSERT(!DEBUG);
continue;
}
-
- Device* device = mDevices.valueAt(deviceIndex);
+ if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
+ if (eventItem.events & EPOLLIN) {
+ size_t numFrames = device->videoDevice->readAndQueueFrames();
+ if (numFrames == 0) {
+ ALOGE("Received epoll event for video device %s, but could not read frame",
+ device->videoDevice->getName().c_str());
+ }
+ } else if (eventItem.events & EPOLLHUP) {
+ // TODO(b/121395353) - consider adding EPOLLRDHUP
+ ALOGI("Removing video device %s due to epoll hang-up event.",
+ device->videoDevice->getName().c_str());
+ unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+ device->videoDevice = nullptr;
+ } else {
+ ALOGW("Received unexpected epoll event 0x%08x for device %s.",
+ eventItem.events, device->videoDevice->getName().c_str());
+ ALOG_ASSERT(!DEBUG);
+ }
+ continue;
+ }
+ // This must be an input event
if (eventItem.events & EPOLLIN) {
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
@@ -866,86 +957,7 @@
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
- ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
- device->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec,
- iev.type, iev.code, iev.value);
-
- // Some input devices may have a better concept of the time
- // when an input event was actually generated than the kernel
- // which simply timestamps all events on entry to evdev.
- // This is a custom Android extension of the input protocol
- // mainly intended for use with uinput based device drivers.
- if (iev.type == EV_MSC) {
- if (iev.code == MSC_ANDROID_TIME_SEC) {
- device->timestampOverrideSec = iev.value;
- continue;
- } else if (iev.code == MSC_ANDROID_TIME_USEC) {
- device->timestampOverrideUsec = iev.value;
- continue;
- }
- }
- if (device->timestampOverrideSec || device->timestampOverrideUsec) {
- iev.time.tv_sec = device->timestampOverrideSec;
- iev.time.tv_usec = device->timestampOverrideUsec;
- if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
- device->timestampOverrideSec = 0;
- device->timestampOverrideUsec = 0;
- }
- ALOGV("applied override time %d.%06d",
- int(iev.time.tv_sec), int(iev.time.tv_usec));
- }
-
- // Use the time specified in the event instead of the current time
- // so that downstream code can get more accurate estimates of
- // event dispatch latency from the time the event is enqueued onto
- // the evdev client buffer.
- //
- // The event's timestamp fortuitously uses the same monotonic clock
- // time base as the rest of Android. The kernel event device driver
- // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
- // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
- // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
- // system call that also queries ktime_get_ts().
- event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
- + nsecs_t(iev.time.tv_usec) * 1000LL;
- ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
-
- // Bug 7291243: Add a guard in case the kernel generates timestamps
- // that appear to be far into the future because they were generated
- // using the wrong clock source.
- //
- // This can happen because when the input device is initially opened
- // it has a default clock source of CLOCK_REALTIME. Any input events
- // enqueued right after the device is opened will have timestamps
- // generated using CLOCK_REALTIME. We later set the clock source
- // to CLOCK_MONOTONIC but it is already too late.
- //
- // Invalid input event timestamps can result in ANRs, crashes and
- // and other issues that are hard to track down. We must not let them
- // propagate through the system.
- //
- // Log a warning so that we notice the problem and recover gracefully.
- if (event->when >= now + 10 * 1000000000LL) {
- // Double-check. Time may have moved on.
- nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
- if (event->when > time) {
- ALOGW("An input event from %s has a timestamp that appears to "
- "have been generated using the wrong clock source "
- "(expected CLOCK_MONOTONIC): "
- "event time %" PRId64 ", current time %" PRId64
- ", call time %" PRId64 ". "
- "Using current time instead.",
- device->path.string(), event->when, time, now);
- event->when = time;
- } else {
- ALOGV("Event time is ok but failed the fast path and required "
- "an extra call to systemTime: "
- "event time %" PRId64 ", current time %" PRId64
- ", call time %" PRId64 ".",
- event->when, time, now);
- }
- }
+ event->when = processEventTimestamp(iev);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
@@ -962,12 +974,12 @@
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device %s due to epoll hang-up event.",
- device->identifier.name.string());
+ device->identifier.name.c_str());
deviceChanged = true;
closeDeviceLocked(device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
- eventItem.events, device->identifier.name.string());
+ eventItem.events, device->identifier.name.c_str());
}
}
@@ -1037,6 +1049,16 @@
return event - buffer;
}
+std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
+ AutoMutex _l(mLock);
+
+ Device* device = getDeviceLocked(deviceId);
+ if (!device || !device->videoDevice) {
+ return {};
+ }
+ return device->videoDevice->consumeFrames();
+}
+
void EventHub::wake() {
ALOGV("wake() called");
@@ -1046,16 +1068,22 @@
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1 && errno != EAGAIN) {
- ALOGW("Could not write wake signal, errno=%d", errno);
+ ALOGW("Could not write wake signal: %s", strerror(errno));
}
}
void EventHub::scanDevicesLocked() {
- status_t res = scanDirLocked(DEVICE_PATH);
- if(res < 0) {
- ALOGE("scan dir failed for %s\n", DEVICE_PATH);
+ status_t result = scanDirLocked(DEVICE_PATH);
+ if(result < 0) {
+ ALOGE("scan dir failed for %s", DEVICE_PATH);
}
- if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
+ if (isV4lScanningEnabled()) {
+ result = scanVideoDirLocked(VIDEO_DEVICE_PATH);
+ if (result != OK) {
+ ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);
+ }
+ }
+ if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) {
createVirtualKeyboardLocked();
}
}
@@ -1082,32 +1110,76 @@
AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
};
-status_t EventHub::registerDeviceForEpollLocked(Device* device) {
- struct epoll_event eventItem;
- memset(&eventItem, 0, sizeof(eventItem));
- eventItem.events = EPOLLIN;
- if (mUsingEpollWakeup) {
- eventItem.events |= EPOLLWAKEUP;
- }
- eventItem.data.u32 = device->id;
- if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {
- ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
+status_t EventHub::registerFdForEpoll(int fd) {
+ // TODO(b/121395353) - consider adding EPOLLRDHUP
+ struct epoll_event eventItem = {};
+ eventItem.events = EPOLLIN | EPOLLWAKEUP;
+ eventItem.data.fd = fd;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
+ ALOGE("Could not add fd to epoll instance: %s", strerror(errno));
return -errno;
}
return OK;
}
-status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
- if (device->hasValidFd()) {
- if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
- ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
- return -errno;
- }
+status_t EventHub::unregisterFdFromEpoll(int fd) {
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, nullptr)) {
+ ALOGW("Could not remove fd from epoll instance: %s", strerror(errno));
+ return -errno;
}
return OK;
}
-status_t EventHub::openDeviceLocked(const char *devicePath) {
+status_t EventHub::registerDeviceForEpollLocked(Device* device) {
+ if (device == nullptr) {
+ if (DEBUG) {
+ LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device");
+ }
+ return BAD_VALUE;
+ }
+ status_t result = registerFdForEpoll(device->fd);
+ if (result != OK) {
+ ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id);
+ return result;
+ }
+ if (device->videoDevice) {
+ registerVideoDeviceForEpollLocked(*device->videoDevice);
+ }
+ return result;
+}
+
+void EventHub::registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) {
+ status_t result = registerFdForEpoll(videoDevice.getFd());
+ if (result != OK) {
+ ALOGE("Could not add video device %s to epoll", videoDevice.getName().c_str());
+ }
+}
+
+status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
+ if (device->hasValidFd()) {
+ status_t result = unregisterFdFromEpoll(device->fd);
+ if (result != OK) {
+ ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id);
+ return result;
+ }
+ }
+ if (device->videoDevice) {
+ unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+ }
+ return OK;
+}
+
+void EventHub::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) {
+ if (videoDevice.hasValidFd()) {
+ status_t result = unregisterFdFromEpoll(videoDevice.getFd());
+ if (result != OK) {
+ ALOGW("Could not remove video device fd from epoll for device: %s",
+ videoDevice.getName().c_str());
+ }
+ }
+}
+
+status_t EventHub::openDeviceLocked(const char* devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
@@ -1122,17 +1194,17 @@
// Get device name.
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
- //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
+ ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
- identifier.name.setTo(buffer);
+ identifier.name = buffer;
}
// Check to see if the device is on our excluded list
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
- const String8& item = mExcludedDevices.itemAt(i);
+ const std::string& item = mExcludedDevices[i];
if (identifier.name == item) {
- ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
+ ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str());
close(fd);
return -1;
}
@@ -1163,7 +1235,7 @@
//fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
- identifier.location.setTo(buffer);
+ identifier.location = buffer;
}
// Get device unique id.
@@ -1171,7 +1243,7 @@
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
- identifier.uniqueId.setTo(buffer);
+ identifier.uniqueId = buffer;
}
// Fill in the descriptor.
@@ -1179,7 +1251,7 @@
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
- Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
+ Device* device = new Device(fd, deviceId, devicePath, identifier);
ALOGV("add device %d: %s\n", deviceId, devicePath);
ALOGV(" bus: %04x\n"
@@ -1187,10 +1259,10 @@
" product %04x\n"
" version %04x\n",
identifier.bus, identifier.vendor, identifier.product, identifier.version);
- ALOGV(" name: \"%s\"\n", identifier.name.string());
- ALOGV(" location: \"%s\"\n", identifier.location.string());
- ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
- ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
+ ALOGV(" name: \"%s\"\n", identifier.name.c_str());
+ ALOGV(" location: \"%s\"\n", identifier.location.c_str());
+ ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.c_str());
+ ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.c_str());
ALOGV(" driver: v%d.%d.%d\n",
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
@@ -1293,8 +1365,8 @@
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// Load the virtual keys for the touch screen, if any.
// We do this now so that we can make sure to load the keymap if necessary.
- status_t status = loadVirtualKeyMapLocked(device);
- if (!status) {
+ bool success = loadVirtualKeyMapLocked(device);
+ if (success) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
}
@@ -1343,7 +1415,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, device->identifier.name.string());
+ deviceId, devicePath, device->identifier.name.c_str());
delete device;
return -1;
}
@@ -1364,6 +1436,18 @@
setLedForControllerLocked(device);
}
+ // Find a matching video device by comparing device names
+ // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll
+ for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
+ if (device->identifier.name == videoDevice->getName()) {
+ device->videoDevice = std::move(videoDevice);
+ break;
+ }
+ }
+ mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
+ mUnattachedVideoDevices.end(),
+ [](const std::unique_ptr<TouchVideoDevice>& videoDevice){
+ return videoDevice == nullptr; }), mUnattachedVideoDevices.end());
if (registerDeviceForEpollLocked(device) != OK) {
delete device;
@@ -1374,11 +1458,11 @@
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.string(),
+ deviceId, fd, devicePath, device->identifier.name.c_str(),
device->classes,
- device->configurationFile.string(),
- device->keyMap.keyLayoutFile.string(),
- device->keyMap.keyCharacterMapFile.string(),
+ device->configurationFile.c_str(),
+ device->keyMap.keyLayoutFile.c_str(),
+ device->keyMap.keyCharacterMapFile.c_str(),
toString(mBuiltInKeyboardId == deviceId));
addDeviceLocked(device);
@@ -1392,11 +1476,11 @@
unsigned int repeatRate[] = {0, 0};
if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
ALOGW("Unable to disable kernel key repeat for %s: %s",
- device->path.string(), strerror(errno));
+ device->path.c_str(), strerror(errno));
}
}
- String8 wakeMechanism("EPOLLWAKEUP");
+ std::string wakeMechanism = "EPOLLWAKEUP";
if (!mUsingEpollWakeup) {
#ifndef EVIOCSSUSPENDBLOCK
// uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels
@@ -1416,14 +1500,39 @@
// clock.
int clockId = CLOCK_MONOTONIC;
bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
- ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.string(),
+ ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(),
toString(usingClockIoctl));
}
+void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
+ std::unique_ptr<TouchVideoDevice> videoDevice = TouchVideoDevice::create(devicePath);
+ if (!videoDevice) {
+ ALOGE("Could not create touch video device for %s. Ignoring", devicePath.c_str());
+ return;
+ }
+ // Transfer ownership of this video device to a matching input device
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ Device* device = mDevices.valueAt(i);
+ if (videoDevice->getName() == device->identifier.name) {
+ device->videoDevice = std::move(videoDevice);
+ if (device->enabled) {
+ registerVideoDeviceForEpollLocked(*device->videoDevice);
+ }
+ return;
+ }
+ }
+
+ // Couldn't find a matching input device, so just add it to a temporary holding queue.
+ // A matching input device may appear later.
+ ALOGI("Adding video device %s to list of unattached video devices",
+ videoDevice->getName().c_str());
+ mUnattachedVideoDevices.push_back(std::move(videoDevice));
+}
+
bool EventHub::isDeviceEnabled(int32_t deviceId) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == NULL) {
+ if (device == nullptr) {
ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
return false;
}
@@ -1433,7 +1542,7 @@
status_t EventHub::enableDevice(int32_t deviceId) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == NULL) {
+ if (device == nullptr) {
ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
return BAD_VALUE;
}
@@ -1455,7 +1564,7 @@
status_t EventHub::disableDevice(int32_t deviceId) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device == NULL) {
+ if (device == nullptr) {
ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
return BAD_VALUE;
}
@@ -1473,7 +1582,8 @@
identifier.uniqueId = "<virtual>";
assignDescriptorLocked(identifier);
- Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier);
+ Device* device = new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
+ identifier);
device->classes = INPUT_DEVICE_CLASS_KEYBOARD
| INPUT_DEVICE_CLASS_ALPHAKEY
| INPUT_DEVICE_CLASS_DPAD
@@ -1491,29 +1601,30 @@
void EventHub::loadConfigurationLocked(Device* device) {
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
- if (device->configurationFile.isEmpty()) {
+ if (device->configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
- device->identifier.name.string());
+ device->identifier.name.c_str());
} else {
- status_t status = PropertyMap::load(device->configurationFile,
+ status_t status = PropertyMap::load(String8(device->configurationFile.c_str()),
&device->configuration);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
- device->identifier.name.string());
+ device->identifier.name.c_str());
}
}
}
-status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
+bool EventHub::loadVirtualKeyMapLocked(Device* device) {
// The virtual key map is supplied by the kernel as a system board property file.
- String8 path;
- path.append("/sys/board_properties/virtualkeys.");
- path.append(device->identifier.name);
- if (access(path.string(), R_OK)) {
- return NAME_NOT_FOUND;
+ std::string path;
+ path += "/sys/board_properties/virtualkeys.";
+ path += device->identifier.getCanonicalName();
+ if (access(path.c_str(), R_OK)) {
+ return false;
}
- return VirtualKeyMap::load(path, &device->virtualKeyMap);
+ device->virtualKeyMap = VirtualKeyMap::load(path);
+ return device->virtualKeyMap != nullptr;
}
status_t EventHub::loadKeyMapLocked(Device* device) {
@@ -1543,7 +1654,7 @@
int32_t EventHub::getNextControllerNumberLocked(Device* device) {
if (mControllerNumbers.isFull()) {
ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s",
- device->identifier.name.string());
+ device->identifier.name.c_str());
return 0;
}
// Since the controller number 0 is reserved for non-controllers, translate all numbers up by
@@ -1571,11 +1682,11 @@
return false;
}
- Vector<int32_t> scanCodes;
+ std::vector<int32_t> scanCodes;
device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
const size_t N = scanCodes.size();
for (size_t i=0; i<N && i<=KEY_MAX; i++) {
- int32_t sc = scanCodes.itemAt(i);
+ int32_t sc = scanCodes[i];
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
return true;
}
@@ -1599,34 +1710,60 @@
return NAME_NOT_FOUND;
}
-status_t EventHub::closeDeviceByPathLocked(const char *devicePath) {
+void EventHub::closeDeviceByPathLocked(const char *devicePath) {
Device* device = getDeviceByPathLocked(devicePath);
if (device) {
closeDeviceLocked(device);
- return 0;
+ return;
}
ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
- return -1;
+}
+
+/**
+ * Find the video device by filename, and close it.
+ * The video device is closed by path during an inotify event, where we don't have the
+ * additional context about the video device fd, or the associated input device.
+ */
+void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) {
+ // A video device may be owned by an existing input device, or it may be stored in
+ // the mUnattachedVideoDevices queue. Check both locations.
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ Device* device = mDevices.valueAt(i);
+ if (device->videoDevice && device->videoDevice->getPath() == devicePath) {
+ unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
+ device->videoDevice = nullptr;
+ return;
+ }
+ }
+ mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(),
+ mUnattachedVideoDevices.end(), [&devicePath](
+ const std::unique_ptr<TouchVideoDevice>& videoDevice) {
+ return videoDevice->getPath() == devicePath; }), mUnattachedVideoDevices.end());
}
void EventHub::closeAllDevicesLocked() {
+ mUnattachedVideoDevices.clear();
while (mDevices.size() > 0) {
closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
}
}
void EventHub::closeDeviceLocked(Device* device) {
- ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
- device->path.string(), device->identifier.name.string(), device->id,
+ ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x",
+ device->path.c_str(), device->identifier.name.c_str(), device->id,
device->fd, device->classes);
if (device->id == mBuiltInKeyboardId) {
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
- device->path.string(), mBuiltInKeyboardId);
+ device->path.c_str(), mBuiltInKeyboardId);
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
}
unregisterDeviceFromEpollLocked(device);
+ if (device->videoDevice) {
+ // This must be done after the video device is removed from epoll
+ mUnattachedVideoDevices.push_back(std::move(device->videoDevice));
+ }
releaseControllerNumberLocked(device);
@@ -1634,9 +1771,9 @@
device->close();
// Unlink for opening devices list if it is present.
- Device* pred = NULL;
+ Device* pred = nullptr;
bool found = false;
- for (Device* entry = mOpeningDevices; entry != NULL; ) {
+ for (Device* entry = mOpeningDevices; entry != nullptr; ) {
if (entry == device) {
found = true;
break;
@@ -1648,7 +1785,7 @@
// Unlink the device from the opening devices list then delete it.
// We don't need to tell the client that the device was closed because
// it does not even know it was opened in the first place.
- ALOGI("Device %s was immediately closed after opening.", device->path.string());
+ ALOGI("Device %s was immediately closed after opening.", device->path.c_str());
if (pred) {
pred->next = device->next;
} else {
@@ -1665,8 +1802,6 @@
status_t EventHub::readNotifyLocked() {
int res;
- char devname[PATH_MAX];
- char *filename;
char event_buf[512];
int event_size;
int event_pos = 0;
@@ -1680,22 +1815,32 @@
ALOGW("could not get event, %s\n", strerror(errno));
return -1;
}
- //printf("got %d bytes of event information\n", res);
-
- strcpy(devname, DEVICE_PATH);
- filename = devname + strlen(devname);
- *filename++ = '/';
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
- //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
- strcpy(filename, event->name);
- if(event->mask & IN_CREATE) {
- openDeviceLocked(devname);
- } else {
- ALOGI("Removing device '%s' due to inotify event\n", devname);
- closeDeviceByPathLocked(devname);
+ if (event->wd == mInputWd) {
+ std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name);
+ if(event->mask & IN_CREATE) {
+ openDeviceLocked(filename.c_str());
+ } else {
+ ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
+ closeDeviceByPathLocked(filename.c_str());
+ }
+ }
+ else if (event->wd == mVideoWd) {
+ if (isV4lTouchNode(event->name)) {
+ std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name);
+ if (event->mask & IN_CREATE) {
+ openVideoDeviceLocked(filename);
+ } else {
+ ALOGI("Removing video device '%s' due to inotify event", filename.c_str());
+ closeVideoDeviceByPathLocked(filename);
+ }
+ }
+ }
+ else {
+ LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd);
}
}
event_size = sizeof(*event) + event->len;
@@ -1712,7 +1857,7 @@
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
- if(dir == NULL)
+ if(dir == nullptr)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
@@ -1729,6 +1874,30 @@
return 0;
}
+/**
+ * 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);
+ }
+ }
+ closedir(dir);
+ return OK;
+}
+
void EventHub::requestReopenDevices() {
ALOGV("requestReopenDevices() called");
@@ -1750,30 +1919,44 @@
const Device* device = mDevices.valueAt(i);
if (mBuiltInKeyboardId == device->id) {
dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
- device->id, device->identifier.name.string());
+ device->id, device->identifier.name.c_str());
} else {
dump += StringPrintf(INDENT2 "%d: %s\n", device->id,
- device->identifier.name.string());
+ device->identifier.name.c_str());
}
dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes);
- dump += StringPrintf(INDENT3 "Path: %s\n", device->path.string());
+ dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str());
dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled));
- dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
- dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.string());
+ dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str());
+ dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.c_str());
dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
- dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
+ dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
"product=0x%04x, version=0x%04x\n",
device->identifier.bus, device->identifier.vendor,
device->identifier.product, device->identifier.version);
dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
- device->keyMap.keyLayoutFile.string());
+ device->keyMap.keyLayoutFile.c_str());
dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
- device->keyMap.keyCharacterMapFile.string());
+ device->keyMap.keyCharacterMapFile.c_str());
dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
- device->configurationFile.string());
+ device->configurationFile.c_str());
dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
- toString(device->overlayKeyMap != NULL));
+ toString(device->overlayKeyMap != nullptr));
+ dump += INDENT3 "VideoDevice: ";
+ if (device->videoDevice) {
+ dump += device->videoDevice->dump() + "\n";
+ } else {
+ dump += "<none>\n";
+ }
+ }
+
+ dump += INDENT "Unattached video devices:\n";
+ for (const std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
+ dump += INDENT2 + videoDevice->dump() + "\n";
+ }
+ if (mUnattachedVideoDevices.empty()) {
+ dump += INDENT2 "<none>\n";
}
} // release lock
}
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h
index 66bc294..63a20ef 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/EventHub.h
@@ -18,6 +18,8 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
+#include <vector>
+
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/Keyboard.h>
@@ -29,41 +31,21 @@
#include <utils/List.h>
#include <utils/Errors.h>
#include <utils/PropertyMap.h>
-#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <utils/BitSet.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
-/*
- * These constants are used privately in Android to pass raw timestamps
- * through evdev from uinput device drivers because there is currently no
- * other way to transfer this information. The evdev driver automatically
- * timestamps all input events with the time they were posted and clobbers
- * whatever information was passed in.
- *
- * For the purposes of this hack, the timestamp is specified in the
- * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
- * seconds and microseconds.
- */
-#define MSC_ANDROID_TIME_SEC 0x6
-#define MSC_ANDROID_TIME_USEC 0x7
-
namespace android {
-enum {
- // Device id of a special "virtual" keyboard that is always present.
- VIRTUAL_KEYBOARD_ID = -1,
- // Device id of the "built-in" keyboard if there is one.
- BUILT_IN_KEYBOARD_ID = 0,
-};
-
/*
* A raw event as retrieved from the EventHub.
*/
@@ -207,7 +189,7 @@
// Sets devices that are excluded from opening.
// This can be used to ignore input devices for sensors.
- virtual void setExcludedDevices(const Vector<String8>& devices) = 0;
+ virtual void setExcludedDevices(const std::vector<std::string>& devices) = 0;
/*
* Wait for events to become available and returns them.
@@ -222,6 +204,7 @@
* Returns the number of events obtained, or 0 if the timeout expired.
*/
virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
+ virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
/*
* Query current input state.
@@ -245,7 +228,7 @@
virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
virtual void getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
@@ -303,7 +286,7 @@
virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
AxisInfo* outAxisInfo) const;
- virtual void setExcludedDevices(const Vector<String8>& devices);
+ virtual void setExcludedDevices(const std::vector<std::string>& devices);
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
@@ -314,13 +297,14 @@
const int32_t* keyCodes, uint8_t* outFlags) const;
virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
+ virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId);
virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
virtual bool hasLed(int32_t deviceId, int32_t led) const;
virtual void setLedState(int32_t deviceId, int32_t led, bool on);
virtual void getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const;
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
@@ -344,9 +328,11 @@
int fd; // may be -1 if device is closed
const int32_t id;
- const String8 path;
+ const std::string path;
const InputDeviceIdentifier identifier;
+ std::unique_ptr<TouchVideoDevice> videoDevice;
+
uint32_t classes;
uint8_t keyBitmask[(KEY_MAX + 1) / 8];
@@ -357,9 +343,9 @@
uint8_t ffBitmask[(FF_MAX + 1) / 8];
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
- String8 configurationFile;
+ std::string configurationFile;
PropertyMap* configuration;
- VirtualKeyMap* virtualKeyMap;
+ std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
sp<KeyCharacterMap> overlayKeyMap;
@@ -370,10 +356,8 @@
int32_t controllerNumber;
- int32_t timestampOverrideSec;
- int32_t timestampOverrideUsec;
-
- Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
+ Device(int fd, int32_t id, const std::string& path,
+ const InputDeviceIdentifier& identifier);
~Device();
void close();
@@ -385,19 +369,21 @@
const bool isVirtual; // set if fd < 0 is passed to constructor
const sp<KeyCharacterMap>& getKeyCharacterMap() const {
- if (combinedKeyMap != NULL) {
+ if (combinedKeyMap != nullptr) {
return combinedKeyMap;
}
return keyMap.keyCharacterMap;
}
};
- status_t openDeviceLocked(const char *devicePath);
+ status_t openDeviceLocked(const char* devicePath);
+ void openVideoDeviceLocked(const std::string& devicePath);
void createVirtualKeyboardLocked();
void addDeviceLocked(Device* device);
void assignDescriptorLocked(InputDeviceIdentifier& identifier);
- status_t closeDeviceByPathLocked(const char *devicePath);
+ void closeDeviceByPathLocked(const char *devicePath);
+ void closeVideoDeviceByPathLocked(const std::string& devicePath);
void closeDeviceLocked(Device* device);
void closeAllDevicesLocked();
@@ -406,21 +392,31 @@
bool isDeviceEnabled(int32_t deviceId);
status_t enableDevice(int32_t deviceId);
status_t disableDevice(int32_t deviceId);
+ status_t registerFdForEpoll(int fd);
+ status_t unregisterFdFromEpoll(int fd);
status_t registerDeviceForEpollLocked(Device* device);
+ void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
status_t unregisterDeviceFromEpollLocked(Device* device);
+ void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
status_t scanDirLocked(const char *dirname);
+ status_t scanVideoDirLocked(const std::string& dirname);
void scanDevicesLocked();
status_t readNotifyLocked();
- Device* getDeviceByDescriptorLocked(String8& descriptor) const;
+ Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
Device* getDeviceLocked(int32_t deviceId) const;
Device* getDeviceByPathLocked(const char* devicePath) const;
+ /**
+ * Look through all available fd's (both for input devices and for video devices),
+ * and return the device pointer.
+ */
+ Device* getDeviceByFdLocked(int fd) const;
bool hasKeycodeLocked(Device* device, int keycode) const;
void loadConfigurationLocked(Device* device);
- status_t loadVirtualKeyMapLocked(Device* device);
+ bool loadVirtualKeyMapLocked(Device* device);
status_t loadKeyMapLocked(Device* device);
bool isExternalDeviceLocked(Device* device);
@@ -450,6 +446,14 @@
BitSet32 mControllerNumbers;
KeyedVector<int32_t, Device*> mDevices;
+ /**
+ * Video devices that report touchscreen heatmap, but have not (yet) been paired
+ * with a specific input device. Video device discovery is independent from input device
+ * discovery, so the two types of devices could be found in any order.
+ * Ideally, video devices in this queue do not have an open fd, or at least aren't
+ * actively streaming.
+ */
+ std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices;
Device *mOpeningDevices;
Device *mClosingDevices;
@@ -457,19 +461,15 @@
bool mNeedToSendFinishedDeviceScan;
bool mNeedToReopenDevices;
bool mNeedToScanDevices;
- Vector<String8> mExcludedDevices;
+ std::vector<std::string> mExcludedDevices;
int mEpollFd;
int mINotifyFd;
int mWakeReadPipeFd;
int mWakeWritePipeFd;
- // Ids used for epoll notifications not associated with devices.
- static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
- static const uint32_t EPOLL_ID_WAKE = 0x80000002;
-
- // Epoll FD list size hint.
- static const int EPOLL_SIZE_HINT = 8;
+ int mInputWd;
+ int mVideoWd;
// Maximum number of signalled FDs to handle at a time.
static const int EPOLL_MAX_EVENTS = 16;
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
new file mode 100644
index 0000000..b4db338
--- /dev/null
+++ b/services/inputflinger/InputClassifier.cpp
@@ -0,0 +1,789 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputClassifier"
+
+#include "InputClassifier.h"
+
+#include <algorithm>
+#include <android-base/stringprintf.h>
+#include <cmath>
+#include <inttypes.h>
+#include <log/log.h>
+#if defined(__linux__)
+ #include <pthread.h>
+#endif
+#include <server_configurable_flags/get_flags.h>
+#include <unordered_set>
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+#define INDENT1 " "
+#define INDENT2 " "
+#define INDENT3 " "
+#define INDENT4 " "
+#define INDENT5 " "
+
+using android::base::StringPrintf;
+using android::hardware::hidl_bitfield;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using namespace android::hardware::input;
+
+namespace android {
+
+static constexpr bool DEBUG = false;
+
+// Category (=namespace) name for the input settings that are applied at boot time
+static const char* INPUT_NATIVE_BOOT = "input_native_boot";
+// Feature flag name for the deep press feature
+static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
+
+//Max number of elements to store in mEvents.
+static constexpr size_t MAX_EVENTS = 5;
+
+template<class K, class V>
+static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
+ auto it = map.find(key);
+ if (it == map.end()) {
+ return defaultValue;
+ }
+ return it->second;
+}
+
+static common::V1_0::Source getSource(uint32_t source) {
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_UNKNOWN) ==
+ common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_KEYBOARD) ==
+ common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_DPAD) ==
+ common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_GAMEPAD) ==
+ common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHSCREEN) ==
+ common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE) ==
+ common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_STYLUS) ==
+ common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_BLUETOOTH_STYLUS) ==
+ common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TRACKBALL) ==
+ common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE_RELATIVE) ==
+ common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHPAD) ==
+ common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCH_NAVIGATION) ==
+ common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_JOYSTICK) ==
+ common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ROTARY_ENCODER) ==
+ common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch");
+ static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ANY) ==
+ common::V1_0::Source::ANY, "SOURCE_ANY mismatch");
+ return static_cast<common::V1_0::Source>(source);
+}
+
+static common::V1_0::Action getAction(int32_t actionMasked) {
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_DOWN) ==
+ common::V1_0::Action::DOWN, "ACTION_DOWN mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_UP) ==
+ common::V1_0::Action::UP, "ACTION_UP mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_MOVE) ==
+ common::V1_0::Action::MOVE, "ACTION_MOVE mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_CANCEL) ==
+ common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_OUTSIDE) ==
+ common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_DOWN) ==
+ common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_UP) ==
+ common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch");
+ static_assert(static_cast<common::V1_0::Action>( AMOTION_EVENT_ACTION_HOVER_MOVE) ==
+ common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_SCROLL) ==
+ common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_ENTER) ==
+ common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_EXIT) ==
+ common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_PRESS) ==
+ common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch");
+ static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_RELEASE) ==
+ common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch");
+ return static_cast<common::V1_0::Action>(actionMasked);
+}
+
+static common::V1_0::Button getActionButton(int32_t actionButton) {
+ static_assert(static_cast<common::V1_0::Button>(0) ==
+ common::V1_0::Button::NONE, "BUTTON_NONE mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_PRIMARY) ==
+ common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_SECONDARY) ==
+ common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_TERTIARY) ==
+ common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_BACK) ==
+ common::V1_0::Button::BACK, "BUTTON_BACK mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_FORWARD) ==
+ common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) ==
+ common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch");
+ static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) ==
+ common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch");
+ return static_cast<common::V1_0::Button>(actionButton);
+}
+
+static hidl_bitfield<common::V1_0::Flag> getFlags(int32_t flags) {
+ static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) ==
+ common::V1_0::Flag::WINDOW_IS_OBSCURED);
+ static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) ==
+ common::V1_0::Flag::IS_GENERATED_GESTURE);
+ static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_TAINTED) ==
+ common::V1_0::Flag::TAINTED);
+ return static_cast<hidl_bitfield<common::V1_0::Flag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::PolicyFlag> getPolicyFlags(int32_t flags) {
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_WAKE) ==
+ common::V1_0::PolicyFlag::WAKE);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_VIRTUAL) ==
+ common::V1_0::PolicyFlag::VIRTUAL);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FUNCTION) ==
+ common::V1_0::PolicyFlag::FUNCTION);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_GESTURE) ==
+ common::V1_0::PolicyFlag::GESTURE);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INJECTED) ==
+ common::V1_0::PolicyFlag::INJECTED);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_TRUSTED) ==
+ common::V1_0::PolicyFlag::TRUSTED);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FILTERED) ==
+ common::V1_0::PolicyFlag::FILTERED);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_DISABLE_KEY_REPEAT) ==
+ common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INTERACTIVE) ==
+ common::V1_0::PolicyFlag::INTERACTIVE);
+ static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_PASS_TO_USER) ==
+ common::V1_0::PolicyFlag::PASS_TO_USER);
+ return static_cast<hidl_bitfield<common::V1_0::PolicyFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::EdgeFlag> getEdgeFlags(int32_t flags) {
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_NONE) ==
+ common::V1_0::EdgeFlag::NONE);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_TOP) ==
+ common::V1_0::EdgeFlag::TOP);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_BOTTOM) ==
+ common::V1_0::EdgeFlag::BOTTOM);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_LEFT) ==
+ common::V1_0::EdgeFlag::LEFT);
+ static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_RIGHT) ==
+ common::V1_0::EdgeFlag::RIGHT);
+ return static_cast<hidl_bitfield<common::V1_0::EdgeFlag>>(flags);
+}
+
+static hidl_bitfield<common::V1_0::Meta> getMetastate(int32_t state) {
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_NONE) ==
+ common::V1_0::Meta::NONE);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_ON) ==
+ common::V1_0::Meta::ALT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_LEFT_ON) ==
+ common::V1_0::Meta::ALT_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_RIGHT_ON) ==
+ common::V1_0::Meta::ALT_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_ON) ==
+ common::V1_0::Meta::SHIFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_LEFT_ON) ==
+ common::V1_0::Meta::SHIFT_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_RIGHT_ON) ==
+ common::V1_0::Meta::SHIFT_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SYM_ON) ==
+ common::V1_0::Meta::SYM_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_FUNCTION_ON) ==
+ common::V1_0::Meta::FUNCTION_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_ON) ==
+ common::V1_0::Meta::CTRL_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_LEFT_ON) ==
+ common::V1_0::Meta::CTRL_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_RIGHT_ON) ==
+ common::V1_0::Meta::CTRL_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_META_ON) ==
+ common::V1_0::Meta::META_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_META_LEFT_ON) ==
+ common::V1_0::Meta::META_LEFT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_META_RIGHT_ON) ==
+ common::V1_0::Meta::META_RIGHT_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_CAPS_LOCK_ON) ==
+ common::V1_0::Meta::CAPS_LOCK_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_NUM_LOCK_ON) ==
+ common::V1_0::Meta::NUM_LOCK_ON);
+ static_assert(static_cast<common::V1_0::Meta>(AMETA_SCROLL_LOCK_ON) ==
+ common::V1_0::Meta::SCROLL_LOCK_ON);
+ return static_cast<hidl_bitfield<common::V1_0::Meta>>(state);
+}
+
+static hidl_bitfield<common::V1_0::Button> getButtonState(int32_t buttonState) {
+ // No need for static_assert here.
+ // The button values have already been asserted in getActionButton(..) above
+ return static_cast<hidl_bitfield<common::V1_0::Button>>(buttonState);
+}
+
+static common::V1_0::ToolType getToolType(int32_t toolType) {
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) ==
+ common::V1_0::ToolType::UNKNOWN);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_FINGER) ==
+ common::V1_0::ToolType::FINGER);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_STYLUS) ==
+ common::V1_0::ToolType::STYLUS);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_MOUSE) ==
+ common::V1_0::ToolType::MOUSE);
+ static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_ERASER) ==
+ common::V1_0::ToolType::ERASER);
+ return static_cast<common::V1_0::ToolType>(toolType);
+}
+
+static common::V1_0::Axis getAxis(uint64_t axis) {
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_X) ==
+ common::V1_0::Axis::X);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Y) ==
+ common::V1_0::Axis::Y);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_PRESSURE) ==
+ common::V1_0::Axis::PRESSURE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SIZE) ==
+ common::V1_0::Axis::SIZE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MAJOR) ==
+ common::V1_0::Axis::TOUCH_MAJOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MINOR) ==
+ common::V1_0::Axis::TOUCH_MINOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MAJOR) ==
+ common::V1_0::Axis::TOOL_MAJOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MINOR) ==
+ common::V1_0::Axis::TOOL_MINOR);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_ORIENTATION) ==
+ common::V1_0::Axis::ORIENTATION);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_VSCROLL) ==
+ common::V1_0::Axis::VSCROLL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HSCROLL) ==
+ common::V1_0::Axis::HSCROLL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Z) ==
+ common::V1_0::Axis::Z);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RX) ==
+ common::V1_0::Axis::RX);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RY) ==
+ common::V1_0::Axis::RY);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RZ) ==
+ common::V1_0::Axis::RZ);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_X) ==
+ common::V1_0::Axis::HAT_X);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_Y) ==
+ common::V1_0::Axis::HAT_Y);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_LTRIGGER) ==
+ common::V1_0::Axis::LTRIGGER);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RTRIGGER) ==
+ common::V1_0::Axis::RTRIGGER);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_THROTTLE) ==
+ common::V1_0::Axis::THROTTLE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RUDDER) ==
+ common::V1_0::Axis::RUDDER);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_WHEEL) ==
+ common::V1_0::Axis::WHEEL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GAS) ==
+ common::V1_0::Axis::GAS);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_BRAKE) ==
+ common::V1_0::Axis::BRAKE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_DISTANCE) ==
+ common::V1_0::Axis::DISTANCE);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TILT) ==
+ common::V1_0::Axis::TILT);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SCROLL) ==
+ common::V1_0::Axis::SCROLL);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_X) ==
+ common::V1_0::Axis::RELATIVE_X);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_Y) ==
+ common::V1_0::Axis::RELATIVE_Y);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_1) ==
+ common::V1_0::Axis::GENERIC_1);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_2) ==
+ common::V1_0::Axis::GENERIC_2);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_3) ==
+ common::V1_0::Axis::GENERIC_3);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_4) ==
+ common::V1_0::Axis::GENERIC_4);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_5) ==
+ common::V1_0::Axis::GENERIC_5);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_6) ==
+ common::V1_0::Axis::GENERIC_6);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_7) ==
+ common::V1_0::Axis::GENERIC_7);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_8) ==
+ common::V1_0::Axis::GENERIC_8);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_9) ==
+ common::V1_0::Axis::GENERIC_9);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_10) ==
+ common::V1_0::Axis::GENERIC_10);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_11) ==
+ common::V1_0::Axis::GENERIC_11);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_12) ==
+ common::V1_0::Axis::GENERIC_12);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) ==
+ common::V1_0::Axis::GENERIC_13);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) ==
+ common::V1_0::Axis::GENERIC_14);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) ==
+ common::V1_0::Axis::GENERIC_15);
+ static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) ==
+ common::V1_0::Axis::GENERIC_16);
+ return static_cast<common::V1_0::Axis>(axis);
+}
+
+static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
+ common::V1_0::VideoFrame out;
+ out.width = frame.getWidth();
+ out.height = frame.getHeight();
+ out.data = frame.getData();
+ struct timeval timestamp = frame.getTimestamp();
+ out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) +
+ microseconds_to_nanoseconds(timestamp.tv_usec);
+ return out;
+}
+
+static std::vector<common::V1_0::VideoFrame> convertVideoFrames(
+ const std::vector<TouchVideoFrame>& frames) {
+ std::vector<common::V1_0::VideoFrame> out;
+ for (const TouchVideoFrame& frame : frames) {
+ out.push_back(getHalVideoFrame(frame));
+ }
+ return out;
+}
+
+static uint8_t getActionIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args,
+ std::vector<common::V1_0::PointerProperties>* outPointerProperties,
+ std::vector<common::V1_0::PointerCoords>* outPointerCoords) {
+ outPointerProperties->reserve(args.pointerCount);
+ outPointerCoords->reserve(args.pointerCount);
+ for (size_t i = 0; i < args.pointerCount; i++) {
+ common::V1_0::PointerProperties properties;
+ properties.id = args.pointerProperties[i].id;
+ properties.toolType = getToolType(args.pointerProperties[i].toolType);
+ outPointerProperties->push_back(properties);
+
+ common::V1_0::PointerCoords coords;
+ BitSet64 bits (args.pointerCoords[i].bits);
+ std::vector<float> values;
+ size_t index = 0;
+ while (!bits.isEmpty()) {
+ uint32_t axis = bits.clearFirstMarkedBit();
+ coords.bits |= 1 << static_cast<uint64_t>(getAxis(axis));
+ float value = args.pointerCoords[i].values[index++];
+ values.push_back(value);
+ }
+ coords.values = values;
+ outPointerCoords->push_back(coords);
+ }
+}
+
+static common::V1_0::MotionEvent getMotionEvent(const NotifyMotionArgs& args) {
+ common::V1_0::MotionEvent event;
+ event.deviceId = args.deviceId;
+ event.source = getSource(args.source);
+ event.displayId = args.displayId;
+ event.downTime = args.downTime;
+ event.eventTime = args.eventTime;
+ event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
+ event.actionIndex = getActionIndex(args.action);
+ event.actionButton = getActionButton(args.actionButton);
+ event.flags = getFlags(args.flags);
+ event.policyFlags = getPolicyFlags(args.policyFlags);
+ event.edgeFlags = getEdgeFlags(args.edgeFlags);
+ event.metaState = getMetastate(args.metaState);
+ event.buttonState = getButtonState(args.buttonState);
+ event.xPrecision = args.xPrecision;
+ event.yPrecision = args.yPrecision;
+
+ std::vector<common::V1_0::PointerProperties> pointerProperties;
+ std::vector<common::V1_0::PointerCoords> pointerCoords;
+ getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords);
+ event.pointerProperties = pointerProperties;
+ event.pointerCoords = pointerCoords;
+
+ event.deviceTimestamp = args.deviceTimestamp;
+ event.frames = convertVideoFrames(args.videoFrames);
+
+ return event;
+}
+
+static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
+ static_assert(MotionClassification::NONE ==
+ static_cast<MotionClassification>(common::V1_0::Classification::NONE));
+ static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
+ static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
+ static_assert(MotionClassification::DEEP_PRESS ==
+ static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
+ return static_cast<MotionClassification>(classification);
+}
+
+static bool isTouchEvent(const NotifyMotionArgs& args) {
+ return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
+}
+
+// Check if the "deep touch" feature is on.
+static bool deepPressEnabled() {
+ std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
+ INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
+ std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
+ if (flag_value == "1" || flag_value == "true") {
+ ALOGI("Deep press feature enabled.");
+ return true;
+ }
+ ALOGI("Deep press feature is not enabled.");
+ return false;
+}
+
+
+// --- ClassifierEvent ---
+
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
+ type(ClassifierEventType::MOTION), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
+ type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
+ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
+ type(type), args(std::move(args)) { };
+
+ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
+ type(other.type), args(std::move(other.args)) { };
+
+ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
+ type = other.type;
+ args = std::move(other.args);
+ return *this;
+}
+
+ClassifierEvent ClassifierEvent::createHalResetEvent() {
+ return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
+}
+
+ClassifierEvent ClassifierEvent::createExitEvent() {
+ return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
+}
+
+std::optional<int32_t> ClassifierEvent::getDeviceId() const {
+ switch (type) {
+ case ClassifierEventType::MOTION: {
+ NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
+ return motionArgs->deviceId;
+ }
+ case ClassifierEventType::DEVICE_RESET: {
+ NotifyDeviceResetArgs* deviceResetArgs =
+ static_cast<NotifyDeviceResetArgs*>(args.get());
+ return deviceResetArgs->deviceId;
+ }
+ case ClassifierEventType::HAL_RESET: {
+ return std::nullopt;
+ }
+ case ClassifierEventType::EXIT: {
+ return std::nullopt;
+ }
+ }
+}
+
+// --- MotionClassifier ---
+
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) :
+ mEvents(MAX_EVENTS), mService(service) {
+ mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+ // Under normal operation, we do not need to reset the HAL here. But in the case where system
+ // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
+ // have received events in the past. That means, that HAL could be in an inconsistent state
+ // once it receives events from the newly created MotionClassifier.
+ mEvents.push(ClassifierEvent::createHalResetEvent());
+}
+
+MotionClassifier::~MotionClassifier() {
+ requestExit();
+ mHalThread.join();
+}
+
+void MotionClassifier::ensureHalThread(const char* function) {
+ if (DEBUG) {
+ if (std::this_thread::get_id() != mHalThread.get_id()) {
+ ALOGE("Function %s should only be called from InputClassifier thread", function);
+ }
+ }
+}
+
+/**
+ * Obtain the classification from the HAL for a given MotionEvent.
+ * Should only be called from the InputClassifier thread (mHalThread).
+ * Should not be called from the thread that notifyMotion runs on.
+ *
+ * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
+ * to return a classification, this would directly impact the touch latency.
+ * To remove any possibility of negatively affecting the touch latency, the HAL
+ * is called from a dedicated thread.
+ */
+void MotionClassifier::callInputClassifierHal() {
+ ensureHalThread(__func__);
+ while (true) {
+ ClassifierEvent event = mEvents.pop();
+ bool halResponseOk = true;
+ switch (event.type) {
+ case ClassifierEventType::MOTION: {
+ NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
+ common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs);
+ Return<common::V1_0::Classification> response = mService->classify(motionEvent);
+ halResponseOk = response.isOk();
+ if (halResponseOk) {
+ common::V1_0::Classification halClassification = response;
+ updateClassification(motionArgs->deviceId, motionArgs->eventTime,
+ getMotionClassification(halClassification));
+ }
+ break;
+ }
+ case ClassifierEventType::DEVICE_RESET: {
+ const int32_t deviceId = *(event.getDeviceId());
+ halResponseOk = mService->resetDevice(deviceId).isOk();
+ setClassification(deviceId, MotionClassification::NONE);
+ break;
+ }
+ case ClassifierEventType::HAL_RESET: {
+ halResponseOk = mService->reset().isOk();
+ clearClassifications();
+ break;
+ }
+ case ClassifierEventType::EXIT: {
+ clearClassifications();
+ return;
+ }
+ }
+ if (!halResponseOk) {
+ ALOGE("Error communicating with InputClassifier HAL. "
+ "Exiting MotionClassifier HAL thread");
+ clearClassifications();
+ return;
+ }
+ }
+}
+
+void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
+ bool eventAdded = mEvents.push(std::move(event));
+ if (!eventAdded) {
+ // If the queue is full, suspect the HAL is slow in processing the events.
+ ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime);
+ reset();
+ }
+}
+
+void MotionClassifier::requestExit() {
+ reset();
+ mEvents.push(ClassifierEvent::createExitEvent());
+}
+
+void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
+ MotionClassification classification) {
+ std::scoped_lock lock(mLock);
+ const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
+ if (eventTime < lastDownTime) {
+ // HAL just finished processing an event that belonged to an earlier gesture,
+ // but new gesture is already in progress. Drop this classification.
+ ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
+ nanoseconds_to_milliseconds(lastDownTime - eventTime));
+ return;
+ }
+ mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
+ std::scoped_lock lock(mLock);
+ mClassifications[deviceId] = classification;
+}
+
+void MotionClassifier::clearClassifications() {
+ std::scoped_lock lock(mLock);
+ mClassifications.clear();
+}
+
+MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
+}
+
+void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
+ std::scoped_lock lock(mLock);
+ mLastDownTimes[deviceId] = downTime;
+ mClassifications[deviceId] = MotionClassification::NONE;
+}
+
+MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
+ if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
+ updateLastDownTime(args.deviceId, args.downTime);
+ }
+
+ ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
+ enqueueEvent(std::move(event));
+ return getClassification(args.deviceId);
+}
+
+void MotionClassifier::reset() {
+ mEvents.clear();
+ mEvents.push(ClassifierEvent::createHalResetEvent());
+}
+
+/**
+ * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
+ * Request InputClassifier thread to call resetDevice for this particular device.
+ */
+void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
+ int32_t deviceId = args.deviceId;
+ // Clear the pending events right away, to avoid unnecessary work done by the HAL.
+ mEvents.erase([deviceId](const ClassifierEvent& event) {
+ std::optional<int32_t> eventDeviceId = event.getDeviceId();
+ return eventDeviceId && (*eventDeviceId == deviceId);
+ });
+ enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
+}
+
+void MotionClassifier::dump(std::string& dump) {
+ std::scoped_lock lock(mLock);
+ std::string serviceStatus = mService->ping().isOk() ? "running" : " not responding";
+ dump += StringPrintf(INDENT2 "mService status: %s\n", serviceStatus.c_str());
+ dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
+ mEvents.size(), MAX_EVENTS);
+ dump += INDENT2 "mClassifications, mLastDownTimes:\n";
+ dump += INDENT3 "Device Id\tClassification\tLast down time";
+ // Combine mClassifications and mLastDownTimes into a single table.
+ // Create a superset of device ids.
+ std::unordered_set<int32_t> deviceIds;
+ std::for_each(mClassifications.begin(), mClassifications.end(),
+ [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
+ std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
+ [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
+ for(int32_t deviceId : deviceIds) {
+ const MotionClassification classification =
+ getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
+ const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
+ dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
+ deviceId, motionClassificationToString(classification), downTime);
+ }
+}
+
+
+// --- InputClassifier ---
+
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
+ mListener(listener) {
+ // The rest of the initialization is done in onFirstRef, because we need to obtain
+ // an sp to 'this' in order to register for HAL death notifications
+}
+
+void InputClassifier::onFirstRef() {
+ std::scoped_lock lock(mLock);
+ if (!deepPressEnabled()) {
+ // If feature is not enabled, the InputClassifier will just be in passthrough
+ // mode, and will forward all events to the next InputListener, unmodified
+ return;
+ }
+
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (!service) {
+ // Not really an error, maybe the device does not have this HAL,
+ // but somehow the feature flag is flipped
+ ALOGI("Could not obtain InputClassifier HAL");
+ return;
+ }
+ const bool linked = service->linkToDeath(this, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link android::InputClassifier to the HAL death");
+ return;
+ }
+
+ mMotionClassifier = std::make_unique<MotionClassifier>(service);
+}
+
+void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+ // pass through
+ mListener->notifyConfigurationChanged(args);
+}
+
+void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
+ // pass through
+ mListener->notifyKey(args);
+}
+
+void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
+ std::scoped_lock lock(mLock);
+ // MotionClassifier is only used for touch events, for now
+ const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
+ if (!sendToMotionClassifier) {
+ mListener->notifyMotion(args);
+ return;
+ }
+
+ NotifyMotionArgs newArgs(*args);
+ newArgs.classification = mMotionClassifier->classify(newArgs);
+ mListener->notifyMotion(&newArgs);
+}
+
+void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
+ // pass through
+ mListener->notifySwitch(args);
+}
+
+void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+ std::scoped_lock lock(mLock);
+ if (mMotionClassifier) {
+ mMotionClassifier->reset(*args);
+ }
+ // continue to next stage
+ mListener->notifyDeviceReset(args);
+}
+
+void InputClassifier::serviceDied(uint64_t /*cookie*/,
+ const wp<android::hidl::base::V1_0::IBase>& who) {
+ std::scoped_lock lock(mLock);
+ ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
+ mMotionClassifier = nullptr;
+ sp<android::hidl::base::V1_0::IBase> service = who.promote();
+ if (service) {
+ service->unlinkToDeath(this);
+ }
+}
+
+void InputClassifier::dump(std::string& dump) {
+ std::scoped_lock lock(mLock);
+ dump += "Input Classifier State:\n";
+
+ dump += INDENT1 "Motion Classifier:\n";
+ if (mMotionClassifier) {
+ mMotionClassifier->dump(dump);
+ } else {
+ dump += INDENT2 "<nullptr>";
+ }
+ dump += "\n";
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
new file mode 100644
index 0000000..0b1483f
--- /dev/null
+++ b/services/inputflinger/InputClassifier.h
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_INPUT_CLASSIFIER_H
+#define _UI_INPUT_CLASSIFIER_H
+
+#include <android-base/thread_annotations.h>
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include <thread>
+
+#include "BlockingQueue.h"
+#include "InputListener.h"
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+namespace android {
+
+enum class ClassifierEventType : uint8_t {
+ MOTION = 0,
+ DEVICE_RESET = 1,
+ HAL_RESET = 2,
+ EXIT = 3,
+};
+
+struct ClassifierEvent {
+ ClassifierEventType type;
+ std::unique_ptr<NotifyArgs> args;
+
+ ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
+ ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
+ ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
+ ClassifierEvent(ClassifierEvent&& other);
+ ClassifierEvent& operator=(ClassifierEvent&& other);
+
+ // Convenience function to create a HAL_RESET event
+ static ClassifierEvent createHalResetEvent();
+ // Convenience function to create an EXIT event
+ static ClassifierEvent createExitEvent();
+
+ std::optional<int32_t> getDeviceId() const;
+};
+
+// --- Interfaces ---
+
+/**
+ * Interface for adding a MotionClassification to NotifyMotionArgs.
+ *
+ * To implement, override the classify function.
+ */
+class MotionClassifierInterface {
+public:
+ MotionClassifierInterface() { }
+ virtual ~MotionClassifierInterface() { }
+ /**
+ * Based on the motion event described by NotifyMotionArgs,
+ * provide a MotionClassification for the current gesture.
+ */
+ virtual MotionClassification classify(const NotifyMotionArgs& args) = 0;
+ /**
+ * Reset all internal HAL state.
+ */
+ virtual void reset() = 0;
+ /**
+ * Reset HAL state for a specific device.
+ */
+ virtual void reset(const NotifyDeviceResetArgs& args) = 0;
+
+ /**
+ * Dump the state of the motion classifier
+ */
+ virtual void dump(std::string& dump) = 0;
+};
+
+/**
+ * Base interface for an InputListener stage.
+ * Provides classification to events.
+ */
+class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
+public:
+ /**
+ * Dump the state of the input classifier.
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void dump(std::string& dump) = 0;
+protected:
+ InputClassifierInterface() { }
+ virtual ~InputClassifierInterface() { }
+};
+
+// --- Implementations ---
+
+/**
+ * Implementation of MotionClassifierInterface that calls the InputClassifier HAL
+ * in order to determine the classification for the current gesture.
+ *
+ * The InputClassifier HAL may keep track of the entire gesture in order to determine
+ * the classification, and may be hardware-specific. It may use the data in
+ * NotifyMotionArgs::videoFrames field to drive the classification decisions.
+ * The HAL is called from a separate thread.
+ */
+class MotionClassifier final : public MotionClassifierInterface {
+public:
+ /**
+ * The provided pointer to the service cannot be null.
+ */
+ MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
+ ~MotionClassifier();
+ /**
+ * Classifies events asynchronously; that is, it doesn't block events on a classification,
+ * but instead sends them over to the classifier HAL
+ * and after a classification is determined,
+ * it then marks the next event it sees in the stream with it.
+ *
+ * Therefore, it is acceptable to have the classifications be delayed by 1-2 events
+ * in a particular gesture.
+ */
+ virtual MotionClassification classify(const NotifyMotionArgs& args) override;
+ virtual void reset() override;
+ virtual void reset(const NotifyDeviceResetArgs& args) override;
+
+ virtual void dump(std::string& dump) override;
+
+private:
+ // The events that need to be sent to the HAL.
+ BlockingQueue<ClassifierEvent> mEvents;
+ /**
+ * Add an event to the queue mEvents.
+ */
+ void enqueueEvent(ClassifierEvent&& event);
+ /**
+ * Thread that will communicate with InputClassifier HAL.
+ * This should be the only thread that communicates with InputClassifier HAL,
+ * because this thread is allowed to block on the HAL calls.
+ */
+ std::thread mHalThread;
+ /**
+ * Print an error message if the caller is not on the InputClassifier thread.
+ * Caller must supply the name of the calling function as __function__
+ */
+ void ensureHalThread(const char* function);
+ /**
+ * Call the InputClassifier HAL
+ */
+ void callInputClassifierHal();
+ /**
+ * Access to the InputClassifier HAL. Can always be safely dereferenced.
+ */
+ const sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService;
+ std::mutex mLock;
+ /**
+ * Per-device input classifications. Should only be accessed using the
+ * getClassification / setClassification methods.
+ */
+ std::unordered_map<int32_t /*deviceId*/, MotionClassification>
+ mClassifications GUARDED_BY(mLock);
+ /**
+ * Set the current classification for a given device.
+ */
+ void setClassification(int32_t deviceId, MotionClassification classification);
+ /**
+ * Get the current classification for a given device.
+ */
+ MotionClassification getClassification(int32_t deviceId);
+ void updateClassification(int32_t deviceId, nsecs_t eventTime,
+ MotionClassification classification);
+ /**
+ * Clear all current classifications
+ */
+ void clearClassifications();
+ /**
+ * Per-device times when the last ACTION_DOWN was received.
+ * Used to reject late classifications that do not belong to the current gesture.
+ *
+ * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
+ */
+ std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> mLastDownTimes GUARDED_BY(mLock);
+
+ void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
+
+ /**
+ * Exit the InputClassifier HAL thread.
+ * Useful for tests to ensure proper cleanup.
+ */
+ void requestExit();
+};
+
+
+/**
+ * Implementation of the InputClassifierInterface.
+ * Represents a separate stage of input processing. All of the input events go through this stage.
+ * Acts as a passthrough for all input events except for motion events.
+ * The events of motion type are sent to MotionClassifier.
+ */
+class InputClassifier : public InputClassifierInterface,
+ public android::hardware::hidl_death_recipient {
+public:
+ explicit InputClassifier(const sp<InputListenerInterface>& listener);
+ // Some of the constructor logic is finished in onFirstRef
+ virtual void onFirstRef() override;
+
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override;
+
+ virtual void dump(std::string& dump) override;
+
+private:
+ // Protect access to mMotionClassifier, since it may become null via a hidl callback
+ std::mutex mLock;
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
+ // The next stage to pass input events to
+ sp<InputListenerInterface> mListener;
+};
+
+} // namespace android
+#endif
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9a449fa..1317620 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -46,6 +46,7 @@
#include "InputDispatcher.h"
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <sstream>
#include <stddef.h>
@@ -58,6 +59,7 @@
#include <utils/Trace.h>
#include <powermanager/PowerManager.h>
#include <ui/Region.h>
+#include <binder/Binder.h>
#define INDENT " "
#define INDENT2 " "
@@ -96,6 +98,9 @@
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
+// Sequence number for synthesized or injected events.
+constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -211,10 +216,6 @@
return true;
}
-static bool isMainDisplay(int32_t displayId) {
- return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
-}
-
static void dumpRegion(std::string& dump, const Region& region) {
if (region.isEmpty()) {
dump += "<empty>";
@@ -235,26 +236,34 @@
}
}
+template<typename T, typename U>
+static T getValueByKey(std::unordered_map<U, T>& map, U key) {
+ typename std::unordered_map<U, T>::const_iterator it = map.find(key);
+ return it != map.end() ? it->second : T{};
+}
+
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
- mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
+ mPendingEvent(nullptr), mLastDropReason(DROP_REASON_NOT_DROPPED),
mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
- mNextUnblockedEvent(NULL),
+ mNextUnblockedEvent(nullptr),
mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
+ mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
+ mReporter = createInputReporter();
- mKeyRepeatState.lastKeyEntry = NULL;
+ mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
}
InputDispatcher::~InputDispatcher() {
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
resetKeyRepeatLocked();
releasePendingEventLocked();
@@ -269,8 +278,8 @@
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
- AutoMutex _l(mLock);
- mDispatcherIsAliveCondition.broadcast();
+ std::scoped_lock _l(mLock);
+ mDispatcherIsAlive.notify_all();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
@@ -360,7 +369,7 @@
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if we intend to drop them.
- ALOG_ASSERT(mPendingEvent != NULL);
+ ALOG_ASSERT(mPendingEvent != nullptr);
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
@@ -370,7 +379,7 @@
}
if (mNextUnblockedEvent == mPendingEvent) {
- mNextUnblockedEvent = NULL;
+ mNextUnblockedEvent = nullptr;
}
switch (mPendingEvent->type) {
@@ -393,7 +402,7 @@
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
- if (isAppSwitchKeyEventLocked(typedEntry)) {
+ if (isAppSwitchKeyEvent(typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
@@ -401,7 +410,7 @@
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEventLocked(currentTime, typedEntry)) {
+ && isStaleEvent(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
@@ -417,7 +426,7 @@
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
- && isStaleEventLocked(currentTime, typedEntry)) {
+ && isStaleEvent(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
@@ -455,7 +464,7 @@
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isAppSwitchKeyEventLocked(keyEntry)) {
+ if (isAppSwitchKeyEvent(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
@@ -481,16 +490,16 @@
if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
- && mInputTargetWaitApplicationHandle != NULL) {
+ && 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 != NULL
- && touchedWindowHandle->inputApplicationHandle
- != mInputTargetWaitApplicationHandle) {
+ 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;
@@ -513,11 +522,10 @@
}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
- int32_t x, int32_t y) {
+ int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) {
// Traverse windows from front to back to find touched window.
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+ 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;
@@ -527,14 +535,29 @@
bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
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);
+ }
+ return findTouchedWindowAtLocked(
+ portalToDisplayId, x, y, addOutsideTargets, addPortalWindows);
+ }
// Found window.
return windowHandle;
}
}
+
+ if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
+ mTempTouchState.addOrUpdateWindow(
+ windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
+ }
}
}
}
- return NULL;
+ return nullptr;
}
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
@@ -591,13 +614,13 @@
}
}
-bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) {
+static bool isAppSwitchKeyCode(int32_t keyCode) {
return keyCode == AKEYCODE_HOME
|| keyCode == AKEYCODE_ENDCALL
|| keyCode == AKEYCODE_APP_SWITCH;
}
-bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) {
+bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
&& isAppSwitchKeyCode(keyEntry->keyCode)
&& (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
@@ -620,7 +643,7 @@
#endif
}
-bool InputDispatcher::isStaleEventLocked(nsecs_t currentTime, EventEntry* entry) {
+bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) {
return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
}
@@ -663,7 +686,7 @@
if (mPendingEvent) {
resetANRTimeoutsLocked();
releaseInboundEventLocked(mPendingEvent);
- mPendingEvent = NULL;
+ mPendingEvent = nullptr;
}
}
@@ -673,10 +696,10 @@
#if DEBUG_DISPATCH_CYCLE
ALOGD("Injected inbound event was dropped.");
#endif
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED);
}
if (entry == mNextUnblockedEvent) {
- mNextUnblockedEvent = NULL;
+ mNextUnblockedEvent = nullptr;
}
addRecentEventLocked(entry);
entry->release();
@@ -685,7 +708,7 @@
void InputDispatcher::resetKeyRepeatLocked() {
if (mKeyRepeatState.lastKeyEntry) {
mKeyRepeatState.lastKeyEntry->release();
- mKeyRepeatState.lastKeyEntry = NULL;
+ mKeyRepeatState.lastKeyEntry = nullptr;
}
}
@@ -701,8 +724,8 @@
entry->policyFlags = policyFlags;
entry->repeatCount += 1;
} else {
- KeyEntry* newEntry = new KeyEntry(currentTime,
- entry->deviceId, entry->source, policyFlags,
+ KeyEntry* newEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ entry->deviceId, entry->source, entry->displayId, policyFlags,
entry->action, entry->flags, entry->keyCode, entry->scanCode,
entry->metaState, entry->repeatCount + 1, entry->downTime);
@@ -732,7 +755,7 @@
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
CommandEntry* commandEntry = postCommandLocked(
- & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+ & InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
commandEntry->eventTime = entry->eventTime;
return true;
}
@@ -787,7 +810,7 @@
entry->dispatchInProgress = true;
- logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+ logOutboundKeyDetails("dispatchKey - ", entry);
}
// Handle case where the policy asked us to try again later last time.
@@ -807,8 +830,11 @@
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
- if (mFocusedWindowHandle != NULL) {
- commandEntry->inputWindowHandle = mFocusedWindowHandle;
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
+ if (focusedWindowHandle != nullptr) {
+ commandEntry->inputChannel =
+ getInputChannelLocked(focusedWindowHandle->getToken());
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
@@ -824,38 +850,40 @@
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+ mReporter->reportDroppedKey(entry->sequenceNum);
return true;
}
// Identify targets.
- Vector<InputTarget> inputTargets;
+ std::vector<InputTarget> inputTargets;
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
- setInjectionResultLocked(entry, injectionResult);
+ setInjectionResult(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true;
}
- addMonitoringTargetsLocked(inputTargets);
+ // Add monitor channels from event's or focused display.
+ addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
-void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
+void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
- "repeatCount=%d, downTime=%" PRId64,
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
+ "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+ "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
prefix,
- entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
entry->repeatCount, entry->downTime);
#endif
@@ -867,12 +895,12 @@
if (! entry->dispatchInProgress) {
entry->dispatchInProgress = true;
- logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+ logOutboundMotionDetails("dispatchMotion - ", entry);
}
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true;
}
@@ -880,7 +908,7 @@
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
// Identify targets.
- Vector<InputTarget> inputTargets;
+ std::vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
int32_t injectionResult;
@@ -897,7 +925,7 @@
return false;
}
- setInjectionResultLocked(entry, injectionResult);
+ setInjectionResult(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
CancelationOptions::Mode mode(isPointerEvent ?
@@ -909,7 +937,24 @@
return true;
}
- addMonitoringTargetsLocked(inputTargets);
+ // Add monitor channels from event's or focused display.
+ addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+
+ if (isPointerEvent) {
+ ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
+ if (stateIndex >= 0) {
+ const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+ if (!state.portalWindows.empty()) {
+ // The event has gone through these portal windows, so we add monitoring targets of
+ // the corresponding displays as well.
+ for (size_t i = 0; i < state.portalWindows.size(); i++) {
+ const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo();
+ addMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+ -windowInfo->frameLeft, -windowInfo->frameTop);
+ }
+ }
+ }
+ }
// Dispatch the motion.
if (conflictingPointerActions) {
@@ -922,14 +967,15 @@
}
-void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
+void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ ", policyFlags=0x%x, "
"action=0x%x, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
prefix,
- entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
entry->action, entry->actionButton, entry->flags,
entry->metaState, entry->buttonState,
entry->edgeFlags, entry->xPrecision, entry->yPrecision,
@@ -956,7 +1002,7 @@
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
+ EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("dispatchEventToCurrentInputTargets");
#endif
@@ -965,9 +1011,7 @@
pokeUserActivityLocked(eventEntry);
- for (size_t i = 0; i < inputTargets.size(); i++) {
- const InputTarget& inputTarget = inputTargets.itemAt(i);
-
+ for (const InputTarget& inputTarget : inputTargets) {
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
@@ -987,7 +1031,7 @@
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
- if (applicationHandle == NULL && windowHandle == NULL) {
+ 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);
@@ -996,19 +1040,19 @@
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationHandle.clear();
+ 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",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str(),
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
reason);
#endif
nsecs_t timeout;
- if (windowHandle != NULL) {
+ if (windowHandle != nullptr) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else if (applicationHandle != NULL) {
+ } else if (applicationHandle != nullptr) {
timeout = applicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
@@ -1019,13 +1063,13 @@
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationHandle.clear();
+ mInputTargetWaitApplicationToken.clear();
- if (windowHandle != NULL) {
- mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
+ if (windowHandle != nullptr) {
+ mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
}
- if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
- mInputTargetWaitApplicationHandle = applicationHandle;
+ if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
+ mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
}
}
}
@@ -1051,6 +1095,13 @@
}
}
+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<InputChannel>& inputChannel) {
if (newTimeout > 0) {
@@ -1065,17 +1116,10 @@
ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- sp<InputWindowHandle> windowHandle = connection->inputWindowHandle;
+ sp<IBinder> token = connection->inputChannel->getToken();
- if (windowHandle != NULL) {
- const InputWindowInfo* info = windowHandle->getInfo();
- if (info) {
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(info->displayId);
- if (stateIndex >= 0) {
- mTouchStatesByDisplay.editValueAt(stateIndex).removeWindow(
- windowHandle);
- }
- }
+ if (token != nullptr) {
+ removeWindowByTokenLocked(token);
}
if (connection->status == Connection::STATUS_NORMAL) {
@@ -1103,49 +1147,82 @@
// Reset input target wait timeout.
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
- mInputTargetWaitApplicationHandle.clear();
+ mInputTargetWaitApplicationToken.clear();
+}
+
+/**
+ * Get the display id that the given event should go to. If this event specifies a valid display id,
+ * then it should be dispatched to that display. Otherwise, the event goes to the focused display.
+ * Focused display is the display that the user most recently interacted with.
+ */
+int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) {
+ int32_t displayId;
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY: {
+ const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
+ displayId = typedEntry->displayId;
+ break;
+ }
+ case EventEntry::TYPE_MOTION: {
+ const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
+ displayId = typedEntry->displayId;
+ break;
+ }
+ default: {
+ ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+ return ADISPLAY_ID_NONE;
+ }
+ }
+ return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
}
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
+ const EventEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
int32_t injectionResult;
std::string reason;
+ int32_t displayId = getTargetDisplayId(entry);
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ sp<InputApplicationHandle> focusedApplicationHandle =
+ getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
// If there is no currently focused window and no focused application
// then drop the event.
- if (mFocusedWindowHandle == NULL) {
- if (mFocusedApplicationHandle != NULL) {
+ if (focusedWindowHandle == nullptr) {
+ if (focusedApplicationHandle != nullptr) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplicationHandle, NULL, nextWakeupTime,
+ 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.");
+ ALOGI("Dropping event because there is no focused window or focused application in display "
+ "%" PRId32 ".", displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
// Check permissions.
- if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
+ if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
// Check whether the window is ready for more input.
reason = checkWindowReadyForMoreInputLocked(currentTime,
- mFocusedWindowHandle, entry, "focused");
+ focusedWindowHandle, entry, "focused");
if (!reason.empty()) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.c_str());
+ focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str());
goto Unresponsive;
}
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- addWindowTargetLocked(mFocusedWindowHandle,
+ addWindowTargetLocked(focusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
inputTargets);
@@ -1153,8 +1230,7 @@
Failed:
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatisticsLocked(currentTime, entry,
- injectionResult, timeSpentWaitingForApplication);
+ updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
ALOGD("findFocusedWindow finished: injectionResult=%d, "
"timeSpentWaitingForApplication=%0.1fms",
@@ -1164,7 +1240,7 @@
}
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
+ const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) {
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
@@ -1186,7 +1262,7 @@
// 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.
- const TouchState* oldState = NULL;
+ const TouchState* oldState = nullptr;
ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
if (oldStateIndex >= 0) {
oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
@@ -1209,7 +1285,8 @@
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.");
+ ALOGD("Dropping event because a pointer for a different device is already down "
+ "in display %" PRId32, displayId);
#endif
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_FAILED;
@@ -1225,7 +1302,8 @@
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.");
+ ALOGI("Dropping move event because a pointer for a different device is already active "
+ "in display %" PRId32, displayId);
#endif
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
@@ -1242,54 +1320,27 @@
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> newTouchedWindowHandle;
- bool isTouchModal = false;
-
- // Traverse windows from front to back to find touched window and outside targets.
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- if (windowInfo->displayId != displayId) {
- continue; // wrong display
- }
-
- int32_t flags = windowInfo->layoutParamsFlags;
- if (windowInfo->visible) {
- if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
- isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
- | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
- if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- newTouchedWindowHandle = windowHandle;
- break; // found touched window, exit window loop
- }
- }
-
- if (maskedAction == AMOTION_EVENT_ACTION_DOWN
- && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(
- windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
- }
- }
- }
+ sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
+ displayId, x, y, maskedAction == AMOTION_EVENT_ACTION_DOWN, true);
// Figure out whether splitting will be allowed for this window.
- if (newTouchedWindowHandle != NULL
+ if (newTouchedWindowHandle != nullptr
&& newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
// New window supports splitting.
isSplit = true;
} else if (isSplit) {
// New window does not support splitting but we have already split events.
// Ignore the new window.
- newTouchedWindowHandle = NULL;
+ newTouchedWindowHandle = nullptr;
}
// Handle the case where we did not find a window.
- if (newTouchedWindowHandle == NULL) {
+ if (newTouchedWindowHandle == nullptr) {
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
- if (newTouchedWindowHandle == NULL) {
- ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y);
+ if (newTouchedWindowHandle == nullptr) {
+ ALOGI("Dropping event because there is no touchable window at (%d, %d) in display "
+ "%" PRId32 ".", x, y, displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1327,7 +1378,7 @@
if (! mTempTouchState.down) {
#if DEBUG_FOCUS
ALOGD("Dropping event because the pointer is not down or we previously "
- "dropped the pointer down event.");
+ "dropped the pointer down event in display %" PRId32, displayId);
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
@@ -1345,11 +1396,12 @@
sp<InputWindowHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y);
if (oldTouchedWindowHandle != newTouchedWindowHandle
- && newTouchedWindowHandle != NULL) {
+ && newTouchedWindowHandle != nullptr) {
#if DEBUG_FOCUS
- ALOGD("Touch is slipping out of window %s into window %s.",
+ ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
oldTouchedWindowHandle->getName().c_str(),
- newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle->getName().c_str(),
+ displayId);
#endif
// Make a slippery exit from the old window.
mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
@@ -1380,7 +1432,7 @@
if (newHoverWindowHandle != mLastHoverWindowHandle) {
// Let the previous window know that the hover sequence is over.
- if (mLastHoverWindowHandle != NULL) {
+ if (mLastHoverWindowHandle != nullptr) {
#if DEBUG_HOVER
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
@@ -1390,7 +1442,7 @@
}
// Let the new window know that the hover sequence is starting.
- if (newHoverWindowHandle != NULL) {
+ if (newHoverWindowHandle != nullptr) {
#if DEBUG_HOVER
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
@@ -1404,8 +1456,7 @@
// is at least one touched foreground window.
{
bool haveForegroundWindow = false;
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
if (! checkInjectionPermission(touchedWindow.windowHandle,
@@ -1418,7 +1469,8 @@
}
if (! haveForegroundWindow) {
#if DEBUG_FOCUS
- ALOGD("Dropping event because there is no touched foreground window to receive it.");
+ ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
+ " to receive it.", displayId);
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
@@ -1434,8 +1486,7 @@
sp<InputWindowHandle> foregroundWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
@@ -1447,15 +1498,14 @@
}
// Ensure all touched foreground windows are ready for new input.
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ 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,
- NULL, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
+ nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str());
goto Unresponsive;
}
}
@@ -1471,8 +1521,9 @@
sp<InputWindowHandle> foregroundWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle->getInfo()->hasWallpaper) {
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+ 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
@@ -1490,8 +1541,7 @@
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
+ for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
@@ -1503,7 +1553,7 @@
Failed:
// Check injection permission once and for all.
if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
- if (checkInjectionPermission(NULL, entry->injectionState)) {
+ if (checkInjectionPermission(nullptr, entry->injectionState)) {
injectionPermission = INJECTION_PERMISSION_GRANTED;
} else {
injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1554,11 +1604,11 @@
uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
- TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+ TouchedWindow& touchedWindow = mTempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
touchedWindow.pointerIds.clearBit(pointerId);
if (touchedWindow.pointerIds.isEmpty()) {
- mTempTouchState.windows.removeAt(i);
+ mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i);
continue;
}
}
@@ -1595,8 +1645,7 @@
mTempTouchState.reset();
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
- updateDispatchStatisticsLocked(currentTime, entry,
- injectionResult, timeSpentWaitingForApplication);
+ updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
"timeSpentWaitingForApplication=%0.1fms",
@@ -1606,40 +1655,58 @@
}
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
- inputTargets.push();
+ int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) {
+ sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ return;
+ }
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- InputTarget& target = inputTargets.editTop();
- target.inputChannel = windowInfo->inputChannel;
+ InputTarget target;
+ target.inputChannel = inputChannel;
target.flags = targetFlags;
target.xOffset = - windowInfo->frameLeft;
target.yOffset = - windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
target.pointerIds = pointerIds;
+ inputTargets.push_back(target);
}
-void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- inputTargets.push();
+void InputDispatcher::addMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
+ int32_t displayId, float xOffset, float yOffset) {
+ std::unordered_map<int32_t, std::vector<sp<InputChannel>>>::const_iterator it =
+ mMonitoringChannelsByDisplay.find(displayId);
- InputTarget& target = inputTargets.editTop();
- target.inputChannel = mMonitoringChannels[i];
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = 0;
- target.yOffset = 0;
- target.pointerIds.clear();
- target.scaleFactor = 1.0f;
+ if (it != mMonitoringChannelsByDisplay.end()) {
+ const std::vector<sp<InputChannel>>& monitoringChannels = it->second;
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ InputTarget target;
+ target.inputChannel = monitoringChannels[i];
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.xOffset = xOffset;
+ target.yOffset = yOffset;
+ target.pointerIds.clear();
+ target.globalScaleFactor = 1.0f;
+ inputTargets.push_back(target);
+ }
+ } else {
+ // If there is no monitor channel registered or all monitor channel unregistered,
+ // the display can't detect the extra system gesture by a copy of input events.
+ ALOGW("There is no monitor channel found in display %" PRId32, displayId);
}
}
bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState) {
if (injectionState
- && (windowHandle == NULL
+ && (windowHandle == nullptr
|| windowHandle->getInfo()->ownerUid != injectionState->injectorUid)
&& !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
- if (windowHandle != NULL) {
+ if (windowHandle != nullptr) {
ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
"owned by uid %d",
injectionState->injectorPid, injectionState->injectorUid,
@@ -1657,9 +1724,8 @@
bool InputDispatcher::isWindowObscuredAtPointLocked(
const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
int32_t displayId = windowHandle->getInfo()->displayId;
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+ const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (otherHandle == windowHandle) {
break;
}
@@ -1677,10 +1743,9 @@
bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
int32_t displayId = windowHandle->getInfo()->displayId;
+ const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+ for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (otherHandle == windowHandle) {
break;
}
@@ -1704,7 +1769,8 @@
}
// If the window's connection is not registered then keep waiting.
- ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
+ ssize_t connectionIndex = getConnectionIndexLocked(
+ getInputChannelLocked(windowHandle->getToken()));
if (connectionIndex < 0) {
return StringPrintf("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the process "
@@ -1775,11 +1841,11 @@
return "";
}
-std::string InputDispatcher::getApplicationWindowLabelLocked(
+std::string InputDispatcher::getApplicationWindowLabel(
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
- if (applicationHandle != NULL) {
- if (windowHandle != NULL) {
+ if (applicationHandle != nullptr) {
+ if (windowHandle != nullptr) {
std::string label(applicationHandle->getName());
label += " - ";
label += windowHandle->getName();
@@ -1787,7 +1853,7 @@
} else {
return applicationHandle->getName();
}
- } else if (windowHandle != NULL) {
+ } else if (windowHandle != nullptr) {
return windowHandle->getName();
} else {
return "<unknown application or window>";
@@ -1795,8 +1861,11 @@
}
void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
- if (mFocusedWindowHandle != NULL) {
- const InputWindowInfo* info = mFocusedWindowHandle->getInfo();
+ int32_t displayId = getTargetDisplayId(eventEntry);
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ if (focusedWindowHandle != nullptr) {
+ const InputWindowInfo* info = focusedWindowHandle->getInfo();
if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
@@ -1838,11 +1907,13 @@
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, scaleFactor=%f, "
- "pointerIds=0x%x",
+ "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+ "windowScaleFactor=(%f, %f), pointerIds=0x%x",
connection->getInputChannelName().c_str(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor, inputTarget->pointerIds.value);
+ inputTarget->globalScaleFactor,
+ inputTarget->windowXScale, inputTarget->windowYScale,
+ inputTarget->pointerIds.value);
#endif
// Skip this event if the connection status is not normal.
@@ -1869,7 +1940,7 @@
#if DEBUG_FOCUS
ALOGD("channel '%s' ~ Split motion event.",
connection->getInputChannelName().c_str());
- logOutboundMotionDetailsLocked(" ", splitMotionEntry);
+ logOutboundMotionDetails(" ", splitMotionEntry);
#endif
enqueueDispatchEntriesLocked(currentTime, connection,
splitMotionEntry, inputTarget);
@@ -1887,17 +1958,17 @@
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -1906,7 +1977,7 @@
}
}
-void InputDispatcher::enqueueDispatchEntryLocked(
+void InputDispatcher::enqueueDispatchEntry(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
@@ -1919,7 +1990,8 @@
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor);
+ inputTarget->globalScaleFactor, inputTarget->windowXScale,
+ inputTarget->windowYScale);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
@@ -1988,12 +2060,12 @@
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatchesLocked(eventEntry);
+ incrementPendingForegroundDispatches(eventEntry);
}
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -2017,7 +2089,7 @@
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
- keyEntry->deviceId, keyEntry->source,
+ keyEntry->deviceId, keyEntry->source, keyEntry->displayId,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
@@ -2035,13 +2107,15 @@
float xOffset, yOffset;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- float scaleFactor = dispatchEntry->scaleFactor;
- xOffset = dispatchEntry->xOffset * scaleFactor;
- yOffset = dispatchEntry->yOffset * scaleFactor;
- if (scaleFactor != 1.0f) {
+ float globalScaleFactor = dispatchEntry->globalScaleFactor;
+ float wxs = dispatchEntry->windowXScale;
+ float wys = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * wxs;
+ yOffset = dispatchEntry->yOffset * wys;
+ if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(scaleFactor);
+ scaledCoords[i].scale(globalScaleFactor, wxs, wys);
}
usingCoords = scaledCoords;
}
@@ -2063,7 +2137,7 @@
motionEntry->deviceId, motionEntry->source, motionEntry->displayId,
dispatchEntry->resolvedAction, motionEntry->actionButton,
dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
- motionEntry->metaState, motionEntry->buttonState,
+ motionEntry->metaState, motionEntry->buttonState, motionEntry->classification,
xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, motionEntry->eventTime,
motionEntry->pointerCount, motionEntry->pointerProperties,
@@ -2106,9 +2180,9 @@
// Re-enqueue the event on the wait queue.
connection->outboundQueue.dequeue(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
connection->waitQueue.enqueueAtTail(dispatchEntry);
- traceWaitQueueLengthLocked(connection);
+ traceWaitQueueLength(connection);
}
}
@@ -2138,10 +2212,10 @@
#endif
// Clear the dispatch queues.
- drainDispatchQueueLocked(&connection->outboundQueue);
- traceOutboundQueueLengthLocked(connection);
- drainDispatchQueueLocked(&connection->waitQueue);
- traceWaitQueueLengthLocked(connection);
+ drainDispatchQueue(&connection->outboundQueue);
+ traceOutboundQueueLength(connection);
+ drainDispatchQueue(&connection->waitQueue);
+ traceWaitQueueLength(connection);
// The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
@@ -2155,16 +2229,16 @@
}
}
-void InputDispatcher::drainDispatchQueueLocked(Queue<DispatchEntry>* queue) {
+void InputDispatcher::drainDispatchQueue(Queue<DispatchEntry>* queue) {
while (!queue->isEmpty()) {
DispatchEntry* dispatchEntry = queue->dequeueAtHead();
- releaseDispatchEntryLocked(dispatchEntry);
+ releaseDispatchEntry(dispatchEntry);
}
}
-void InputDispatcher::releaseDispatchEntryLocked(DispatchEntry* dispatchEntry) {
+void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
if (dispatchEntry->hasForegroundTarget()) {
- decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
+ decrementPendingForegroundDispatches(dispatchEntry->eventEntry);
}
delete dispatchEntry;
}
@@ -2173,7 +2247,7 @@
InputDispatcher* d = static_cast<InputDispatcher*>(data);
{ // acquire lock
- AutoMutex _l(d->mLock);
+ std::scoped_lock _l(d->mLock);
ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
if (connectionIndex < 0) {
@@ -2233,7 +2307,7 @@
} // release lock
}
-void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
+void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked (
const CancelationOptions& options) {
for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
synthesizeCancelationEventsForConnectionLocked(
@@ -2241,10 +2315,14 @@
}
}
-void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked (
const CancelationOptions& options) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options);
+ for (auto& it : mMonitoringChannelsByDisplay) {
+ const std::vector<sp<InputChannel>>& monitoringChannels = it.second;
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options);
+ }
}
}
@@ -2265,11 +2343,11 @@
nsecs_t currentTime = now();
- Vector<EventEntry*> cancelationEvents;
+ std::vector<EventEntry*> cancelationEvents;
connection->inputState.synthesizeCancelationEvents(currentTime,
cancelationEvents, options);
- if (!cancelationEvents.isEmpty()) {
+ if (!cancelationEvents.empty()) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
"with reality: %s, mode=%d.",
@@ -2277,34 +2355,37 @@
options.reason, options.mode);
#endif
for (size_t i = 0; i < cancelationEvents.size(); i++) {
- EventEntry* cancelationEventEntry = cancelationEvents.itemAt(i);
+ EventEntry* cancelationEventEntry = cancelationEvents[i];
switch (cancelationEventEntry->type) {
case EventEntry::TYPE_KEY:
- logOutboundKeyDetailsLocked("cancel - ",
+ logOutboundKeyDetails("cancel - ",
static_cast<KeyEntry*>(cancelationEventEntry));
break;
case EventEntry::TYPE_MOTION:
- logOutboundMotionDetailsLocked("cancel - ",
+ logOutboundMotionDetails("cancel - ",
static_cast<MotionEntry*>(cancelationEventEntry));
break;
}
InputTarget target;
- sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel);
- if (windowHandle != NULL) {
+ sp<InputWindowHandle> windowHandle = getWindowHandleLocked(
+ connection->inputChannel->getToken());
+ if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
target.xOffset = -windowInfo->frameLeft;
target.yOffset = -windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
} else {
target.xOffset = 0;
target.yOffset = 0;
- target.scaleFactor = 1.0f;
+ target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+ enqueueDispatchEntry(connection, cancelationEventEntry, // increments ref
&target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
@@ -2349,7 +2430,7 @@
"we expected there to be %d pointers. This probably means we received "
"a broken sequence of pointer ids from the input device.",
splitPointerCount, pointerIds.count());
- return NULL;
+ return nullptr;
}
int32_t action = originalMotionEntry->action;
@@ -2381,20 +2462,22 @@
}
MotionEntry* splitMotionEntry = new MotionEntry(
+ originalMotionEntry->sequenceNum,
originalMotionEntry->eventTime,
originalMotionEntry->deviceId,
originalMotionEntry->source,
+ originalMotionEntry->displayId,
originalMotionEntry->policyFlags,
action,
originalMotionEntry->actionButton,
originalMotionEntry->flags,
originalMotionEntry->metaState,
originalMotionEntry->buttonState,
+ originalMotionEntry->classification,
originalMotionEntry->edgeFlags,
originalMotionEntry->xPrecision,
originalMotionEntry->yPrecision,
originalMotionEntry->downTime,
- originalMotionEntry->displayId,
splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
if (originalMotionEntry->injectionState) {
@@ -2412,9 +2495,10 @@
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);
+ ConfigurationChangedEntry* newEntry =
+ new ConfigurationChangedEntry(args->sequenceNum, args->eventTime);
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
@@ -2423,12 +2507,49 @@
}
}
+/**
+ * If one of the meta shortcuts is detected, process them here:
+ * Meta + Backspace -> generate BACK
+ * Meta + Enter -> generate HOME
+ * This will potentially overwrite keyCode and metaState.
+ */
+void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
+ int32_t& keyCode, int32_t& metaState) {
+ if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
+ int32_t newKeyCode = AKEYCODE_UNKNOWN;
+ if (keyCode == AKEYCODE_DEL) {
+ newKeyCode = AKEYCODE_BACK;
+ } else if (keyCode == AKEYCODE_ENTER) {
+ newKeyCode = AKEYCODE_HOME;
+ }
+ if (newKeyCode != AKEYCODE_UNKNOWN) {
+ std::scoped_lock _l(mLock);
+ struct KeyReplacement replacement = {keyCode, deviceId};
+ mReplacedKeys.add(replacement, newKeyCode);
+ keyCode = newKeyCode;
+ metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
+ }
+ } else if (action == AKEY_EVENT_ACTION_UP) {
+ // In order to maintain a consistent stream of up and down events, check to see if the key
+ // going up is one we've replaced in a down event and haven't yet replaced in an up event,
+ // 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);
+ metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
+ }
+ }
+}
+
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("notifyKey - eventTime=%" PRId64
- ", deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
+ ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, "
"flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
- args->eventTime, args->deviceId, args->source, args->policyFlags,
+ args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
args->action, args->flags, args->keyCode, args->scanCode,
args->metaState, args->downTime);
#endif
@@ -2439,6 +2560,9 @@
uint32_t policyFlags = args->policyFlags;
int32_t flags = args->flags;
int32_t metaState = args->metaState;
+ // InputDispatcher tracks and generates key repeats on behalf of
+ // whatever notifies it, so repeatCount should always be set to 0
+ constexpr int32_t repeatCount = 0;
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
policyFlags |= POLICY_FLAG_VIRTUAL;
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
@@ -2450,37 +2574,11 @@
policyFlags |= POLICY_FLAG_TRUSTED;
int32_t keyCode = args->keyCode;
- if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
- int32_t newKeyCode = AKEYCODE_UNKNOWN;
- if (keyCode == AKEYCODE_DEL) {
- newKeyCode = AKEYCODE_BACK;
- } else if (keyCode == AKEYCODE_ENTER) {
- newKeyCode = AKEYCODE_HOME;
- }
- if (newKeyCode != AKEYCODE_UNKNOWN) {
- AutoMutex _l(mLock);
- struct KeyReplacement replacement = {keyCode, args->deviceId};
- mReplacedKeys.add(replacement, newKeyCode);
- keyCode = newKeyCode;
- metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
- }
- } else if (args->action == AKEY_EVENT_ACTION_UP) {
- // In order to maintain a consistent stream of up and down events, check to see if the key
- // going up is one we've replaced in a down event and haven't yet replaced in an up event,
- // even if the modifier was released between the down and the up events.
- AutoMutex _l(mLock);
- struct KeyReplacement replacement = {keyCode, args->deviceId};
- ssize_t index = mReplacedKeys.indexOfKey(replacement);
- if (index >= 0) {
- keyCode = mReplacedKeys.valueAt(index);
- mReplacedKeys.removeItemsAt(index);
- metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
- }
- }
+ accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
KeyEvent event;
- event.initialize(args->deviceId, args->source, args->action,
- flags, keyCode, args->scanCode, metaState, 0,
+ event.initialize(args->deviceId, args->source, args->displayId, args->action,
+ flags, keyCode, args->scanCode, metaState, repeatCount,
args->downTime, args->eventTime);
android::base::Timer t;
@@ -2505,9 +2603,8 @@
mLock.lock();
}
- int32_t repeatCount = 0;
- KeyEntry* newEntry = new KeyEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
+ KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime,
+ args->deviceId, args->source, args->displayId, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
@@ -2526,10 +2623,11 @@
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, "
+ ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ ", policyFlags=0x%x, "
"action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- args->eventTime, args->deviceId, args->source, args->policyFlags,
+ args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
args->action, args->actionButton, args->flags, args->metaState, args->buttonState,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
for (uint32_t i = 0; i < args->pointerCount; i++) {
@@ -2559,7 +2657,7 @@
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
+ mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -2573,9 +2671,10 @@
mLock.unlock();
MotionEvent event;
- event.initialize(args->deviceId, args->source, args->action, args->actionButton,
+ event.initialize(args->deviceId, args->source, args->displayId,
+ args->action, args->actionButton,
args->flags, args->edgeFlags, args->metaState, args->buttonState,
- 0, 0, args->xPrecision, args->yPrecision,
+ args->classification, 0, 0, args->xPrecision, args->yPrecision,
args->downTime, args->eventTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
@@ -2588,12 +2687,11 @@
}
// Just enqueue a new motion event.
- MotionEntry* newEntry = new MotionEntry(args->eventTime,
- args->deviceId, args->source, policyFlags,
+ MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime,
+ args->deviceId, args->source, args->displayId, policyFlags,
args->action, args->actionButton, args->flags,
- args->metaState, args->buttonState,
+ args->metaState, args->buttonState, args->classification,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
- args->displayId,
args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
needWake = enqueueInboundEventLocked(newEntry);
@@ -2606,8 +2704,7 @@
}
bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
- // TODO: support sending secondary display events to input filter
- return mInputFilterEnabled && isMainDisplay(args->displayId);
+ return mInputFilterEnabled;
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
@@ -2631,9 +2728,10 @@
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- DeviceResetEntry* newEntry = new DeviceResetEntry(args->eventTime, args->deviceId);
+ DeviceResetEntry* newEntry =
+ new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId);
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
@@ -2642,14 +2740,13 @@
}
}
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId,
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x, displayId=%d",
- event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags,
- displayId);
+ "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
#endif
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
@@ -2663,20 +2760,29 @@
EventEntry* lastInjectedEntry;
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
- const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
- int32_t action = keyEvent->getAction();
+ KeyEvent keyEvent;
+ keyEvent.initialize(*static_cast<const KeyEvent*>(event));
+ int32_t action = keyEvent.getAction();
if (! validateKeyEvent(action)) {
return INPUT_EVENT_INJECTION_FAILED;
}
- int32_t flags = keyEvent->getFlags();
+ int32_t flags = keyEvent.getFlags();
+ int32_t keyCode = keyEvent.getKeyCode();
+ int32_t metaState = keyEvent.getMetaState();
+ accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+ /*byref*/ keyCode, /*byref*/ metaState);
+ keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
+ action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
+ keyEvent.getDownTime(), keyEvent.getEventTime());
+
if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
policyFlags |= POLICY_FLAG_VIRTUAL;
}
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
android::base::Timer t;
- mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+ mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -2684,11 +2790,11 @@
}
mLock.lock();
- firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
- keyEvent->getDeviceId(), keyEvent->getSource(),
+ firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
+ keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(),
policyFlags, action, flags,
- keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
- keyEvent->getRepeatCount(), keyEvent->getDownTime());
+ keyEvent.getKeyCode(), keyEvent.getScanCode(), keyEvent.getMetaState(),
+ keyEvent.getRepeatCount(), keyEvent.getDownTime());
lastInjectedEntry = firstInjectedEntry;
break;
}
@@ -2699,6 +2805,7 @@
size_t pointerCount = motionEvent->getPointerCount();
const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
int32_t actionButton = motionEvent->getActionButton();
+ int32_t displayId = motionEvent->getDisplayId();
if (! validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -2706,7 +2813,7 @@
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
nsecs_t eventTime = motionEvent->getEventTime();
android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+ mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -2716,26 +2823,29 @@
mLock.lock();
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
- firstInjectedEntry = new MotionEntry(*sampleEventTimes,
- motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+ firstInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
+ motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(),
+ policyFlags,
action, actionButton, motionEvent->getFlags(),
motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getEdgeFlags(),
+ motionEvent->getClassification(), motionEvent->getEdgeFlags(),
motionEvent->getXPrecision(), motionEvent->getYPrecision(),
- motionEvent->getDownTime(), displayId,
+ motionEvent->getDownTime(),
uint32_t(pointerCount), pointerProperties, samplePointerCoords,
motionEvent->getXOffset(), motionEvent->getYOffset());
lastInjectedEntry = firstInjectedEntry;
for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
samplePointerCoords += pointerCount;
- MotionEntry* nextInjectedEntry = new MotionEntry(*sampleEventTimes,
- motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+ MotionEntry* nextInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM,
+ *sampleEventTimes,
+ motionEvent->getDeviceId(), motionEvent->getSource(),
+ motionEvent->getDisplayId(), policyFlags,
action, actionButton, motionEvent->getFlags(),
motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getEdgeFlags(),
+ motionEvent->getClassification(), motionEvent->getEdgeFlags(),
motionEvent->getXPrecision(), motionEvent->getYPrecision(),
- motionEvent->getDownTime(), displayId,
+ motionEvent->getDownTime(),
uint32_t(pointerCount), pointerProperties, samplePointerCoords,
motionEvent->getXOffset(), motionEvent->getYOffset());
lastInjectedEntry->next = nextInjectedEntry;
@@ -2758,7 +2868,7 @@
lastInjectedEntry->injectionState = injectionState;
bool needWake = false;
- for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {
+ for (EventEntry* entry = firstInjectedEntry; entry != nullptr; ) {
EventEntry* nextEntry = entry->next;
needWake |= enqueueInboundEventLocked(entry);
entry = nextEntry;
@@ -2772,7 +2882,7 @@
int32_t injectionResult;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::unique_lock _l(mLock);
if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
@@ -2793,7 +2903,7 @@
break;
}
- mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
@@ -2813,7 +2923,7 @@
break;
}
- mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
}
}
@@ -2835,7 +2945,7 @@
|| mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}
-void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
#if DEBUG_INJECTION
@@ -2864,122 +2974,211 @@
}
injectionState->injectionResult = injectionResult;
- mInjectionResultAvailableCondition.broadcast();
+ mInjectionResultAvailable.notify_all();
}
}
-void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches += 1;
}
}
-void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches -= 1;
if (injectionState->pendingForegroundDispatches == 0) {
- mInjectionSyncFinishedCondition.broadcast();
+ mInjectionSyncFinished.notify_all();
}
}
}
+std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
+ int32_t displayId) const {
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>::const_iterator it =
+ mWindowHandlesByDisplay.find(displayId);
+ if(it != mWindowHandlesByDisplay.end()) {
+ return it->second;
+ }
+
+ // Return an empty one if nothing found.
+ return std::vector<sp<InputWindowHandle>>();
+}
+
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
- const sp<InputChannel>& inputChannel) const {
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
- if (windowHandle->getInputChannel() == inputChannel) {
- return windowHandle;
+ const sp<IBinder>& windowHandleToken) const {
+ for (auto& it : mWindowHandlesByDisplay) {
+ const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
}
}
- return NULL;
+ return nullptr;
}
-bool InputDispatcher::hasWindowHandleLocked(
- const sp<InputWindowHandle>& windowHandle) const {
- size_t numWindows = mWindowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- if (mWindowHandles.itemAt(i) == windowHandle) {
- return true;
+bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
+ for (auto& it : mWindowHandlesByDisplay) {
+ const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ for (const sp<InputWindowHandle>& handle : windowHandles) {
+ if (handle->getToken() == windowHandle->getToken()) {
+ if (windowHandle->getInfo()->displayId != it.first) {
+ ALOGE("Found window %s in display %" PRId32
+ ", but it should belong to display %" PRId32,
+ windowHandle->getName().c_str(), it.first,
+ windowHandle->getInfo()->displayId);
+ }
+ return true;
+ }
}
}
return false;
}
-void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
+sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+ size_t count = mInputChannelsByToken.count(token);
+ if (count == 0) {
+ return nullptr;
+ }
+ return mInputChannelsByToken.at(token);
+}
+
+/**
+ * Called from InputManagerService, update window handle list by displayId that can receive input.
+ * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
+ * If set an empty list, remove all handles from the specific display.
+ * For focused handle, check if need to change and send a cancel event to previous one.
+ * For removed handle, check if need to send a cancel event if already in touch.
+ */
+void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
+ int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) {
#if DEBUG_FOCUS
- ALOGD("setInputWindows");
+ ALOGD("setInputWindows displayId=%" PRId32, displayId);
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
- mWindowHandles = inputWindowHandles;
+ // Copy old handles for release if they are no longer present.
+ const std::vector<sp<InputWindowHandle>> oldWindowHandles =
+ getWindowHandlesLocked(displayId);
- sp<InputWindowHandle> newFocusedWindowHandle;
+ sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
bool foundHoveredWindow = false;
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
- if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
- mWindowHandles.removeAt(i--);
- continue;
+
+ if (inputWindowHandles.empty()) {
+ // Remove all handles on a display if there are no windows left.
+ mWindowHandlesByDisplay.erase(displayId);
+ } else {
+ // Since we compare the pointer of input window handles across window updates, we need
+ // to make sure the handle object for the same window stays unchanged across updates.
+ const std::vector<sp<InputWindowHandle>>& oldHandles =
+ mWindowHandlesByDisplay[displayId];
+ std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
+ for (const sp<InputWindowHandle>& handle : oldHandles) {
+ oldHandlesByTokens[handle->getToken()] = handle;
}
- if (windowHandle->getInfo()->hasFocus) {
- newFocusedWindowHandle = windowHandle;
+
+ std::vector<sp<InputWindowHandle>> newHandles;
+ for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
+ if (!handle->updateInfo() || (getInputChannelLocked(handle->getToken()) == nullptr
+ && handle->getInfo()->portalToDisplayId == ADISPLAY_ID_NONE)) {
+ ALOGE("Window handle %s has no registered input channel",
+ handle->getName().c_str());
+ continue;
+ }
+
+ if (handle->getInfo()->displayId != displayId) {
+ ALOGE("Window %s updated by wrong display %d, should belong to display %d",
+ handle->getName().c_str(), displayId,
+ handle->getInfo()->displayId);
+ continue;
+ }
+
+ if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
+ const sp<InputWindowHandle> oldHandle =
+ oldHandlesByTokens.at(handle->getToken());
+ oldHandle->updateFrom(handle);
+ newHandles.push_back(oldHandle);
+ } else {
+ newHandles.push_back(handle);
+ }
}
- if (windowHandle == mLastHoverWindowHandle) {
- foundHoveredWindow = true;
+
+ for (const sp<InputWindowHandle>& windowHandle : newHandles) {
+ // Set newFocusedWindowHandle to the top most focused window instead of the last one
+ if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus
+ && windowHandle->getInfo()->visible) {
+ newFocusedWindowHandle = windowHandle;
+ }
+ if (windowHandle == mLastHoverWindowHandle) {
+ foundHoveredWindow = true;
+ }
}
+
+ // Insert or replace
+ mWindowHandlesByDisplay[displayId] = newHandles;
}
if (!foundHoveredWindow) {
- mLastHoverWindowHandle = NULL;
+ mLastHoverWindowHandle = nullptr;
}
- if (mFocusedWindowHandle != newFocusedWindowHandle) {
- if (mFocusedWindowHandle != NULL) {
+ sp<InputWindowHandle> oldFocusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+ if (oldFocusedWindowHandle != newFocusedWindowHandle) {
+ if (oldFocusedWindowHandle != nullptr) {
#if DEBUG_FOCUS
- ALOGD("Focus left window: %s",
- mFocusedWindowHandle->getName().c_str());
+ ALOGD("Focus left window: %s in display %" PRId32,
+ oldFocusedWindowHandle->getName().c_str(), displayId);
#endif
- sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
- if (focusedInputChannel != NULL) {
+ sp<InputChannel> focusedInputChannel = getInputChannelLocked(
+ oldFocusedWindowHandle->getToken());
+ if (focusedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(
focusedInputChannel, options);
}
+ mFocusedWindowHandlesByDisplay.erase(displayId);
}
- if (newFocusedWindowHandle != NULL) {
+ if (newFocusedWindowHandle != nullptr) {
#if DEBUG_FOCUS
- ALOGD("Focus entered window: %s",
- newFocusedWindowHandle->getName().c_str());
+ ALOGD("Focus entered window: %s in display %" PRId32,
+ newFocusedWindowHandle->getName().c_str(), displayId);
#endif
+ mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
}
- mFocusedWindowHandle = newFocusedWindowHandle;
+
+ if (mFocusedDisplayId == displayId) {
+ onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ }
+
}
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+ ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
+ if (stateIndex >= 0) {
+ TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
for (size_t i = 0; i < state.windows.size(); ) {
- TouchedWindow& touchedWindow = state.windows.editItemAt(i);
+ TouchedWindow& touchedWindow = state.windows[i];
if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
#if DEBUG_FOCUS
- ALOGD("Touched window was removed: %s",
- touchedWindow.windowHandle->getName().c_str());
+ ALOGD("Touched window was removed: %s in display %" PRId32,
+ touchedWindow.windowHandle->getName().c_str(), displayId);
#endif
sp<InputChannel> touchedInputChannel =
- touchedWindow.windowHandle->getInputChannel();
- if (touchedInputChannel != NULL) {
+ getInputChannelLocked(touchedWindow.windowHandle->getToken());
+ if (touchedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(
touchedInputChannel, options);
}
- state.windows.removeAt(i);
+ state.windows.erase(state.windows.begin() + i);
} else {
++i;
}
@@ -2990,41 +3189,47 @@
// This ensures that unused input channels are released promptly.
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
- for (size_t i = 0; i < oldWindowHandles.size(); i++) {
- const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
+ for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
if (!hasWindowHandleLocked(oldWindowHandle)) {
#if DEBUG_FOCUS
ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
#endif
- oldWindowHandle->releaseInfo();
+ oldWindowHandle->releaseChannel();
}
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
+
+ if (setInputWindowsListener) {
+ setInputWindowsListener->onSetInputWindowsFinished();
+ }
}
void InputDispatcher::setFocusedApplication(
- const sp<InputApplicationHandle>& inputApplicationHandle) {
+ int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
#if DEBUG_FOCUS
- ALOGD("setFocusedApplication");
+ ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
- if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) {
- if (mFocusedApplicationHandle != inputApplicationHandle) {
- if (mFocusedApplicationHandle != NULL) {
+ sp<InputApplicationHandle> oldFocusedApplicationHandle =
+ getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+ if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
+ if (oldFocusedApplicationHandle != inputApplicationHandle) {
+ if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- mFocusedApplicationHandle->releaseInfo();
+ oldFocusedApplicationHandle->releaseInfo();
}
- mFocusedApplicationHandle = inputApplicationHandle;
+ mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
- } else if (mFocusedApplicationHandle != NULL) {
+ } else if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- mFocusedApplicationHandle->releaseInfo();
- mFocusedApplicationHandle.clear();
+ oldFocusedApplicationHandle->releaseInfo();
+ oldFocusedApplicationHandle.clear();
+ mFocusedApplicationHandlesByDisplay.erase(displayId);
}
#if DEBUG_FOCUS
@@ -3036,6 +3241,65 @@
mLooper->wake();
}
+/**
+ * Sets the focused display, which is responsible for receiving focus-dispatched input events where
+ * the display not specified.
+ *
+ * We track any unreleased events for each window. If a window loses the ability to receive the
+ * released event, we will send a cancel event to it. So when the focused display is changed, we
+ * cancel all the unreleased display-unspecified events for the focused window on the old focused
+ * display. The display-specified events won't be affected.
+ */
+void InputDispatcher::setFocusedDisplay(int32_t displayId) {
+#if DEBUG_FOCUS
+ ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
+#endif
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ if (mFocusedDisplayId != displayId) {
+ sp<InputWindowHandle> oldFocusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
+ if (oldFocusedWindowHandle != nullptr) {
+ sp<InputChannel> inputChannel =
+ getInputChannelLocked(oldFocusedWindowHandle->getToken());
+ if (inputChannel != nullptr) {
+ CancelationOptions options(
+ CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS,
+ "The display which contains this window no longer has focus.");
+ synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
+ }
+ }
+ mFocusedDisplayId = displayId;
+
+ // Sanity check
+ sp<InputWindowHandle> newFocusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+
+ if (newFocusedWindowHandle == nullptr) {
+ ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
+ 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, windowHandle->getName().c_str());
+ }
+ }
+ }
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
#if DEBUG_FOCUS
ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
@@ -3043,7 +3307,7 @@
bool changed;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
@@ -3062,7 +3326,7 @@
}
#if DEBUG_FOCUS
- //logDispatchStateLocked();
+ logDispatchStateLocked();
#endif
} // release lock
@@ -3078,7 +3342,7 @@
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mInputFilterEnabled == enabled) {
return;
@@ -3092,29 +3356,27 @@
mLooper->wake();
}
-bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+ if (fromToken == toToken) {
#if DEBUG_FOCUS
- ALOGD("transferTouchFocus: fromChannel=%s, toChannel=%s",
- fromChannel->getName().c_str(), toChannel->getName().c_str());
+ ALOGD("Trivial transfer to same window.");
#endif
- { // acquire lock
- AutoMutex _l(mLock);
+ return true;
+ }
- sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromChannel);
- sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toChannel);
- if (fromWindowHandle == NULL || toWindowHandle == NULL) {
-#if DEBUG_FOCUS
- ALOGD("Cannot transfer focus because from or to window not found.");
-#endif
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
+ sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken);
+ if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
+ ALOGW("Cannot transfer focus because from or to window not found.");
return false;
}
- if (fromWindowHandle == toWindowHandle) {
#if DEBUG_FOCUS
- ALOGD("Trivial transfer to same window.");
+ ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
+ fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
#endif
- return true;
- }
if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
#if DEBUG_FOCUS
ALOGD("Cannot transfer focus because windows are on different displays.");
@@ -3131,7 +3393,7 @@
int32_t oldTargetFlags = touchedWindow.targetFlags;
BitSet32 pointerIds = touchedWindow.pointerIds;
- state.windows.removeAt(i);
+ state.windows.erase(state.windows.begin() + i);
int32_t newTargetFlags = oldTargetFlags
& (InputTarget::FLAG_FOREGROUND
@@ -3152,6 +3414,9 @@
return false;
}
+
+ sp<InputChannel> fromChannel = getInputChannelLocked(fromToken);
+ sp<InputChannel> toChannel = getInputChannelLocked(toToken);
ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
@@ -3207,17 +3472,35 @@
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
dump += StringPrintf(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
dump += StringPrintf(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
+ dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId);
- if (mFocusedApplicationHandle != NULL) {
- dump += StringPrintf(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
- mFocusedApplicationHandle->getName().c_str(),
- mFocusedApplicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
+ if (!mFocusedApplicationHandlesByDisplay.empty()) {
+ dump += StringPrintf(INDENT "FocusedApplications:\n");
+ for (auto& it : mFocusedApplicationHandlesByDisplay) {
+ const int32_t displayId = it.first;
+ const sp<InputApplicationHandle>& applicationHandle = it.second;
+ dump += StringPrintf(
+ INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n",
+ displayId,
+ applicationHandle->getName().c_str(),
+ applicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0);
+ }
} else {
- dump += StringPrintf(INDENT "FocusedApplication: <null>\n");
+ dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
}
- dump += StringPrintf(INDENT "FocusedWindow: name='%s'\n",
- mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().c_str() : "<null>");
+
+ if (!mFocusedWindowHandlesByDisplay.empty()) {
+ dump += StringPrintf(INDENT "FocusedWindows:\n");
+ for (auto& it : mFocusedWindowHandlesByDisplay) {
+ const int32_t displayId = it.first;
+ const sp<InputWindowHandle>& windowHandle = it.second;
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n",
+ displayId, windowHandle->getName().c_str());
+ }
+ } else {
+ dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
+ }
if (!mTouchStatesByDisplay.isEmpty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -3226,7 +3509,7 @@
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);
- if (!state.windows.isEmpty()) {
+ if (!state.windows.empty()) {
dump += INDENT3 "Windows:\n";
for (size_t i = 0; i < state.windows.size(); i++) {
const TouchedWindow& touchedWindow = state.windows[i];
@@ -3238,49 +3521,71 @@
} else {
dump += INDENT3 "Windows: <none>\n";
}
+ if (!state.portalWindows.empty()) {
+ dump += INDENT3 "Portal windows:\n";
+ for (size_t i = 0; i < state.portalWindows.size(); i++) {
+ const sp<InputWindowHandle> portalWindowHandle = state.portalWindows[i];
+ dump += StringPrintf(INDENT4 "%zu: name='%s'\n",
+ i, portalWindowHandle->getName().c_str());
+ }
+ }
}
} else {
dump += INDENT "TouchStates: <no displays touched>\n";
}
- if (!mWindowHandles.isEmpty()) {
- dump += INDENT "Windows:\n";
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ if (!mWindowHandlesByDisplay.empty()) {
+ for (auto& it : mWindowHandlesByDisplay) {
+ const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
+ if (!windowHandles.empty()) {
+ dump += INDENT2 "Windows:\n";
+ for (size_t i = 0; i < windowHandles.size(); i++) {
+ const sp<InputWindowHandle>& windowHandle = windowHandles[i];
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
- dump += StringPrintf(INDENT2 "%zu: name='%s', displayId=%d, "
- "paused=%s, hasFocus=%s, hasWallpaper=%s, "
- "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], scale=%f, "
- "touchableRegion=",
- i, windowInfo->name.c_str(), windowInfo->displayId,
- toString(windowInfo->paused),
- toString(windowInfo->hasFocus),
- toString(windowInfo->hasWallpaper),
- toString(windowInfo->visible),
- toString(windowInfo->canReceiveKeys),
- windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
- windowInfo->layer,
- windowInfo->frameLeft, windowInfo->frameTop,
- windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->scaleFactor);
- dumpRegion(dump, windowInfo->touchableRegion);
- dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
- dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
- windowInfo->ownerPid, windowInfo->ownerUid,
- windowInfo->dispatchingTimeout / 1000000.0);
+ 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, layer=%d, "
+ "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
+ "touchableRegion=",
+ i, windowInfo->name.c_str(), windowInfo->displayId,
+ windowInfo->portalToDisplayId,
+ toString(windowInfo->paused),
+ toString(windowInfo->hasFocus),
+ toString(windowInfo->hasWallpaper),
+ toString(windowInfo->visible),
+ toString(windowInfo->canReceiveKeys),
+ windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
+ windowInfo->layer,
+ windowInfo->frameLeft, windowInfo->frameTop,
+ windowInfo->frameRight, windowInfo->frameBottom,
+ windowInfo->globalScaleFactor,
+ windowInfo->windowXScale, windowInfo->windowYScale);
+ dumpRegion(dump, windowInfo->touchableRegion);
+ dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
+ dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ windowInfo->ownerPid, windowInfo->ownerUid,
+ windowInfo->dispatchingTimeout / 1000000.0);
+ }
+ } else {
+ dump += INDENT2 "Windows: <none>\n";
+ }
}
} else {
- dump += INDENT "Windows: <none>\n";
+ dump += INDENT "Displays: <none>\n";
}
- if (!mMonitoringChannels.isEmpty()) {
- dump += INDENT "MonitoringChannels:\n";
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- const sp<InputChannel>& channel = mMonitoringChannels[i];
- dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
- }
+ if (!mMonitoringChannelsByDisplay.empty()) {
+ for (auto& it : mMonitoringChannelsByDisplay) {
+ const std::vector<sp<InputChannel>>& monitoringChannels = it.second;
+ dump += StringPrintf(INDENT "MonitoringChannels in display %" PRId32 ":\n", it.first);
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ const sp<InputChannel>& channel = monitoringChannels[i];
+ dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
+ }
+ }
} else {
dump += INDENT "MonitoringChannels: <none>\n";
}
@@ -3397,15 +3702,21 @@
mConfig.keyRepeatTimeout * 0.000001f);
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, int32_t displayId) {
#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),
- toString(monitor));
+ ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
+ inputChannel->getName().c_str(), displayId);
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
+
+ // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
+ // treat inputChannel as monitor channel for displayId.
+ bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE;
+ if (monitor) {
+ inputChannel->setToken(new BBinder());
+ }
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
@@ -3413,13 +3724,17 @@
return BAD_VALUE;
}
- sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
+ sp<Connection> connection = new Connection(inputChannel, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
+ mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+ // Store monitor channel by displayId.
if (monitor) {
- mMonitoringChannels.push(inputChannel);
+ std::vector<sp<InputChannel>>& monitoringChannels =
+ mMonitoringChannelsByDisplay[displayId];
+ monitoringChannels.push_back(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
@@ -3436,7 +3751,7 @@
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
if (status) {
@@ -3462,6 +3777,8 @@
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
mConnectionsByFd.removeItemsAt(connectionIndex);
+ mInputChannelsByToken.erase(inputChannel->getToken());
+
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
}
@@ -3476,20 +3793,33 @@
}
void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- if (mMonitoringChannels[i] == inputChannel) {
- mMonitoringChannels.removeAt(i);
- break;
- }
+ for (auto it = mMonitoringChannelsByDisplay.begin();
+ it != mMonitoringChannelsByDisplay.end(); ) {
+ std::vector<sp<InputChannel>>& monitoringChannels = it->second;
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ if (monitoringChannels[i] == inputChannel) {
+ monitoringChannels.erase(monitoringChannels.begin() + i);
+ break;
+ }
+ }
+ if (monitoringChannels.empty()) {
+ it = mMonitoringChannelsByDisplay.erase(it);
+ } else {
+ ++it;
+ }
}
}
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
- ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
- if (connection->inputChannel.get() == inputChannel.get()) {
- return connectionIndex;
+ if (inputChannel == nullptr) {
+ return -1;
+ }
+
+ for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
+ sp<Connection> connection = mConnectionsByFd.valueAt(i);
+ if (connection->inputChannel->getToken() == inputChannel->getToken()) {
+ return i;
}
}
@@ -3516,6 +3846,16 @@
commandEntry->connection = connection;
}
+void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
+ const sp<InputWindowHandle>& newFocus) {
+ sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
+ sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyFocusChangedLockedInterruptible);
+ commandEntry->oldToken = oldToken;
+ commandEntry->newToken = newToken;
+}
+
void InputDispatcher::onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
@@ -3524,11 +3864,11 @@
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",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str(),
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(),
dispatchLatency, waitDuration, reason);
// Capture a record of the InputDispatcher state at the time of the ANR.
- time_t t = time(NULL);
+ time_t t = time(nullptr);
struct tm tm;
localtime_r(&t, &tm);
char timestr[64];
@@ -3537,7 +3877,7 @@
mLastANRState += INDENT "ANR:\n";
mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
mLastANRState += StringPrintf(INDENT2 "Window: %s\n",
- getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str());
+ 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);
@@ -3546,11 +3886,12 @@
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputWindowHandle = windowHandle;
+ commandEntry->inputChannel = windowHandle != nullptr ?
+ getInputChannelLocked(windowHandle->getToken()) : nullptr;
commandEntry->reason = reason;
}
-void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible (
CommandEntry* commandEntry) {
mLock.unlock();
@@ -3566,25 +3907,34 @@
if (connection->status != Connection::STATUS_ZOMBIE) {
mLock.unlock();
- mPolicy->notifyInputChannelBroken(connection->inputWindowHandle);
+ mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken());
mLock.lock();
}
}
+void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<IBinder> oldToken = commandEntry->oldToken;
+ sp<IBinder> newToken = commandEntry->newToken;
+ mLock.unlock();
+ mPolicy->notifyFocusChanged(oldToken, newToken);
+ mLock.lock();
+}
+
void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
nsecs_t newTimeout = mPolicy->notifyANR(
- commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
+ commandEntry->inputApplicationHandle,
+ commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
commandEntry->reason);
mLock.lock();
resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
- commandEntry->inputWindowHandle != NULL
- ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
+ commandEntry->inputChannel);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -3597,7 +3947,9 @@
mLock.unlock();
android::base::Timer t;
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
+ sp<IBinder> token = commandEntry->inputChannel != nullptr ?
+ commandEntry->inputChannel->getToken() : nullptr;
+ nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token,
&event, entry->policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
@@ -3655,12 +4007,12 @@
// a few things.
if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
connection->waitQueue.dequeue(dispatchEntry);
- traceWaitQueueLengthLocked(connection);
+ traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.enqueueAtHead(dispatchEntry);
- traceOutboundQueueLengthLocked(connection);
+ traceOutboundQueueLength(connection);
} else {
- releaseDispatchEntryLocked(dispatchEntry);
+ releaseDispatchEntry(dispatchEntry);
}
}
@@ -3671,171 +4023,181 @@
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) {
- if (!(keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
- // Get the fallback key state.
- // Clear it out after dispatching the UP.
- int32_t originalKeyCode = keyEntry->keyCode;
- int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
- if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
- connection->inputState.removeFallbackKey(originalKeyCode);
+ if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+ if (!handled) {
+ // Report the key as unhandled, since the fallback was not handled.
+ mReporter->reportUnhandledKey(keyEntry->sequenceNum);
}
+ return false;
+ }
- if (handled || !dispatchEntry->hasForegroundTarget()) {
- // If the application handles the original key for which we previously
- // generated a fallback or if the window is not a foreground window,
- // then cancel the associated fallback key, if any.
- if (fallbackKeyCode != -1) {
- // Dispatch the unhandled key to the policy with the cancel flag.
+ // Get the fallback key state.
+ // Clear it out after dispatching the UP.
+ int32_t originalKeyCode = keyEntry->keyCode;
+ int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
+ if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
+
+ if (handled || !dispatchEntry->hasForegroundTarget()) {
+ // If the application handles the original key for which we previously
+ // generated a fallback or if the window is not a foreground window,
+ // then cancel the associated fallback key, if any.
+ if (fallbackKeyCode != -1) {
+ // Dispatch the unhandled key to the policy with the cancel flag.
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
-#endif
- KeyEvent event;
- initializeKeyEvent(&event, keyEntry);
- event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
-
- mLock.unlock();
-
- mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
- &event, keyEntry->policyFlags, &event);
-
- mLock.lock();
-
- // Cancel the fallback key.
- if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
- "application handled the original non-fallback key "
- "or is no longer a foreground target, "
- "canceling previously dispatched fallback key");
- options.keyCode = fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
- }
- connection->inputState.removeFallbackKey(originalKeyCode);
- }
- } else {
- // If the application did not handle a non-fallback key, first check
- // that we are in a good state to perform unhandled key event processing
- // Then ask the policy what to do with it.
- bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
- && keyEntry->repeatCount == 0;
- if (fallbackKeyCode == -1 && !initialDown) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Skipping unhandled key event processing "
- "since this is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
-#endif
- return false;
- }
-
- // Dispatch the unhandled key to the policy.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Asking policy to perform fallback action. "
+ ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
keyEntry->policyFlags);
#endif
KeyEvent event;
initializeKeyEvent(&event, keyEntry);
+ event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
mLock.unlock();
- bool fallback = mPolicy->dispatchUnhandledKey(connection->inputWindowHandle,
- &event, keyEntry->policyFlags, &event);
+ mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
+ &event, keyEntry->policyFlags, &event);
mLock.lock();
- if (connection->status != Connection::STATUS_NORMAL) {
- connection->inputState.removeFallbackKey(originalKeyCode);
- return false;
- }
-
- // Latch the fallback keycode for this key on an initial down.
- // The fallback keycode cannot change at any other point in the lifecycle.
- if (initialDown) {
- if (fallback) {
- fallbackKeyCode = event.getKeyCode();
- } else {
- fallbackKeyCode = AKEYCODE_UNKNOWN;
- }
- connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
- }
-
- ALOG_ASSERT(fallbackKeyCode != -1);
-
- // Cancel the fallback key if the policy decides not to send it anymore.
- // We will continue to dispatch the key to the policy but we will no
- // longer dispatch a fallback key to the application.
- if (fallbackKeyCode != AKEYCODE_UNKNOWN
- && (!fallback || fallbackKeyCode != event.getKeyCode())) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- if (fallback) {
- ALOGD("Unhandled key event: Policy requested to send key %d"
- "as a fallback for %d, but on the DOWN it had requested "
- "to send %d instead. Fallback canceled.",
- event.getKeyCode(), originalKeyCode, fallbackKeyCode);
- } else {
- ALOGD("Unhandled key event: Policy did not request fallback for %d, "
- "but on the DOWN it had requested to send %d. "
- "Fallback canceled.",
- originalKeyCode, fallbackKeyCode);
- }
-#endif
-
+ // Cancel the fallback key.
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
- "canceling fallback, policy no longer desires it");
+ "application handled the original non-fallback key "
+ "or is no longer a foreground target, "
+ "canceling previously dispatched fallback key");
options.keyCode = fallbackKeyCode;
synthesizeCancelationEventsForConnectionLocked(connection, options);
-
- fallback = false;
- fallbackKeyCode = AKEYCODE_UNKNOWN;
- if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
- connection->inputState.setFallbackKey(originalKeyCode,
- fallbackKeyCode);
- }
}
-
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
+ } else {
+ // If the application did not handle a non-fallback key, first check
+ // that we are in a good state to perform unhandled key event processing
+ // Then ask the policy what to do with it.
+ bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN
+ && keyEntry->repeatCount == 0;
+ if (fallbackKeyCode == -1 && !initialDown) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- {
- std::string msg;
- const KeyedVector<int32_t, int32_t>& fallbackKeys =
- connection->inputState.getFallbackKeys();
- for (size_t i = 0; i < fallbackKeys.size(); i++) {
- msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i),
- fallbackKeys.valueAt(i));
- }
- ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
- fallbackKeys.size(), msg.c_str());
- }
+ ALOGD("Unhandled key event: Skipping unhandled key event processing "
+ "since this is not an initial down. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ originalKeyCode, keyEntry->action, keyEntry->repeatCount,
+ keyEntry->policyFlags);
#endif
+ return false;
+ }
+ // Dispatch the unhandled key to the policy.
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Unhandled key event: Asking policy to perform fallback action. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
+ keyEntry->policyFlags);
+#endif
+ KeyEvent event;
+ initializeKeyEvent(&event, keyEntry);
+
+ mLock.unlock();
+
+ bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(),
+ &event, keyEntry->policyFlags, &event);
+
+ mLock.lock();
+
+ if (connection->status != Connection::STATUS_NORMAL) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ return false;
+ }
+
+ // Latch the fallback keycode for this key on an initial down.
+ // The fallback keycode cannot change at any other point in the lifecycle.
+ if (initialDown) {
if (fallback) {
- // Restart the dispatch cycle using the fallback key.
- keyEntry->eventTime = event.getEventTime();
- keyEntry->deviceId = event.getDeviceId();
- keyEntry->source = event.getSource();
- keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry->keyCode = fallbackKeyCode;
- keyEntry->scanCode = event.getScanCode();
- keyEntry->metaState = event.getMetaState();
- keyEntry->repeatCount = event.getRepeatCount();
- keyEntry->downTime = event.getDownTime();
- keyEntry->syntheticRepeat = false;
+ fallbackKeyCode = event.getKeyCode();
+ } else {
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
+ }
+ connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
+ }
+
+ ALOG_ASSERT(fallbackKeyCode != -1);
+
+ // Cancel the fallback key if the policy decides not to send it anymore.
+ // We will continue to dispatch the key to the policy but we will no
+ // longer dispatch a fallback key to the application.
+ if (fallbackKeyCode != AKEYCODE_UNKNOWN
+ && (!fallback || fallbackKeyCode != event.getKeyCode())) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ if (fallback) {
+ ALOGD("Unhandled key event: Policy requested to send key %d"
+ "as a fallback for %d, but on the DOWN it had requested "
+ "to send %d instead. Fallback canceled.",
+ event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+ } else {
+ ALOGD("Unhandled key event: Policy did not request fallback for %d, "
+ "but on the DOWN it had requested to send %d. "
+ "Fallback canceled.",
+ originalKeyCode, fallbackKeyCode);
+ }
+#endif
+
+ CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ "canceling fallback, policy no longer desires it");
+ options.keyCode = fallbackKeyCode;
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
+
+ fallback = false;
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
+ if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+ connection->inputState.setFallbackKey(originalKeyCode,
+ fallbackKeyCode);
+ }
+ }
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Dispatching fallback key. "
- "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, fallbackKeyCode, keyEntry->metaState);
-#endif
- return true; // restart the event
- } else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: No fallback key.");
-#endif
+ {
+ std::string msg;
+ const KeyedVector<int32_t, int32_t>& fallbackKeys =
+ connection->inputState.getFallbackKeys();
+ for (size_t i = 0; i < fallbackKeys.size(); i++) {
+ msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i),
+ fallbackKeys.valueAt(i));
}
+ ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
+ fallbackKeys.size(), msg.c_str());
+ }
+#endif
+
+ if (fallback) {
+ // Restart the dispatch cycle using the fallback key.
+ keyEntry->eventTime = event.getEventTime();
+ keyEntry->deviceId = event.getDeviceId();
+ keyEntry->source = event.getSource();
+ keyEntry->displayId = event.getDisplayId();
+ keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
+ keyEntry->keyCode = fallbackKeyCode;
+ keyEntry->scanCode = event.getScanCode();
+ keyEntry->metaState = event.getMetaState();
+ keyEntry->repeatCount = event.getRepeatCount();
+ keyEntry->downTime = event.getDownTime();
+ keyEntry->syntheticRepeat = false;
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Unhandled key event: Dispatching fallback key. "
+ "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+ originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+#endif
+ return true; // restart the event
+ } else {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ ALOGD("Unhandled key event: No fallback key.");
+#endif
+
+ // Report the key as unhandled, since there is no fallback key.
+ mReporter->reportUnhandledKey(keyEntry->sequenceNum);
}
}
return false;
@@ -3855,12 +4217,12 @@
}
void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
- event->initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+ event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
entry->downTime, entry->eventTime);
}
-void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
// TODO Write some statistics about how long we spend waiting.
}
@@ -3871,7 +4233,7 @@
}
}
-void InputDispatcher::traceOutboundQueueLengthLocked(const sp<Connection>& connection) {
+void InputDispatcher::traceOutboundQueueLength(const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
char counterName[40];
snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str());
@@ -3879,7 +4241,7 @@
}
}
-void InputDispatcher::traceWaitQueueLengthLocked(const sp<Connection>& connection) {
+void InputDispatcher::traceWaitQueueLength(const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
char counterName[40];
snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str());
@@ -3888,7 +4250,7 @@
}
void InputDispatcher::dump(std::string& dump) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
dump += "Input Dispatcher State:\n";
dumpDispatchStateLocked(dump);
@@ -3901,10 +4263,9 @@
void InputDispatcher::monitor() {
// Acquire and release the lock to ensure that the dispatcher has not deadlocked.
- mLock.lock();
+ std::unique_lock _l(mLock);
mLooper->wake();
- mDispatcherIsAliveCondition.wait(mLock);
- mLock.unlock();
+ mDispatcherIsAlive.wait(_l);
}
@@ -3932,9 +4293,10 @@
// --- InputDispatcher::EventEntry ---
-InputDispatcher::EventEntry::EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags) :
- refCount(1), type(type), eventTime(eventTime), policyFlags(policyFlags),
- injectionState(NULL), dispatchInProgress(false) {
+InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type,
+ nsecs_t eventTime, uint32_t policyFlags) :
+ sequenceNum(sequenceNum), refCount(1), type(type), eventTime(eventTime),
+ policyFlags(policyFlags), injectionState(nullptr), dispatchInProgress(false) {
}
InputDispatcher::EventEntry::~EventEntry() {
@@ -3953,15 +4315,16 @@
void InputDispatcher::EventEntry::releaseInjectionState() {
if (injectionState) {
injectionState->release();
- injectionState = NULL;
+ injectionState = nullptr;
}
}
// --- InputDispatcher::ConfigurationChangedEntry ---
-InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(nsecs_t eventTime) :
- EventEntry(TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
+InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(
+ uint32_t sequenceNum, nsecs_t eventTime) :
+ EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {
}
InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() {
@@ -3974,8 +4337,9 @@
// --- InputDispatcher::DeviceResetEntry ---
-InputDispatcher::DeviceResetEntry::DeviceResetEntry(nsecs_t eventTime, int32_t deviceId) :
- EventEntry(TYPE_DEVICE_RESET, eventTime, 0),
+InputDispatcher::DeviceResetEntry::DeviceResetEntry(
+ uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
+ EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0),
deviceId(deviceId) {
}
@@ -3990,12 +4354,12 @@
// --- InputDispatcher::KeyEntry ---
-InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
+InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
int32_t repeatCount, nsecs_t downTime) :
- EventEntry(TYPE_KEY, eventTime, policyFlags),
- deviceId(deviceId), source(source), action(action), flags(flags),
+ EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+ deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags),
keyCode(keyCode), scanCode(scanCode), metaState(metaState),
repeatCount(repeatCount), downTime(downTime),
syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
@@ -4006,10 +4370,10 @@
}
void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, action=%s, "
+ msg += StringPrintf("KeyEvent(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, keyActionToString(action).c_str(), flags, keyCode,
+ deviceId, source, displayId, keyActionToString(action).c_str(), flags, keyCode,
scanCode, metaState, repeatCount, policyFlags);
}
@@ -4025,19 +4389,21 @@
// --- InputDispatcher::MotionEntry ---
-InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime, int32_t deviceId,
- uint32_t source, uint32_t policyFlags, int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- float xPrecision, float yPrecision, nsecs_t downTime,
- int32_t displayId, uint32_t pointerCount,
+InputDispatcher::MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t actionButton,
+ int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime,
+ uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xOffset, float yOffset) :
- EventEntry(TYPE_MOTION, eventTime, policyFlags),
+ EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
eventTime(eventTime),
- deviceId(deviceId), source(source), action(action), actionButton(actionButton),
- flags(flags), metaState(metaState), buttonState(buttonState),
- edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision),
- downTime(downTime), displayId(displayId), pointerCount(pointerCount) {
+ deviceId(deviceId), source(source), displayId(displayId), action(action),
+ actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState),
+ classification(classification), edgeFlags(edgeFlags),
+ xPrecision(xPrecision), yPrecision(yPrecision),
+ downTime(downTime), pointerCount(pointerCount) {
for (uint32_t i = 0; i < pointerCount; i++) {
this->pointerProperties[i].copyFrom(pointerProperties[i]);
this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -4051,11 +4417,13 @@
}
void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, action=%s, actionButton=0x%08x, "
- "flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, "
- "edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, displayId=%d, pointers=[",
- deviceId, source, motionActionToString(action).c_str(), actionButton, flags, metaState,
- buttonState, edgeFlags, xPrecision, yPrecision, displayId);
+ msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
+ ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, "
+ "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[",
+ deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags,
+ metaState, buttonState, motionClassificationToString(classification), edgeFlags,
+ xPrecision, yPrecision);
+
for (uint32_t i = 0; i < pointerCount; i++) {
if (i) {
msg += ", ";
@@ -4072,10 +4440,12 @@
volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+ int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
+ float windowXScale, float windowYScale) :
seq(nextSeq()),
eventEntry(eventEntry), targetFlags(targetFlags),
- xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
+ xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
+ windowXScale(windowXScale), windowYScale(windowYScale),
deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
eventEntry->refCount += 1;
}
@@ -4103,13 +4473,12 @@
}
bool InputDispatcher::InputState::isNeutral() const {
- return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
+ return mKeyMementos.empty() && mMotionMementos.empty();
}
bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source,
int32_t displayId) const {
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
+ for (const MotionMemento& memento : mMotionMementos) {
if (memento.deviceId == deviceId
&& memento.source == source
&& memento.displayId == displayId
@@ -4135,7 +4504,7 @@
}
ssize_t index = findKeyMemento(entry);
if (index >= 0) {
- mKeyMementos.removeAt(index);
+ mKeyMementos.erase(mKeyMementos.begin() + index);
return true;
}
/* FIXME: We can't just drop the key up event because that prevents creating
@@ -4160,7 +4529,7 @@
case AKEY_EVENT_ACTION_DOWN: {
ssize_t index = findKeyMemento(entry);
if (index >= 0) {
- mKeyMementos.removeAt(index);
+ mKeyMementos.erase(mKeyMementos.begin() + index);
}
addKeyMemento(entry, flags);
return true;
@@ -4179,13 +4548,13 @@
case AMOTION_EVENT_ACTION_CANCEL: {
ssize_t index = findMotionMemento(entry, false /*hovering*/);
if (index >= 0) {
- mMotionMementos.removeAt(index);
+ mMotionMementos.erase(mMotionMementos.begin() + index);
return true;
}
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
- "actionMasked=%d",
- entry->deviceId, entry->source, actionMasked);
+ "displayId=%" PRId32 ", actionMasked=%d",
+ entry->deviceId, entry->source, entry->displayId, actionMasked);
#endif
return false;
}
@@ -4193,7 +4562,7 @@
case AMOTION_EVENT_ACTION_DOWN: {
ssize_t index = findMotionMemento(entry, false /*hovering*/);
if (index >= 0) {
- mMotionMementos.removeAt(index);
+ mMotionMementos.erase(mMotionMementos.begin() + index);
}
addMotionMemento(entry, flags, false /*hovering*/);
return true;
@@ -4218,9 +4587,9 @@
// anything generating fallback events (e.g. DPad keys for joystick movements).
if (index >= 0) {
if (entry->pointerCoords[0].isEmpty()) {
- mMotionMementos.removeAt(index);
+ mMotionMementos.erase(mMotionMementos.begin() + index);
} else {
- MotionMemento& memento = mMotionMementos.editItemAt(index);
+ MotionMemento& memento = mMotionMementos[index];
memento.setPointers(entry);
}
} else if (!entry->pointerCoords[0].isEmpty()) {
@@ -4231,14 +4600,14 @@
return true;
}
if (index >= 0) {
- MotionMemento& memento = mMotionMementos.editItemAt(index);
+ MotionMemento& memento = mMotionMementos[index];
memento.setPointers(entry);
return true;
}
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Dropping inconsistent motion pointer up/down or move event: "
- "deviceId=%d, source=%08x, actionMasked=%d",
- entry->deviceId, entry->source, actionMasked);
+ "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
+ entry->deviceId, entry->source, entry->displayId, actionMasked);
#endif
return false;
}
@@ -4246,12 +4615,13 @@
case AMOTION_EVENT_ACTION_HOVER_EXIT: {
ssize_t index = findMotionMemento(entry, true /*hovering*/);
if (index >= 0) {
- mMotionMementos.removeAt(index);
+ mMotionMementos.erase(mMotionMementos.begin() + index);
return true;
}
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x",
- entry->deviceId, entry->source);
+ ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
+ "displayId=%" PRId32,
+ entry->deviceId, entry->source, entry->displayId);
#endif
return false;
}
@@ -4260,7 +4630,7 @@
case AMOTION_EVENT_ACTION_HOVER_MOVE: {
ssize_t index = findMotionMemento(entry, true /*hovering*/);
if (index >= 0) {
- mMotionMementos.removeAt(index);
+ mMotionMementos.erase(mMotionMementos.begin() + index);
}
addMotionMemento(entry, flags, true /*hovering*/);
return true;
@@ -4273,9 +4643,10 @@
ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const {
for (size_t i = 0; i < mKeyMementos.size(); i++) {
- const KeyMemento& memento = mKeyMementos.itemAt(i);
+ const KeyMemento& memento = mKeyMementos[i];
if (memento.deviceId == entry->deviceId
&& memento.source == entry->source
+ && memento.displayId == entry->displayId
&& memento.keyCode == entry->keyCode
&& memento.scanCode == entry->scanCode) {
return i;
@@ -4287,7 +4658,7 @@
ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
bool hovering) const {
for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
+ const MotionMemento& memento = mMotionMementos[i];
if (memento.deviceId == entry->deviceId
&& memento.source == entry->source
&& memento.displayId == entry->displayId
@@ -4299,32 +4670,33 @@
}
void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
- mKeyMementos.push();
- KeyMemento& memento = mKeyMementos.editTop();
+ KeyMemento memento;
memento.deviceId = entry->deviceId;
memento.source = entry->source;
+ memento.displayId = entry->displayId;
memento.keyCode = entry->keyCode;
memento.scanCode = entry->scanCode;
memento.metaState = entry->metaState;
memento.flags = flags;
memento.downTime = entry->downTime;
memento.policyFlags = entry->policyFlags;
+ mKeyMementos.push_back(memento);
}
void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
int32_t flags, bool hovering) {
- mMotionMementos.push();
- MotionMemento& memento = mMotionMementos.editTop();
+ MotionMemento memento;
memento.deviceId = entry->deviceId;
memento.source = entry->source;
+ memento.displayId = entry->displayId;
memento.flags = flags;
memento.xPrecision = entry->xPrecision;
memento.yPrecision = entry->yPrecision;
memento.downTime = entry->downTime;
- memento.displayId = entry->displayId;
memento.setPointers(entry);
memento.hovering = hovering;
memento.policyFlags = entry->policyFlags;
+ mMotionMementos.push_back(memento);
}
void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
@@ -4336,30 +4708,27 @@
}
void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
- Vector<EventEntry*>& outEvents, const CancelationOptions& options) {
- for (size_t i = 0; i < mKeyMementos.size(); i++) {
- const KeyMemento& memento = mKeyMementos.itemAt(i);
+ std::vector<EventEntry*>& outEvents, const CancelationOptions& options) {
+ for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
- outEvents.push(new KeyEntry(currentTime,
- memento.deviceId, memento.source, memento.policyFlags,
+ outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime));
}
}
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
+ for (const MotionMemento& memento : mMotionMementos) {
if (shouldCancelMotion(memento, options)) {
- outEvents.push(new MotionEntry(currentTime,
- memento.deviceId, memento.source, memento.policyFlags,
- memento.hovering
- ? AMOTION_EVENT_ACTION_HOVER_EXIT
- : AMOTION_EVENT_ACTION_CANCEL,
- memento.flags, 0, 0, 0, 0,
+ const int32_t action = memento.hovering ?
+ AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
+ outEvents.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
+ memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
+ action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
memento.xPrecision, memento.yPrecision, memento.downTime,
- memento.displayId,
memento.pointerCount, memento.pointerProperties, memento.pointerCoords,
- 0, 0));
+ 0 /*xOffset*/, 0 /*yOffset*/));
}
}
}
@@ -4372,19 +4741,19 @@
void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
for (size_t i = 0; i < mMotionMementos.size(); i++) {
- const MotionMemento& memento = mMotionMementos.itemAt(i);
+ const MotionMemento& memento = mMotionMementos[i];
if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
for (size_t j = 0; j < other.mMotionMementos.size(); ) {
- const MotionMemento& otherMemento = other.mMotionMementos.itemAt(j);
+ const MotionMemento& otherMemento = other.mMotionMementos[j];
if (memento.deviceId == otherMemento.deviceId
&& memento.source == otherMemento.source
&& memento.displayId == otherMemento.displayId) {
- other.mMotionMementos.removeAt(j);
+ other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
} else {
j += 1;
}
}
- other.mMotionMementos.push(memento);
+ other.mMotionMementos.push_back(memento);
}
}
}
@@ -4424,6 +4793,8 @@
return true;
case CancelationOptions::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
+ case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS:
+ return memento.displayId == ADISPLAY_ID_NONE;
default:
return false;
}
@@ -4442,6 +4813,8 @@
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+ case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS:
+ return memento.displayId == ADISPLAY_ID_NONE;
default:
return false;
}
@@ -4450,9 +4823,8 @@
// --- InputDispatcher::Connection ---
-InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
- status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
+InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) :
+ status(STATUS_NORMAL), inputChannel(inputChannel),
monitor(monitor),
inputPublisher(inputChannel), inputPublisherBlocked(false) {
}
@@ -4461,8 +4833,8 @@
}
const std::string InputDispatcher::Connection::getWindowName() const {
- if (inputWindowHandle != NULL) {
- return inputWindowHandle->getName();
+ if (inputChannel != nullptr) {
+ return inputChannel->getName();
}
if (monitor) {
return "monitor";
@@ -4487,19 +4859,19 @@
}
InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) {
- for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) {
+ for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) {
if (entry->seq == seq) {
return entry;
}
}
- return NULL;
+ return nullptr;
}
// --- InputDispatcher::CommandEntry ---
InputDispatcher::CommandEntry::CommandEntry(Command command) :
- command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0),
+ command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0),
seq(0), handled(false) {
}
@@ -4510,7 +4882,7 @@
// --- InputDispatcher::TouchState ---
InputDispatcher::TouchState::TouchState() :
- down(false), split(false), deviceId(-1), source(0), displayId(-1) {
+ down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {
}
InputDispatcher::TouchState::~TouchState() {
@@ -4521,8 +4893,9 @@
split = false;
deviceId = -1;
source = 0;
- displayId = -1;
+ displayId = ADISPLAY_ID_NONE;
windows.clear();
+ portalWindows.clear();
}
void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4532,6 +4905,7 @@
source = other.source;
displayId = other.displayId;
windows = other.windows;
+ portalWindows = other.portalWindows;
}
void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4541,7 +4915,7 @@
}
for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows.editItemAt(i);
+ TouchedWindow& touchedWindow = windows[i];
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
@@ -4552,18 +4926,36 @@
}
}
- windows.push();
-
- TouchedWindow& touchedWindow = windows.editTop();
+ TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
touchedWindow.pointerIds = pointerIds;
+ windows.push_back(touchedWindow);
+}
+
+void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+ size_t numWindows = portalWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ if (portalWindows[i] == windowHandle) {
+ return;
+ }
+ }
+ portalWindows.push_back(windowHandle);
}
void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
for (size_t i = 0; i < windows.size(); i++) {
- if (windows.itemAt(i).windowHandle == windowHandle) {
- windows.removeAt(i);
+ if (windows[i].windowHandle == windowHandle) {
+ windows.erase(windows.begin() + i);
+ return;
+ }
+ }
+}
+
+void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) {
+ for (size_t i = 0; i < windows.size(); i++) {
+ if (windows[i].windowHandle->getToken() == token) {
+ windows.erase(windows.begin() + i);
return;
}
}
@@ -4571,33 +4963,32 @@
void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {
for (size_t i = 0 ; i < windows.size(); ) {
- TouchedWindow& window = windows.editItemAt(i);
+ TouchedWindow& window = windows[i];
if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS
| InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
i += 1;
} else {
- windows.removeAt(i);
+ windows.erase(windows.begin() + i);
}
}
}
sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const {
for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows.itemAt(i);
+ const TouchedWindow& window = windows[i];
if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
return window.windowHandle;
}
}
- return NULL;
+ return nullptr;
}
bool InputDispatcher::TouchState::isSlippery() const {
// Must have exactly one foreground window.
bool haveSlipperyForegroundWindow = false;
- for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows.itemAt(i);
+ for (const TouchedWindow& window : windows) {
if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
if (haveSlipperyForegroundWindow
|| !(window.windowHandle->getInfo()->layoutParamsFlags
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 8da8450..4d2c216 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -17,25 +17,27 @@
#ifndef _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
+#include <condition_variable>
#include <input/Input.h>
+#include <input/InputApplication.h>
#include <input/InputTransport.h>
-#include <utils/KeyedVector.h>
-#include <utils/Vector.h>
+#include <input/InputWindow.h>
+#include <input/ISetInputWindowsListener.h>
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
#include <utils/BitSet.h>
#include <cutils/atomic.h>
+#include <unordered_map>
#include <stddef.h>
#include <unistd.h>
#include <limits.h>
+#include <unordered_map>
-#include "InputWindow.h"
-#include "InputApplication.h"
#include "InputListener.h"
-
+#include "InputReporterInterface.h"
namespace android {
@@ -158,7 +160,9 @@
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float scaleFactor;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
@@ -207,11 +211,12 @@
/* 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<InputWindowHandle>& inputWindowHandle,
+ const sp<IBinder>& token,
const std::string& reason) = 0;
/* Notifies the system that an input channel is unrecoverably broken. */
- virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) = 0;
+ virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
+ virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
/* Gets the input dispatcher configuration. */
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
@@ -239,15 +244,16 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;
+ virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
/* Allows the policy a chance to perform default processing for an unhandled key.
* Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
- virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
+ virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
/* Notifies the policy about switch events.
@@ -299,7 +305,7 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId,
+ virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
uint32_t policyFlags) = 0;
@@ -307,14 +313,22 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) = 0;
+ virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
+ int32_t displayId,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
- /* Sets the focused application.
+ /* Sets the focused application on the given display.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual void setFocusedApplication(
- const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+ int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+ /* Sets the focused display.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setFocusedDisplay(int32_t displayId) = 0;
/* Sets the input dispatching mode.
*
@@ -330,21 +344,25 @@
*/
virtual void setInputFilterEnabled(bool enabled) = 0;
- /* Transfers touch focus from the window associated with one channel to the
- * window associated with the other channel.
+ /* Transfers touch focus from one window to another window.
*
* Returns true on success. False if the window did not actually have touch focus.
*/
- virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel) = 0;
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
- /* Registers or unregister input channels that may be used as targets for input events.
- * If monitor is true, the channel will receive a copy of all input events.
+ /* Registers input channels that may be used as targets for input events.
+ * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE,
+ * the channel will receive a copy of all input events form the specific displayId.
*
- * These methods may be called on any thread (usually by the input manager).
+ * This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0;
+ virtual status_t registerInputChannel(
+ const sp<InputChannel>& inputChannel, int32_t displayId) = 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;
};
@@ -383,20 +401,23 @@
virtual void notifySwitch(const NotifySwitchArgs* args);
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId,
+ virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
uint32_t policyFlags);
- virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles);
- virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle);
+ virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
+ int32_t displayId,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr);
+ virtual void setFocusedApplication(int32_t displayId,
+ const sp<InputApplicationHandle>& inputApplicationHandle);
+ virtual void setFocusedDisplay(int32_t displayId);
virtual void setInputDispatchMode(bool enabled, bool frozen);
virtual void setInputFilterEnabled(bool enabled);
- virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel);
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+ int32_t displayId);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
private:
@@ -406,7 +427,7 @@
T* prev;
protected:
- inline Link() : next(NULL), prev(NULL) { }
+ inline Link() : next(nullptr), prev(nullptr) { }
};
struct InjectionState {
@@ -433,6 +454,7 @@
TYPE_MOTION
};
+ uint32_t sequenceNum;
mutable int32_t refCount;
int32_t type;
nsecs_t eventTime;
@@ -441,20 +463,20 @@
bool dispatchInProgress; // initially false, set to true while dispatching
- inline bool isInjected() const { return injectionState != NULL; }
+ inline bool isInjected() const { return injectionState != nullptr; }
void release();
virtual void appendDescription(std::string& msg) const = 0;
protected:
- EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+ EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
void releaseInjectionState();
};
struct ConfigurationChangedEntry : EventEntry {
- explicit ConfigurationChangedEntry(nsecs_t eventTime);
+ explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
virtual void appendDescription(std::string& msg) const;
protected:
@@ -464,7 +486,7 @@
struct DeviceResetEntry : EventEntry {
int32_t deviceId;
- DeviceResetEntry(nsecs_t eventTime, int32_t deviceId);
+ DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
virtual void appendDescription(std::string& msg) const;
protected:
@@ -474,6 +496,7 @@
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
int32_t action;
int32_t flags;
int32_t keyCode;
@@ -493,9 +516,9 @@
InterceptKeyResult interceptKeyResult; // set based on the interception result
nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
- KeyEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ KeyEntry(uint32_t sequenceNum, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
int32_t repeatCount, nsecs_t downTime);
virtual void appendDescription(std::string& msg) const;
void recycle();
@@ -508,26 +531,27 @@
nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
int32_t action;
int32_t actionButton;
int32_t flags;
int32_t metaState;
int32_t buttonState;
+ MotionClassification classification;
int32_t edgeFlags;
float xPrecision;
float yPrecision;
nsecs_t downTime;
- int32_t displayId;
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
- MotionEntry(nsecs_t eventTime,
- int32_t deviceId, uint32_t source, uint32_t policyFlags,
+ MotionEntry(uint32_t sequenceNum, nsecs_t eventTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- float xPrecision, float yPrecision, nsecs_t downTime,
- int32_t displayId, uint32_t pointerCount,
+ int32_t metaState, int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, float xPrecision, float yPrecision,
+ nsecs_t downTime, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xOffset, float yOffset);
virtual void appendDescription(std::string& msg) const;
@@ -544,7 +568,9 @@
int32_t targetFlags;
float xOffset;
float yOffset;
- float scaleFactor;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
nsecs_t deliveryTime; // time when the event was actually delivered
// Set to the resolved action and flags when the event is enqueued.
@@ -552,7 +578,8 @@
int32_t resolvedFlags;
DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
+ int32_t targetFlags, float xOffset, float yOffset,
+ float globalScaleFactor, float windowXScale, float windowYScale);
~DispatchEntry();
inline bool hasForegroundTarget() const {
@@ -600,11 +627,13 @@
nsecs_t eventTime;
KeyEntry* keyEntry;
sp<InputApplicationHandle> inputApplicationHandle;
- sp<InputWindowHandle> inputWindowHandle;
std::string reason;
int32_t userActivityEventType;
uint32_t seq;
bool handled;
+ sp<InputChannel> inputChannel;
+ sp<IBinder> oldToken;
+ sp<IBinder> newToken;
};
// Generic queue implementation.
@@ -614,7 +643,7 @@
T* tail;
uint32_t entryCount;
- inline Queue() : head(NULL), tail(NULL), entryCount(0) {
+ inline Queue() : head(nullptr), tail(nullptr), entryCount(0) {
}
inline bool isEmpty() const {
@@ -629,7 +658,7 @@
} else {
head = entry;
}
- entry->next = NULL;
+ entry->next = nullptr;
tail = entry;
}
@@ -641,7 +670,7 @@
} else {
tail = entry;
}
- entry->prev = NULL;
+ entry->prev = nullptr;
head = entry;
}
@@ -664,9 +693,9 @@
T* entry = head;
head = entry->next;
if (head) {
- head->prev = NULL;
+ head->prev = nullptr;
} else {
- tail = NULL;
+ tail = nullptr;
}
return entry;
}
@@ -683,6 +712,10 @@
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
CANCEL_FALLBACK_EVENTS = 3,
+
+ /* Cancel events where the display not specified. These events would go to the focused
+ * display. */
+ CANCEL_DISPLAY_UNSPECIFIED_EVENTS = 4,
};
// The criterion to use to determine which events should be canceled.
@@ -727,7 +760,7 @@
// Synthesizes cancelation events for the current state and resets the tracked state.
void synthesizeCancelationEvents(nsecs_t currentTime,
- Vector<EventEntry*>& outEvents, const CancelationOptions& options);
+ std::vector<EventEntry*>& outEvents, const CancelationOptions& options);
// Clears the current state.
void clear();
@@ -754,6 +787,7 @@
struct KeyMemento {
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
@@ -765,11 +799,11 @@
struct MotionMemento {
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
int32_t flags;
float xPrecision;
float yPrecision;
nsecs_t downTime;
- int32_t displayId;
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
@@ -779,8 +813,8 @@
void setPointers(const MotionEntry* entry);
};
- Vector<KeyMemento> mKeyMementos;
- Vector<MotionMemento> mMotionMementos;
+ std::vector<KeyMemento> mKeyMementos;
+ std::vector<MotionMemento> mMotionMementos;
KeyedVector<int32_t, int32_t> mFallbackKeys;
ssize_t findKeyMemento(const KeyEntry* entry) const;
@@ -812,7 +846,6 @@
Status status;
sp<InputChannel> inputChannel; // never null
- sp<InputWindowHandle> inputWindowHandle; // may be null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
@@ -828,8 +861,7 @@
// yet received a "finished" response from the application.
Queue<DispatchEntry> waitQueue;
- explicit Connection(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+ explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
@@ -851,73 +883,82 @@
sp<InputDispatcherPolicyInterface> mPolicy;
InputDispatcherConfiguration mConfig;
- Mutex mLock;
+ std::mutex mLock;
- Condition mDispatcherIsAliveCondition;
+ std::condition_variable mDispatcherIsAlive;
sp<Looper> mLooper;
- EventEntry* mPendingEvent;
- Queue<EventEntry> mInboundQueue;
- Queue<EventEntry> mRecentQueue;
- Queue<CommandEntry> mCommandQueue;
+ EventEntry* mPendingEvent GUARDED_BY(mLock);
+ Queue<EventEntry> mInboundQueue GUARDED_BY(mLock);
+ Queue<EventEntry> mRecentQueue GUARDED_BY(mLock);
+ Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);
- DropReason mLastDropReason;
+ DropReason mLastDropReason GUARDED_BY(mLock);
- void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime);
+ void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
- bool enqueueInboundEventLocked(EventEntry* entry);
+ bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
// Cleans up input state when dropping an inbound event.
- void dropInboundEventLocked(EventEntry* entry, DropReason dropReason);
+ void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(EventEntry* entry);
+ void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
// App switch latency optimization.
- bool mAppSwitchSawKeyDown;
- nsecs_t mAppSwitchDueTime;
+ bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
+ nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
- static bool isAppSwitchKeyCode(int32_t keyCode);
- bool isAppSwitchKeyEventLocked(KeyEntry* keyEntry);
- bool isAppSwitchPendingLocked();
- void resetPendingAppSwitchLocked(bool handled);
+ bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+ bool isAppSwitchPendingLocked() REQUIRES(mLock);
+ void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
// Stale event latency optimization.
- static bool isStaleEventLocked(nsecs_t currentTime, EventEntry* entry);
+ static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
- EventEntry* mNextUnblockedEvent;
+ EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
- sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
+ sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+ bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
- KeyedVector<int, sp<Connection> > mConnectionsByFd;
+ KeyedVector<int, sp<Connection> > mConnectionsByFd GUARDED_BY(mLock);
- ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
+ 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);
- // Input channels that will receive a copy of all input events.
- Vector<sp<InputChannel> > mMonitoringChannels;
+ ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) 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<sp<InputChannel>>> mMonitoringChannelsByDisplay
+ GUARDED_BY(mLock);
// Event injection and synchronization.
- Condition mInjectionResultAvailableCondition;
+ std::condition_variable mInjectionResultAvailable;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
+ void setInjectionResult(EventEntry* entry, int32_t injectionResult);
- Condition mInjectionSyncFinishedCondition;
- void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
- void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
+ std::condition_variable mInjectionSyncFinished;
+ void incrementPendingForegroundDispatches(EventEntry* entry);
+ void decrementPendingForegroundDispatches(EventEntry* entry);
// Key repeat tracking.
struct KeyRepeatState {
KeyEntry* lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
- } mKeyRepeatState;
+ } mKeyRepeatState GUARDED_BY(mLock);
- void resetKeyRepeatLocked();
- KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime);
+ void resetKeyRepeatLocked() REQUIRES(mLock);
+ KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
// Key replacement tracking
struct KeyReplacement {
@@ -931,34 +972,43 @@
}
};
// Maps the key code replaced, device id tuple to the key code it was replaced with
- KeyedVector<KeyReplacement, int32_t> mReplacedKeys;
+ KeyedVector<KeyReplacement, int32_t> 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);
// Deferred command processing.
- bool haveCommandsLocked() const;
- bool runCommandsLockedInterruptible();
- CommandEntry* postCommandLocked(Command command);
+ bool haveCommandsLocked() const REQUIRES(mLock);
+ bool runCommandsLockedInterruptible() REQUIRES(mLock);
+ CommandEntry* postCommandLocked(Command command) REQUIRES(mLock);
// Input filter processing.
- bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args);
- bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args);
+ bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
+ bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
// Inbound event processing.
- void drainInboundQueueLocked();
- void releasePendingEventLocked();
- void releaseInboundEventLocked(EventEntry* entry);
+ void drainInboundQueueLocked() REQUIRES(mLock);
+ void releasePendingEventLocked() REQUIRES(mLock);
+ void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
// Dispatch state.
- bool mDispatchEnabled;
- bool mDispatchFrozen;
- bool mInputFilterEnabled;
+ bool mDispatchEnabled GUARDED_BY(mLock);
+ bool mDispatchFrozen GUARDED_BY(mLock);
+ bool mInputFilterEnabled GUARDED_BY(mLock);
- Vector<sp<InputWindowHandle> > mWindowHandles;
-
- sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const;
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
+ GUARDED_BY(mLock);
+ // Get window handles by display, return an empty vector if not found.
+ std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+ REQUIRES(mLock);
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
+ REQUIRES(mLock);
+ sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
+ bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
// Focus tracking for keys, trackball, etc.
- sp<InputWindowHandle> mFocusedWindowHandle;
+ std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
+ GUARDED_BY(mLock);
// Focus tracking for touch.
struct TouchedWindow {
@@ -972,7 +1022,12 @@
int32_t deviceId; // id of the device that is currently down, others are rejected
uint32_t source; // source of the device that is current down, others are rejected
int32_t displayId; // id to the display that currently has a touch, others are rejected
- Vector<TouchedWindow> windows;
+ std::vector<TouchedWindow> windows;
+
+ // This collects the portal windows that the touch has gone through. Each portal window
+ // targets a display (embedded display for most cases). With this info, we can add the
+ // monitoring channels of the displays touched.
+ std::vector<sp<InputWindowHandle>> portalWindows;
TouchState();
~TouchState();
@@ -980,37 +1035,43 @@
void copyFrom(const TouchState& other);
void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
+ void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
void removeWindow(const sp<InputWindowHandle>& windowHandle);
+ void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
};
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay;
- TouchState mTempTouchState;
+ KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+ TouchState mTempTouchState GUARDED_BY(mLock);
- // Focused application.
- sp<InputApplicationHandle> mFocusedApplicationHandle;
+ // Focused applications.
+ std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
+ GUARDED_BY(mLock);
+
+ // Top focused display.
+ int32_t mFocusedDisplayId GUARDED_BY(mLock);
// Dispatcher state at time of last ANR.
- std::string mLastANRState;
+ std::string mLastANRState GUARDED_BY(mLock);
// Dispatch inbound events.
bool dispatchConfigurationChangedLocked(
- nsecs_t currentTime, ConfigurationChangedEntry* entry);
+ nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock);
bool dispatchDeviceResetLocked(
- nsecs_t currentTime, DeviceResetEntry* entry);
+ nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
bool dispatchKeyLocked(
nsecs_t currentTime, KeyEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime);
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
bool dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime);
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
- const Vector<InputTarget>& inputTargets);
+ const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
- void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
+ void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
+ void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
// Keeping track of ANR timeouts.
enum InputTargetWaitCause {
@@ -1019,122 +1080,135 @@
INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
};
- InputTargetWaitCause mInputTargetWaitCause;
- nsecs_t mInputTargetWaitStartTime;
- nsecs_t mInputTargetWaitTimeoutTime;
- bool mInputTargetWaitTimeoutExpired;
- sp<InputApplicationHandle> mInputTargetWaitApplicationHandle;
+ 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);
// Contains the last window which received a hover event.
- sp<InputWindowHandle> mLastHoverWindowHandle;
+ 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);
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<InputChannel>& inputChannel);
- nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
- void resetANRTimeoutsLocked();
+ nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock);
+ void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
+ void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
+ void resetANRTimeoutsLocked() REQUIRES(mLock);
+
+ int32_t getTargetDisplayId(const EventEntry* entry);
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
- Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
+ std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock);
int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
- Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions);
+ std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
+ bool* outConflictingPointerActions) REQUIRES(mLock);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
- void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
+ int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
+ REQUIRES(mLock);
+ void addMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
+ float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
- void pokeUserActivityLocked(const EventEntry* eventEntry);
+ void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
- int32_t x, int32_t y) const;
- bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const;
- std::string getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
+ int32_t x, int32_t y) const REQUIRES(mLock);
+ bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ 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);
+ 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.
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget);
+ EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
+ void enqueueDispatchEntry(const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
+ REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled);
+ uint32_t seq, bool handled) REQUIRES(mLock);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- bool notify);
- void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
- void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
+ bool notify) REQUIRES(mLock);
+ void drainDispatchQueue(Queue<DispatchEntry>* queue);
+ void releaseDispatchEntry(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
void synthesizeCancelationEventsForAllConnectionsLocked(
- const CancelationOptions& options);
- void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(
+ const CancelationOptions& options) REQUIRES(mLock);
void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
// Splitting motion events across windows.
MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
// Reset and drop everything the dispatcher is doing.
- void resetAndDropEverythingLocked(const char* reason);
+ void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
// Dump state.
- void dumpDispatchStateLocked(std::string& dump);
- void logDispatchStateLocked();
+ void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
+ void logDispatchStateLocked() REQUIRES(mLock);
// Registration.
- void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel);
- status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify);
-
- // Add or remove a connection to the mActiveConnections vector.
- void activateConnectionLocked(Connection* connection);
- void deactivateConnectionLocked(Connection* connection);
+ void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+ status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify)
+ REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
void onDispatchCycleFinishedLocked(
- nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
+ nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled)
+ REQUIRES(mLock);
void onDispatchCycleBrokenLocked(
- nsecs_t currentTime, const sp<Connection>& connection);
+ nsecs_t currentTime, const sp<Connection>& connection) 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);
+ nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
// Outbound policy interactions.
- void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
- void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
- void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
- void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
- void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
+ 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 doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
+ void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled);
+ DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock);
bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled);
- void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
+ DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
+ void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
// Statistics gathering.
- void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
- void traceInboundQueueLengthLocked();
- void traceOutboundQueueLengthLocked(const sp<Connection>& connection);
- void traceWaitQueueLengthLocked(const sp<Connection>& connection);
+ void traceInboundQueueLengthLocked() REQUIRES(mLock);
+ void traceOutboundQueueLength(const sp<Connection>& connection);
+ void traceWaitQueueLength(const sp<Connection>& connection);
+
+ sp<InputReporterInterface> mReporter;
};
/* Enqueues and dispatches input events, endlessly. */
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 520fea4..423b69c 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -26,13 +26,18 @@
// --- NotifyConfigurationChangedArgs ---
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(nsecs_t eventTime) :
- eventTime(eventTime) {
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
+ uint32_t sequenceNum, nsecs_t eventTime) :
+ NotifyArgs(sequenceNum, eventTime) {
}
NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
const NotifyConfigurationChangedArgs& other) :
- eventTime(other.eventTime) {
+ NotifyArgs(other.sequenceNum, other.eventTime) {
+}
+
+bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
+ return sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime;
}
void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -42,23 +47,39 @@
// --- NotifyKeyArgs ---
-NotifyKeyArgs::NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
- uint32_t policyFlags,
+NotifyKeyArgs::NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags,
int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, nsecs_t downTime) :
- eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
+ NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
+ displayId(displayId), policyFlags(policyFlags),
action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
metaState(metaState), downTime(downTime) {
}
NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
- eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
- policyFlags(other.policyFlags),
+ NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId),
+ source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
action(other.action), flags(other.flags),
keyCode(other.keyCode), scanCode(other.scanCode),
metaState(other.metaState), downTime(other.downTime) {
}
+bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
+ return sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && deviceId == rhs.deviceId
+ && source == rhs.source
+ && displayId == rhs.displayId
+ && policyFlags == rhs.policyFlags
+ && action == rhs.action
+ && flags == rhs.flags
+ && keyCode == rhs.keyCode
+ && scanCode == rhs.scanCode
+ && metaState == rhs.metaState
+ && downTime == rhs.downTime;
+}
+
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
@@ -66,19 +87,22 @@
// --- NotifyMotionArgs ---
-NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
- uint32_t policyFlags,
+NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags,
int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp,
- uint32_t pointerCount,
+ int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, nsecs_t downTime) :
- eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
+ float xPrecision, float yPrecision, nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames) :
+ NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
+ displayId(displayId), policyFlags(policyFlags),
action(action), actionButton(actionButton),
flags(flags), metaState(metaState), buttonState(buttonState),
- edgeFlags(edgeFlags), displayId(displayId), deviceTimestamp(deviceTimestamp),
+ classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp),
pointerCount(pointerCount),
- xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
+ xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime),
+ videoFrames(videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
this->pointerProperties[i].copyFrom(pointerProperties[i]);
this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -86,19 +110,57 @@
}
NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
- eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
- policyFlags(other.policyFlags),
+ NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId),
+ source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
action(other.action), actionButton(other.actionButton), flags(other.flags),
metaState(other.metaState), buttonState(other.buttonState),
- edgeFlags(other.edgeFlags), displayId(other.displayId),
+ classification(other.classification), edgeFlags(other.edgeFlags),
deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount),
- xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
+ xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime),
+ videoFrames(other.videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
pointerProperties[i].copyFrom(other.pointerProperties[i]);
pointerCoords[i].copyFrom(other.pointerCoords[i]);
}
}
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+ bool equal =
+ sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && deviceId == rhs.deviceId
+ && source == rhs.source
+ && displayId == rhs.displayId
+ && policyFlags == rhs.policyFlags
+ && action == rhs.action
+ && actionButton == rhs.actionButton
+ && flags == rhs.flags
+ && metaState == rhs.metaState
+ && buttonState == rhs.buttonState
+ && classification == rhs.classification
+ && edgeFlags == rhs.edgeFlags
+ && deviceTimestamp == rhs.deviceTimestamp
+ && pointerCount == rhs.pointerCount
+ // PointerProperties and PointerCoords are compared separately below
+ && xPrecision == rhs.xPrecision
+ && yPrecision == rhs.yPrecision
+ && downTime == rhs.downTime
+ && videoFrames == rhs.videoFrames;
+ if (!equal) {
+ return false;
+ }
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ equal =
+ pointerProperties[i] == rhs.pointerProperties[i]
+ && pointerCoords[i] == rhs.pointerCoords[i];
+ if (!equal) {
+ return false;
+ }
+ }
+ return true;
+}
+
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
@@ -106,17 +168,25 @@
// --- NotifySwitchArgs ---
-NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
+NotifySwitchArgs::NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
uint32_t switchValues, uint32_t switchMask) :
- eventTime(eventTime), policyFlags(policyFlags),
+ NotifyArgs(sequenceNum, eventTime), policyFlags(policyFlags),
switchValues(switchValues), switchMask(switchMask) {
}
NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
- eventTime(other.eventTime), policyFlags(other.policyFlags),
+ NotifyArgs(other.sequenceNum, other.eventTime), policyFlags(other.policyFlags),
switchValues(other.switchValues), switchMask(other.switchMask) {
}
+bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
+ return sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && policyFlags == rhs.policyFlags
+ && switchValues == rhs.switchValues
+ && switchMask == rhs.switchMask;
+}
+
void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifySwitch(this);
}
@@ -124,12 +194,19 @@
// --- NotifyDeviceResetArgs ---
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId) :
- eventTime(eventTime), deviceId(deviceId) {
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(
+ uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
+ NotifyArgs(sequenceNum, eventTime), deviceId(deviceId) {
}
NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) :
- eventTime(other.eventTime), deviceId(other.deviceId) {
+ NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId) {
+}
+
+bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
+ return sequenceNum == rhs.sequenceNum
+ && eventTime == rhs.eventTime
+ && deviceId == rhs.deviceId;
}
void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -152,23 +229,23 @@
void QueuedInputListener::notifyConfigurationChanged(
const NotifyConfigurationChangedArgs* args) {
- mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
+ mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args));
}
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
- mArgsQueue.push(new NotifyKeyArgs(*args));
+ mArgsQueue.push_back(new NotifyKeyArgs(*args));
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
- mArgsQueue.push(new NotifyMotionArgs(*args));
+ mArgsQueue.push_back(new NotifyMotionArgs(*args));
}
void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
- mArgsQueue.push(new NotifySwitchArgs(*args));
+ mArgsQueue.push_back(new NotifySwitchArgs(*args));
}
void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- mArgsQueue.push(new NotifyDeviceResetArgs(*args));
+ mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
}
void QueuedInputListener::flush() {
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 519faa6..3996cca 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -19,25 +19,23 @@
//#define LOG_NDEBUG 0
#include "InputManager.h"
+#include "InputReaderFactory.h"
+
+#include <binder/IPCThreadState.h>
#include <log/log.h>
+#include <unordered_map>
+
+#include <private/android_filesystem_config.h>
namespace android {
InputManager::InputManager(
- const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
- mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
- initialize();
-}
-
-InputManager::InputManager(
- const sp<InputReaderInterface>& reader,
- const sp<InputDispatcherInterface>& dispatcher) :
- mReader(reader),
- mDispatcher(dispatcher) {
+ mClassifier = new InputClassifier(mDispatcher);
+ mReader = createInputReader(readerPolicy, mClassifier);
initialize();
}
@@ -86,8 +84,57 @@
return mReader;
}
+sp<InputClassifierInterface> InputManager::getClassifier() {
+ return mClassifier;
+}
+
sp<InputDispatcherInterface> InputManager::getDispatcher() {
return mDispatcher;
}
+class BinderWindowHandle : public InputWindowHandle {
+public:
+ BinderWindowHandle(const InputWindowInfo& info) {
+ mInfo = info;
+ }
+
+ bool updateInfo() override {
+ return true;
+ }
+};
+
+void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener) {
+ std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
+
+ std::vector<sp<InputWindowHandle>> handles;
+ for (const auto& info : infos) {
+ handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
+ handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info));
+ }
+ for (auto const& i : handlesPerDisplay) {
+ mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener);
+ }
+}
+
+void InputManager::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+ mDispatcher->transferTouchFocus(fromToken, toToken);
+}
+
+// Used by tests only.
+void InputManager::registerInputChannel(const sp<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;
+ }
+ mDispatcher->registerInputChannel(channel, false);
+}
+
+void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
+ mDispatcher->unregisterInputChannel(channel);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index a213b2d..e568df5 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -22,18 +22,23 @@
*/
#include "EventHub.h"
-#include "InputReader.h"
+#include "InputReaderBase.h"
+#include "InputClassifier.h"
#include "InputDispatcher.h"
+#include "InputReader.h"
#include <input/Input.h>
#include <input/InputTransport.h>
+#include <input/ISetInputWindowsListener.h>
+
+#include <input/IInputFlinger.h>
#include <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
-#include <utils/String8.h>
namespace android {
+class InputChannel;
/*
* The input manager is the core of the system event processing.
@@ -73,31 +78,35 @@
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
-class InputManager : public InputManagerInterface {
+class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:
virtual ~InputManager();
public:
InputManager(
- const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
- // (used for testing purposes)
- InputManager(
- const sp<InputReaderInterface>& reader,
- const sp<InputDispatcherInterface>& dispatcher);
-
virtual status_t start();
virtual status_t stop();
virtual sp<InputReaderInterface> getReader();
+ virtual sp<InputClassifierInterface> getClassifier();
virtual sp<InputDispatcherInterface> getDispatcher();
+ virtual void setInputWindows(const std::vector<InputWindowInfo>& handles,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener);
+ virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
+
+ virtual void registerInputChannel(const sp<InputChannel>& channel);
+ virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
+ sp<InputClassifierInterface> mClassifier;
+
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index efa6f88..0cc37a0 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -57,6 +57,7 @@
#include <android-base/stringprintf.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
+#include <statslog.h>
#define INDENT " "
#define INDENT2 " "
@@ -71,18 +72,21 @@
// --- Constants ---
// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static const size_t MAX_SLOTS = 32;
+static constexpr size_t MAX_SLOTS = 32;
// Maximum amount of latency to add to touch events while waiting for data from an
// external stylus.
-static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
// Maximum amount of time to wait on touch data before pushing out new pressure data.
-static const nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
// Artificial latency on synthetic events created from stylus data without corresponding touch
// data.
-static const nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
+// How often to report input event statistics
+static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60);
// --- Static Functions ---
@@ -226,7 +230,7 @@
}
static void synthesizeButtonKey(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source,
+ nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId,
uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState,
int32_t buttonState, int32_t keyCode) {
if (
@@ -236,106 +240,31 @@
|| (action == AKEY_EVENT_ACTION_UP
&& (lastButtonState & buttonState)
&& !(currentButtonState & buttonState))) {
- NotifyKeyArgs args(when, deviceId, source, policyFlags,
- action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+ NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId,
+ policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
context->getListener()->notifyKey(&args);
}
}
static void synthesizeButtonKeys(InputReaderContext* context, int32_t action,
- nsecs_t when, int32_t deviceId, uint32_t source,
+ nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId,
uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
+ synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
lastButtonState, currentButtonState,
AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, deviceId, source, policyFlags,
+ synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
lastButtonState, currentButtonState,
AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
}
-// --- InputReaderConfiguration ---
-
-bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType,
- const String8* uniqueDisplayId, DisplayViewport* outViewport) const {
- const DisplayViewport* viewport = NULL;
- if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) {
- for (const DisplayViewport& currentViewport : mVirtualDisplays) {
- if (currentViewport.uniqueId == *uniqueDisplayId) {
- viewport = ¤tViewport;
- break;
- }
- }
- } else if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
- viewport = &mExternalDisplay;
- } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
- viewport = &mInternalDisplay;
- }
-
- if (viewport != NULL && viewport->displayId >= 0) {
- *outViewport = *viewport;
- return true;
- }
- return false;
-}
-
-void InputReaderConfiguration::setPhysicalDisplayViewport(ViewportType viewportType,
- const DisplayViewport& viewport) {
- if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
- mExternalDisplay = viewport;
- } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
- mInternalDisplay = viewport;
- }
-}
-
-void InputReaderConfiguration::setVirtualDisplayViewports(
- const Vector<DisplayViewport>& viewports) {
- mVirtualDisplays = viewports;
-}
-
-void InputReaderConfiguration::dump(std::string& dump) const {
- dump += INDENT4 "ViewportInternal:\n";
- dumpViewport(dump, mInternalDisplay);
- dump += INDENT4 "ViewportExternal:\n";
- dumpViewport(dump, mExternalDisplay);
- dump += INDENT4 "ViewportVirtual:\n";
- for (const DisplayViewport& viewport : mVirtualDisplays) {
- dumpViewport(dump, viewport);
- }
-}
-
-void InputReaderConfiguration::dumpViewport(std::string& dump, const DisplayViewport& viewport) const {
- dump += StringPrintf(INDENT5 "Viewport: displayId=%d, orientation=%d, uniqueId='%s', "
- "logicalFrame=[%d, %d, %d, %d], "
- "physicalFrame=[%d, %d, %d, %d], "
- "deviceSize=[%d, %d]\n",
- viewport.displayId, viewport.orientation, viewport.uniqueId.c_str(),
- viewport.logicalLeft, viewport.logicalTop,
- viewport.logicalRight, viewport.logicalBottom,
- viewport.physicalLeft, viewport.physicalTop,
- viewport.physicalRight, viewport.physicalBottom,
- viewport.deviceWidth, viewport.deviceHeight);
-}
-
-
-// -- TouchAffineTransformation --
-void TouchAffineTransformation::applyTo(float& x, float& y) const {
- float newX, newY;
- newX = x * x_scale + y * x_ymix + x_offset;
- newY = x * y_xmix + y * y_scale + y_offset;
-
- x = newX;
- y = newY;
-}
-
-
// --- InputReader ---
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
- mGlobalMetaState(0), mGeneration(1),
+ mNextSequenceNum(1), mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
@@ -358,7 +287,7 @@
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
- Vector<InputDeviceInfo> inputDevices;
+ std::vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
@@ -473,10 +402,10 @@
if (device->isIgnored()) {
ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
- identifier.name.string());
+ identifier.name.c_str());
} else {
ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
- identifier.name.string(), device->getSources());
+ identifier.name.c_str(), device->getSources());
}
mDevices.add(deviceId, device);
@@ -488,7 +417,7 @@
}
void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
- InputDevice* device = NULL;
+ InputDevice* device = nullptr;
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
@@ -501,10 +430,10 @@
if (device->isIgnored()) {
ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)",
- device->getId(), device->getName().string());
+ device->getId(), device->getName().c_str());
} else {
ALOGI("Device removed: id=%d, name='%s', sources=0x%08x",
- device->getId(), device->getName().string(), device->getSources());
+ device->getId(), device->getName().c_str(), device->getSources());
}
if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -621,7 +550,7 @@
updateGlobalMetaStateLocked();
// Enqueue configuration changed.
- NotifyConfigurationChangedArgs args(when);
+ NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
mQueuedListener->notifyConfigurationChanged(&args);
}
@@ -661,12 +590,13 @@
refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
}
-void InputReader::getExternalStylusDevicesLocked(Vector<InputDeviceInfo>& outDevices) {
+void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
- outDevices.push();
- device->getDeviceInfo(&outDevices.editTop());
+ InputDeviceInfo info;
+ device->getDeviceInfo(&info);
+ outDevices.push_back(info);
}
}
}
@@ -687,7 +617,7 @@
if (now < mDisableVirtualKeysTimeout) {
ALOGI("Dropping virtual key from device %s because virtual keys are "
"temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d",
- device->getName().string(),
+ device->getName().c_str(),
(mDisableVirtualKeysTimeout - now) * 0.000001,
keyCode, scanCode);
return true;
@@ -714,20 +644,21 @@
return ++mGeneration;
}
-void InputReader::getInputDevices(Vector<InputDeviceInfo>& outInputDevices) {
+void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
AutoMutex _l(mLock);
getInputDevicesLocked(outInputDevices);
}
-void InputReader::getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices) {
+void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
outInputDevices.clear();
size_t numDevices = mDevices.size();
for (size_t i = 0; i < numDevices; i++) {
InputDevice* device = mDevices.valueAt(i);
if (!device->isIgnored()) {
- outInputDevices.push();
- device->getDeviceInfo(&outInputDevices.editTop());
+ InputDeviceInfo info;
+ device->getDeviceInfo(&info);
+ outInputDevices.push_back(info);
}
}
}
@@ -876,6 +807,30 @@
return false;
}
+bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+ AutoMutex _l(mLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
+ return false;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+ // No associated display. By default, can dispatch to all displays.
+ if (!associatedDisplayId) {
+ return true;
+ }
+
+ if (*associatedDisplayId == ADISPLAY_ID_NONE) {
+ ALOGW("Device has associated, but no associated display id.");
+ return true;
+ }
+
+ return *associatedDisplayId == displayId;
+}
+
void InputReader::dump(std::string& dump) {
AutoMutex _l(mLock);
@@ -894,7 +849,7 @@
if (i != 0) {
dump += ", ";
}
- dump += mConfig.excludedDeviceNames.itemAt(i).string();
+ dump += mConfig.excludedDeviceNames[i];
}
dump += "]\n";
dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
@@ -998,7 +953,7 @@
return mReader->bumpGenerationLocked();
}
-void InputReader::ContextImpl::getExternalStylusDevices(Vector<InputDeviceInfo>& outDevices) {
+void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
// lock is already held by whatever called refreshConfigurationLocked
mReader->getExternalStylusDevicesLocked(outDevices);
}
@@ -1019,22 +974,10 @@
return mReader->mEventHub.get();
}
-
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
+uint32_t InputReader::ContextImpl::getNextSequenceNum() {
+ return (mReader->mNextSequenceNum)++;
}
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
-}
-
-
// --- InputDevice ---
InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
@@ -1074,21 +1017,27 @@
void InputDevice::dump(std::string& dump) {
InputDeviceInfo deviceInfo;
- getDeviceInfo(& deviceInfo);
+ getDeviceInfo(&deviceInfo);
dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
- deviceInfo.getDisplayName().string());
+ deviceInfo.getDisplayName().c_str());
dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+ dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+ if (mAssociatedDisplayPort) {
+ dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+ } else {
+ dump += "<none>\n";
+ }
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
- const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
- if (!ranges.isEmpty()) {
+ const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
+ if (!ranges.empty()) {
dump += INDENT2 "Motion Ranges:\n";
for (size_t i = 0; i < ranges.size(); i++) {
- const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
+ const InputDeviceInfo::MotionRange& range = ranges[i];
const char* label = getAxisLabel(range.axis);
char name[32];
if (label) {
@@ -1112,7 +1061,7 @@
}
void InputDevice::addMapper(InputMapper* mapper) {
- mMappers.add(mapper);
+ mMappers.push_back(mapper);
}
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) {
@@ -1135,7 +1084,7 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
- String8 alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
+ std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
if (mAlias != alias) {
mAlias = alias;
bumpGeneration();
@@ -1149,9 +1098,21 @@
setEnabled(enabled, when);
}
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ // In most situations, no port will be specified.
+ mAssociatedDisplayPort = std::nullopt;
+ // Find the display port that corresponds to the current input port.
+ const std::string& inputPort = mIdentifier.location;
+ if (!inputPort.empty()) {
+ const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+ const auto& displayPort = ports.find(inputPort);
+ if (displayPort != ports.end()) {
+ mAssociatedDisplayPort = std::make_optional(displayPort->second);
+ }
+ }
+ }
+
+ for (InputMapper* mapper : mMappers) {
mapper->configure(when, config, changes);
mSources |= mapper->getSources();
}
@@ -1159,9 +1120,7 @@
}
void InputDevice::reset(nsecs_t when) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->reset(when);
}
@@ -1176,7 +1135,6 @@
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
- size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
#if DEBUG_RAW_EVENTS
ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
@@ -1196,12 +1154,11 @@
#endif
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
- ALOGI("Detected input event buffer overrun for device %s.", getName().string());
+ ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->process(rawEvent);
}
}
@@ -1210,17 +1167,13 @@
}
void InputDevice::timeoutExpired(nsecs_t when) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->timeoutExpired(when);
}
}
void InputDevice::updateExternalStylusState(const StylusState& state) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->updateExternalStylusState(state);
}
}
@@ -1228,9 +1181,7 @@
void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias,
mIsExternal, mHasMic);
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->populateDeviceInfo(outDeviceInfo);
}
}
@@ -1249,9 +1200,7 @@
int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
int32_t result = AKEY_STATE_UNKNOWN;
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
// If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
// value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
@@ -1269,9 +1218,7 @@
bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) {
bool result = false;
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
}
@@ -1281,50 +1228,39 @@
void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
int32_t token) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->vibrate(pattern, patternSize, repeat, token);
}
}
void InputDevice::cancelVibrate(int32_t token) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->cancelVibrate(token);
}
}
void InputDevice::cancelTouch(nsecs_t when) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->cancelTouch(when);
}
}
int32_t InputDevice::getMetaState() {
int32_t result = 0;
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
result |= mapper->getMetaState();
}
return result;
}
void InputDevice::updateMetaState(int32_t keyCode) {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- mMappers[i]->updateMetaState(keyCode);
+ for (InputMapper* mapper : mMappers) {
+ mapper->updateMetaState(keyCode);
}
}
void InputDevice::fadePointer() {
- size_t numMappers = mMappers.size();
- for (size_t i = 0; i < numMappers; i++) {
- InputMapper* mapper = mMappers[i];
+ for (InputMapper* mapper : mMappers) {
mapper->fadePointer();
}
}
@@ -1334,10 +1270,20 @@
}
void InputDevice::notifyReset(nsecs_t when) {
- NotifyDeviceResetArgs args(when, mId);
+ NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
mContext->getListener()->notifyDeviceReset(&args);
}
+std::optional<int32_t> InputDevice::getAssociatedDisplay() {
+ for (InputMapper* mapper : mMappers) {
+ std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
+ if (associatedDisplayId) {
+ return associatedDisplayId;
+ }
+ }
+
+ return std::nullopt;
+}
// --- CursorButtonAccumulator ---
@@ -1786,7 +1732,7 @@
// --- MultiTouchMotionAccumulator ---
MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() :
- mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false),
+ mCurrentSlot(-1), mSlots(nullptr), mSlotCount(0), mUsingSlotsProtocol(false),
mHaveStylus(false), mDeviceTimestamp(0) {
}
@@ -2107,7 +2053,8 @@
void SwitchInputMapper::sync(nsecs_t when) {
if (mUpdatedSwitchMask) {
uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
- NotifySwitchArgs args(when, 0, updatedSwitchValues, mUpdatedSwitchMask);
+ NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
+ mUpdatedSwitchMask);
getListener()->notifySwitch(&args);
mUpdatedSwitchMask = 0;
@@ -2240,8 +2187,7 @@
KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
uint32_t source, int32_t keyboardType) :
- InputMapper(device), mSource(source),
- mKeyboardType(keyboardType) {
+ InputMapper(device), mSource(source), mKeyboardType(keyboardType) {
}
KeyboardInputMapper::~KeyboardInputMapper() {
@@ -2251,6 +2197,20 @@
return mSource;
}
+int32_t KeyboardInputMapper::getOrientation() {
+ if (mViewport) {
+ return mViewport->orientation;
+ }
+ return DISPLAY_ORIENTATION_0;
+}
+
+int32_t KeyboardInputMapper::getDisplayId() {
+ if (mViewport) {
+ return mViewport->displayId;
+ }
+ return ADISPLAY_ID_NONE;
+}
+
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
@@ -2262,13 +2222,12 @@
dump += INDENT2 "Keyboard Input Mapper:\n";
dumpParameters(dump);
dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
- dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
+ dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
}
-
void KeyboardInputMapper::configure(nsecs_t when,
const InputReaderConfiguration* config, uint32_t changes) {
InputMapper::configure(when, config, changes);
@@ -2279,15 +2238,8 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- DisplayViewport v;
- if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
- mOrientation = v.orientation;
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
- }
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
+ if (mParameters.orientationAware) {
+ mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
}
}
}
@@ -2310,10 +2262,7 @@
config.tryGetProperty(String8("keyboard.orientationAware"),
mParameters.orientationAware);
- mParameters.hasAssociatedDisplay = false;
if (mParameters.orientationAware) {
- mParameters.hasAssociatedDisplay = true;
-
mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
@@ -2327,8 +2276,6 @@
void KeyboardInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
- dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
- toString(mParameters.hasAssociatedDisplay));
dump += StringPrintf(INDENT4 "OrientationAware: %s\n",
toString(mParameters.orientationAware));
dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n",
@@ -2423,15 +2370,15 @@
if (down) {
// Rotate key codes according to orientation if needed.
- if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- keyCode = rotateKeyCode(keyCode, mOrientation);
+ if (mParameters.orientationAware) {
+ keyCode = rotateKeyCode(keyCode, getOrientation());
}
// Add key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
+ keyCode = mKeyDowns[keyDownIndex].keyCode;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL)
@@ -2443,10 +2390,10 @@
mDevice->cancelTouch(when);
}
- mKeyDowns.push();
- KeyDown& keyDown = mKeyDowns.editTop();
+ KeyDown keyDown;
keyDown.keyCode = keyCode;
keyDown.scanCode = scanCode;
+ mKeyDowns.push_back(keyDown);
}
mDownTime = when;
@@ -2455,13 +2402,13 @@
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
- mKeyDowns.removeAt(size_t(keyDownIndex));
+ keyCode = mKeyDowns[keyDownIndex].keyCode;
+ mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
} else {
// key was not actually down
ALOGI("Dropping key up from device %s because the key was not down. "
"keyCode=%d, scanCode=%d",
- getDeviceName().string(), keyCode, scanCode);
+ getDeviceName().c_str(), keyCode, scanCode);
return;
}
}
@@ -2489,8 +2436,8 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
@@ -2699,15 +2646,18 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mOrientation = DISPLAY_ORIENTATION_0;
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
- DisplayViewport v;
- if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
- mOrientation = v.orientation;
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ if (internalViewport) {
+ mOrientation = internalViewport->orientation;
}
- } else {
- mOrientation = DISPLAY_ORIENTATION_0;
+ }
+
+ // Update the PointerController if viewports changed.
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ getPolicy()->obtainPointerController(getDeviceId());
}
bumpGeneration();
}
@@ -2826,8 +2776,8 @@
float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
bool scrolled = vscroll != 0 || hscroll != 0;
- mWheelYVelocityControl.move(when, NULL, &vscroll);
- mWheelXVelocityControl.move(when, &hscroll, NULL);
+ mWheelYVelocityControl.move(when, nullptr, &vscroll);
+ mWheelXVelocityControl.move(when, &hscroll, nullptr);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
@@ -2854,7 +2804,7 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
- displayId = ADISPLAY_ID_DEFAULT;
+ displayId = mPointerController->getDisplayId();
} else {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
@@ -2874,7 +2824,7 @@
// Synthesize key down from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- policyFlags, lastButtonState, currentButtonState);
+ displayId, policyFlags, lastButtonState, currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
@@ -2894,20 +2844,22 @@
while (!released.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
- NotifyMotionArgs releaseArgs(when, getDeviceId(), mSource, policyFlags,
+ NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
+ metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
getListener()->notifyMotion(&releaseArgs);
}
}
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- motionEventAction, 0, 0, metaState, currentButtonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+ displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
if (buttonsPressed) {
@@ -2915,11 +2867,12 @@
while (!pressed.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
- NotifyMotionArgs pressArgs(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
+ NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
getListener()->notifyMotion(&pressArgs);
}
}
@@ -2929,11 +2882,12 @@
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP
&& (mSource == AINPUT_SOURCE_MOUSE)) {
- NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
+ NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, currentButtonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
getListener()->notifyMotion(&hoverArgs);
}
@@ -2942,18 +2896,19 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
+ NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, downTime);
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
getListener()->notifyMotion(&scrollArgs);
}
}
// Synthesize key up from buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- policyFlags, lastButtonState, currentButtonState);
+ displayId, policyFlags, lastButtonState, currentButtonState);
mCursorMotionAccumulator.finishSync();
mCursorScrollAccumulator.finishSync();
@@ -2968,11 +2923,24 @@
}
void CursorInputMapper::fadePointer() {
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
}
}
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+ if (mParameters.hasAssociatedDisplay) {
+ if (mParameters.mode == Parameters::MODE_POINTER) {
+ return std::make_optional(mPointerController->getDisplayId());
+ } else {
+ // If the device is orientationAware and not a mouse,
+ // it expects to dispatch events to any display
+ return std::make_optional(ADISPLAY_ID_NONE);
+ }
+ }
+ return std::nullopt;
+}
+
// --- RotaryEncoderInputMapper ---
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) :
@@ -3019,9 +2987,10 @@
mRotaryEncoderScrollAccumulator.configure(getDevice());
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- DisplayViewport v;
- if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
- mOrientation = v.orientation;
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ if (internalViewport) {
+ mOrientation = internalViewport->orientation;
} else {
mOrientation = DISPLAY_ORIENTATION_0;
}
@@ -3072,11 +3041,12 @@
int32_t metaState = mContext->getGlobalMetaState();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
- NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- 0, 0, 0);
+ NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ 0, 0, 0, /* videoFrames */ {});
getListener()->notifyMotion(&scrollArgs);
}
@@ -3319,9 +3289,9 @@
}
void TouchInputMapper::resolveExternalStylusPresence() {
- Vector<InputDeviceInfo> devices;
+ std::vector<InputDeviceInfo> devices;
mContext->getExternalStylusDevices(devices);
- mExternalStylusConnected = !devices.isEmpty();
+ mExternalStylusConnected = !devices.empty();
if (!mExternalStylusConnected) {
resetExternalStylus();
@@ -3394,10 +3364,15 @@
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+ String8 uniqueDisplayId;
getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
- mParameters.uniqueDisplayId);
+ uniqueDisplayId);
+ mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
}
}
+ if (getDevice()->getAssociatedDisplayPort()) {
+ mParameters.hasAssociatedDisplay = true;
+ }
// Initial downs on external touch devices should wake the device.
// Normally we don't do this for internal touch screens to prevent them from waking
@@ -3472,6 +3447,59 @@
return mExternalStylusConnected;
}
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+ if (mParameters.hasAssociatedDisplay) {
+ const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+ if (displayPort) {
+ // Find the viewport that contains the same port
+ std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
+ if (!v) {
+ ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+ "but the corresponding viewport is not found.",
+ getDeviceName().c_str(), *displayPort);
+ }
+ return v;
+ }
+
+ if (!mParameters.uniqueDisplayId.empty()) {
+ return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+ }
+
+ ViewportType viewportTypeToUse;
+ if (mParameters.associatedDisplayIsExternal) {
+ viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+ } else {
+ viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+ }
+
+ std::optional<DisplayViewport> viewport =
+ mConfig.getDisplayViewportByType(viewportTypeToUse);
+ if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_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);
+ }
+
+ return viewport;
+ }
+
+ DisplayViewport newViewport;
+ // Raw width and height in the natural orientation.
+ int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+ return std::make_optional(newViewport);
+}
+
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
int32_t oldDeviceMode = mDeviceMode;
@@ -3505,47 +3533,30 @@
// Ensure we have valid X and Y axes.
if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
- ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis! "
- "The device will be inoperable.", getDeviceName().string());
+ 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;
+ return;
+ }
+
+ // Get associated display dimensions.
+ std::optional<DisplayViewport> newViewport = findViewport();
+ if (!newViewport) {
+ ALOGI("Touch device '%s' could not query the properties of its associated "
+ "display. The device will be inoperable until the display size "
+ "becomes available.",
+ getDeviceName().c_str());
mDeviceMode = DEVICE_MODE_DISABLED;
return;
}
// Raw width and height in the natural orientation.
- int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
- int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+ int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ int32_t rawHeight = mRawPointerAxes.getRawHeight();
- // Get associated display dimensions.
- DisplayViewport newViewport;
- if (mParameters.hasAssociatedDisplay) {
- const String8* uniqueDisplayId = NULL;
- ViewportType viewportTypeToUse;
-
- if (mParameters.associatedDisplayIsExternal) {
- viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
- } else if (!mParameters.uniqueDisplayId.isEmpty()) {
- // If the IDC file specified a unique display Id, then it expects to be linked to a
- // virtual display with the same unique ID.
- uniqueDisplayId = &mParameters.uniqueDisplayId;
- viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
- } else {
- viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
- }
-
- if (!mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId, &newViewport)) {
- ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
- "display. The device will be inoperable until the display size "
- "becomes available.",
- getDeviceName().string());
- mDeviceMode = DEVICE_MODE_DISABLED;
- return;
- }
- } else {
- newViewport.setNonDisplayViewport(rawWidth, rawHeight);
- }
- bool viewportChanged = mViewport != newViewport;
+ bool viewportChanged = mViewport != *newViewport;
if (viewportChanged) {
- mViewport = newViewport;
+ mViewport = *newViewport;
if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
// Convert rotated viewport to natural surface coordinates.
@@ -3597,6 +3608,12 @@
break;
}
+ if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
+ ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
+ naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
+ naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
+ }
+
mPhysicalWidth = naturalPhysicalWidth;
mPhysicalHeight = naturalPhysicalHeight;
mPhysicalLeft = naturalPhysicalLeft;
@@ -3629,10 +3646,10 @@
mOrientedRanges.clear();
}
- // Create pointer controller if needed.
+ // Create or update pointer controller if needed.
if (mDeviceMode == DEVICE_MODE_POINTER ||
(mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
- if (mPointerController == NULL) {
+ if (mPointerController == nullptr || viewportChanged) {
mPointerController = getPolicy()->obtainPointerController(getDeviceId());
}
} else {
@@ -3642,7 +3659,7 @@
if (viewportChanged || deviceModeChanged) {
ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
"display id %d",
- getDeviceId(), getDeviceName().string(), mSurfaceWidth, mSurfaceHeight,
+ getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
mSurfaceOrientation, mDeviceMode, mViewport.displayId);
// Configure X and Y factors.
@@ -3910,17 +3927,7 @@
}
void TouchInputMapper::dumpSurface(std::string& dump) {
- dump += StringPrintf(INDENT3 "Viewport: displayId=%d, orientation=%d, "
- "logicalFrame=[%d, %d, %d, %d], "
- "physicalFrame=[%d, %d, %d, %d], "
- "deviceSize=[%d, %d]\n",
- mViewport.displayId, mViewport.orientation,
- mViewport.logicalLeft, mViewport.logicalTop,
- mViewport.logicalRight, mViewport.logicalBottom,
- mViewport.physicalLeft, mViewport.physicalTop,
- mViewport.physicalRight, mViewport.physicalBottom,
- mViewport.deviceWidth, mViewport.deviceHeight);
-
+ dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
@@ -3933,7 +3940,7 @@
}
void TouchInputMapper::configureVirtualKeys() {
- Vector<VirtualKeyDefinition> virtualKeyDefinitions;
+ std::vector<VirtualKeyDefinition> virtualKeyDefinitions;
getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
mVirtualKeys.clear();
@@ -3942,19 +3949,13 @@
return;
}
- mVirtualKeys.setCapacity(virtualKeyDefinitions.size());
-
int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
int32_t touchScreenTop = mRawPointerAxes.y.minValue;
- int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
- int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+ int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
+ int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
- for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
- const VirtualKeyDefinition& virtualKeyDefinition =
- virtualKeyDefinitions[i];
-
- mVirtualKeys.add();
- VirtualKey& virtualKey = mVirtualKeys.editTop();
+ for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) {
+ VirtualKey virtualKey;
virtualKey.scanCode = virtualKeyDefinition.scanCode;
int32_t keyCode;
@@ -3964,8 +3965,7 @@
&keyCode, &dummyKeyMetaState, &flags)) {
ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
virtualKey.scanCode);
- mVirtualKeys.pop(); // drop the key
- continue;
+ continue; // drop the key
}
virtualKey.keyCode = keyCode;
@@ -3983,15 +3983,16 @@
* touchScreenHeight / mSurfaceHeight + touchScreenTop;
virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
* touchScreenHeight / mSurfaceHeight + touchScreenTop;
+ mVirtualKeys.push_back(virtualKey);
}
}
void TouchInputMapper::dumpVirtualKeys(std::string& dump) {
- if (!mVirtualKeys.isEmpty()) {
+ if (!mVirtualKeys.empty()) {
dump += INDENT3 "Virtual Keys:\n";
for (size_t i = 0; i < mVirtualKeys.size(); i++) {
- const VirtualKey& virtualKey = mVirtualKeys.itemAt(i);
+ const VirtualKey& virtualKey = mVirtualKeys[i];
dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
"hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
i, virtualKey.scanCode, virtualKey.keyCode,
@@ -4286,7 +4287,7 @@
mPointerSimple.reset();
resetExternalStylus();
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->clearSpots();
}
@@ -4306,23 +4307,37 @@
mExternalStylusFusionTimeout = LLONG_MAX;
}
+void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) {
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t latency = now - evdevTime;
+ mStatistics.addValue(nanoseconds_to_microseconds(latency));
+ nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime;
+ if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) {
+ android::util::stats_write(android::util::TOUCH_EVENT_REPORTED,
+ mStatistics.min, mStatistics.max, mStatistics.mean(), mStatistics.stdev());
+ mStatistics.reset(now);
+ }
+}
+
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ reportEventForStatistics(rawEvent->when);
sync(rawEvent->when);
}
}
void TouchInputMapper::sync(nsecs_t when) {
- const RawState* last = mRawStatesPending.isEmpty() ?
- &mCurrentRawState : &mRawStatesPending.top();
+ const RawState* last = mRawStatesPending.empty() ?
+ &mCurrentRawState : &mRawStatesPending.back();
// Push a new state.
- mRawStatesPending.push();
- RawState* next = &mRawStatesPending.editTop();
+ mRawStatesPending.emplace_back();
+
+ RawState* next = &mRawStatesPending.back();
next->clear();
next->when = when;
@@ -4389,7 +4404,7 @@
cookAndDispatch(mCurrentRawState.when);
}
if (count != 0) {
- mRawStatesPending.removeItemsAt(0, count);
+ mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
}
if (mExternalStylusDataPending) {
@@ -4448,7 +4463,8 @@
// Synthesize key down from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
+ mViewport.displayId, policyFlags,
+ mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
if (mDeviceMode == DEVICE_MODE_POINTER) {
@@ -4495,14 +4511,15 @@
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
if (mDeviceMode == DEVICE_MODE_DIRECT
- && mConfig.showTouches && mPointerController != NULL) {
+ && mConfig.showTouches && mPointerController != nullptr) {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits);
+ mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mViewport.displayId);
}
if (!mCurrentMotionAborted) {
@@ -4520,7 +4537,8 @@
// Synthesize key up from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
- policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
+ mViewport.displayId, policyFlags,
+ mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Clear some transient state.
mCurrentRawState.rawVScroll = 0;
@@ -4735,8 +4753,9 @@
int32_t metaState = mContext->getGlobalMetaState();
policyFlags |= POLICY_FLAG_VIRTUAL;
- NotifyKeyArgs args(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
- keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ mViewport.displayId,
+ policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
getListener()->notifyKey(&args);
}
@@ -5264,7 +5283,8 @@
if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
mPointerController->setSpots(mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits);
+ mPointerGesture.currentGestureIdBits,
+ mPointerController->getDisplayId());
}
} else {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
@@ -5428,11 +5448,13 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+ const int32_t displayId = mPointerController->getDisplayId();
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mViewport.displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- 0, 0, mPointerGesture.downTime);
+ metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
+ 0, 0, mPointerGesture.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -5473,7 +5495,7 @@
mPointerVelocityControl.reset();
// Remove any current spots.
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->clearSpots();
}
@@ -6335,8 +6357,9 @@
void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
bool down, bool hovering) {
int32_t metaState = getContext()->getGlobalMetaState();
+ int32_t displayId = mViewport.displayId;
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
if (down || hovering) {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
mPointerController->clearSpots();
@@ -6345,18 +6368,20 @@
} else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
}
+ displayId = mPointerController->getDisplayId();
}
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
// Send up.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
- 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
- mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+ 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+ mOrientedXPrecision, mOrientedYPrecision,
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -6364,12 +6389,13 @@
mPointerSimple.hovering = false;
// Send hover exit.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -6379,22 +6405,25 @@
mPointerSimple.downTime = when;
// Send down.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ /* deviceTimestamp */ 0,
1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
// Send move.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -6403,32 +6432,34 @@
mPointerSimple.hovering = true;
// Send hover enter.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
- mCurrentRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
// Send hover move.
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
float vscroll = mCurrentRawState.rawVScroll;
float hscroll = mCurrentRawState.rawHScroll;
- mWheelYVelocityControl.move(when, NULL, &vscroll);
- mWheelXVelocityControl.move(when, &hscroll, NULL);
+ mWheelYVelocityControl.move(when, nullptr, &vscroll);
+ mWheelXVelocityControl.move(when, &hscroll, nullptr);
// Send scroll.
PointerCoords pointerCoords;
@@ -6436,12 +6467,13 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
- AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0,
- mViewport.displayId, /* deviceTimestamp */ 0,
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
1, &mPointerSimple.currentProperties, &pointerCoords,
mOrientedXPrecision, mOrientedYPrecision,
- mPointerSimple.downTime);
+ mPointerSimple.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -6498,11 +6530,16 @@
ALOG_ASSERT(false);
}
}
-
- NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
- action, actionButton, flags, metaState, buttonState, edgeFlags,
- mViewport.displayId, deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
- xPrecision, yPrecision, downTime);
+ const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
+ const int32_t deviceId = getDeviceId();
+ std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
+ std::for_each(frames.begin(), frames.end(),
+ [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId,
+ source, displayId, policyFlags,
+ action, actionButton, flags, metaState, buttonState, MotionClassification::NONE,
+ edgeFlags, deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
+ xPrecision, yPrecision, downTime, std::move(frames));
getListener()->notifyMotion(&args);
}
@@ -6535,7 +6572,7 @@
}
void TouchInputMapper::fadePointer() {
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
}
}
@@ -6554,12 +6591,9 @@
&& scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight;
}
-const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(
- int32_t x, int32_t y) {
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
+ for (const VirtualKey& virtualKey: mVirtualKeys) {
#if DEBUG_VIRTUAL_KEYS
ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
"left=%d, top=%d, right=%d, bottom=%d",
@@ -6574,7 +6608,7 @@
}
}
- return NULL;
+ return nullptr;
}
void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
@@ -6769,9 +6803,7 @@
return AKEY_STATE_VIRTUAL;
}
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
if (virtualKey.keyCode == keyCode) {
return AKEY_STATE_UP;
}
@@ -6785,9 +6817,7 @@
return AKEY_STATE_VIRTUAL;
}
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
if (virtualKey.scanCode == scanCode) {
return AKEY_STATE_UP;
}
@@ -6798,10 +6828,7 @@
bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) {
- size_t numVirtualKeys = mVirtualKeys.size();
- for (size_t i = 0; i < numVirtualKeys; i++) {
- const VirtualKey& virtualKey = mVirtualKeys[i];
-
+ for (const VirtualKey& virtualKey : mVirtualKeys) {
for (size_t i = 0; i < numCodes; i++) {
if (virtualKey.keyCode == keyCodes[i]) {
outFlags[i] = 1;
@@ -6812,6 +6839,16 @@
return true;
}
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+ if (mParameters.hasAssociatedDisplay) {
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ return std::make_optional(mPointerController->getDisplayId());
+ } else {
+ return std::make_optional(mViewport.displayId);
+ }
+ }
+ return std::nullopt;
+}
// --- SingleTouchInputMapper ---
@@ -6923,7 +6960,7 @@
#if DEBUG_POINTERS
ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
"ignoring the rest.",
- getDeviceName().string(), MAX_POINTERS);
+ getDeviceName().c_str(), MAX_POINTERS);
#endif
break; // too many fingers!
}
@@ -7014,7 +7051,7 @@
if (slotCount > MAX_SLOTS) {
ALOGW("MultiTouch Device %s reported %zu slots but the framework "
"only supports a maximum of %zu slots at this time.",
- getDeviceName().string(), slotCount, MAX_SLOTS);
+ getDeviceName().c_str(), slotCount, MAX_SLOTS);
slotCount = MAX_SLOTS;
}
mMultiTouchMotionAccumulator.configure(getDevice(),
@@ -7257,7 +7294,7 @@
// Prefer to keep explicitly mapped axes.
if (mAxes.size() > PointerCoords::MAX_AXES) {
ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
- getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
+ getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES);
pruneAxes(true);
pruneAxes(false);
}
@@ -7279,7 +7316,7 @@
} else {
ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
"have already been assigned to other axes.",
- getDeviceName().string(), mAxes.keyAt(i));
+ getDeviceName().c_str(), mAxes.keyAt(i));
mAxes.removeItemsAt(i--);
numAxes -= 1;
}
@@ -7308,7 +7345,7 @@
continue;
}
ALOGI("Discarding joystick '%s' axis %d because there are too many axes.",
- getDeviceName().string(), mAxes.keyAt(i));
+ getDeviceName().c_str(), mAxes.keyAt(i));
mAxes.removeItemsAt(i);
}
}
@@ -7423,10 +7460,11 @@
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
- NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
- AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- ADISPLAY_ID_NONE, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
- 0, 0, 0);
+ NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
+ AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
+ &pointerProperties, &pointerCoords, 0, 0, 0, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 2f98e69..9777779 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -20,6 +20,7 @@
#include "EventHub.h"
#include "PointerControllerInterface.h"
#include "InputListener.h"
+#include "InputReaderBase.h"
#include <input/DisplayViewport.h>
#include <input/Input.h>
@@ -28,315 +29,20 @@
#include <ui/DisplayInfo.h>
#include <utils/KeyedVector.h>
#include <utils/Condition.h>
-#include <utils/Thread.h>
#include <utils/Mutex.h>
#include <utils/Timers.h>
-#include <utils/RefBase.h>
#include <utils/BitSet.h>
-#include <utils/SortedVector.h>
+#include <optional>
#include <stddef.h>
#include <unistd.h>
-
-// Maximum supported size of a vibration pattern.
-// Must be at least 2.
-#define MAX_VIBRATE_PATTERN_SIZE 100
-
-// Maximum allowable delay value in a vibration pattern before
-// which the delay will be truncated.
-#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+#include <vector>
namespace android {
class InputDevice;
class InputMapper;
-/*
- * Input reader configuration.
- *
- * Specifies various options that modify the behavior of the input reader.
- */
-struct InputReaderConfiguration {
- // Describes changes that have occurred.
- enum {
- // The pointer speed changed.
- CHANGE_POINTER_SPEED = 1 << 0,
-
- // The pointer gesture control changed.
- CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
-
- // The display size or orientation changed.
- CHANGE_DISPLAY_INFO = 1 << 2,
-
- // The visible touches option changed.
- CHANGE_SHOW_TOUCHES = 1 << 3,
-
- // The keyboard layouts must be reloaded.
- CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
-
- // The device name alias supplied by the may have changed for some devices.
- CHANGE_DEVICE_ALIAS = 1 << 5,
-
- // The location calibration matrix changed.
- CHANGE_TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
-
- // The presence of an external stylus has changed.
- CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
-
- // The pointer capture mode has changed.
- CHANGE_POINTER_CAPTURE = 1 << 8,
-
- // The set of disabled input devices (disabledDevices) has changed.
- CHANGE_ENABLED_STATE = 1 << 9,
-
- // All devices must be reopened.
- CHANGE_MUST_REOPEN = 1 << 31,
- };
-
- // Gets the amount of time to disable virtual keys after the screen is touched
- // in order to filter out accidental virtual key presses due to swiping gestures
- // or taps near the edge of the display. May be 0 to disable the feature.
- nsecs_t virtualKeyQuietTime;
-
- // The excluded device names for the platform.
- // Devices with these names will be ignored.
- Vector<String8> excludedDeviceNames;
-
- // Velocity control parameters for mouse pointer movements.
- VelocityControlParameters pointerVelocityControlParameters;
-
- // Velocity control parameters for mouse wheel movements.
- VelocityControlParameters wheelVelocityControlParameters;
-
- // True if pointer gestures are enabled.
- bool pointerGesturesEnabled;
-
- // Quiet time between certain pointer gesture transitions.
- // Time to allow for all fingers or buttons to settle into a stable state before
- // starting a new gesture.
- nsecs_t pointerGestureQuietInterval;
-
- // The minimum speed that a pointer must travel for us to consider switching the active
- // touch pointer to it during a drag. This threshold is set to avoid switching due
- // to noise from a finger resting on the touch pad (perhaps just pressing it down).
- float pointerGestureDragMinSwitchSpeed; // in pixels per second
-
- // Tap gesture delay time.
- // The time between down and up must be less than this to be considered a tap.
- nsecs_t pointerGestureTapInterval;
-
- // Tap drag gesture delay time.
- // The time between the previous tap's up and the next down must be less than
- // this to be considered a drag. Otherwise, the previous tap is finished and a
- // new tap begins.
- //
- // Note that the previous tap will be held down for this entire duration so this
- // interval must be shorter than the long press timeout.
- nsecs_t pointerGestureTapDragInterval;
-
- // The distance in pixels that the pointer is allowed to move from initial down
- // to up and still be called a tap.
- float pointerGestureTapSlop; // in pixels
-
- // Time after the first touch points go down to settle on an initial centroid.
- // This is intended to be enough time to handle cases where the user puts down two
- // fingers at almost but not quite exactly the same time.
- nsecs_t pointerGestureMultitouchSettleInterval;
-
- // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
- // at least two pointers have moved at least this far from their starting place.
- float pointerGestureMultitouchMinDistance; // in pixels
-
- // The transition from PRESS to SWIPE gesture mode can only occur when the
- // cosine of the angle between the two vectors is greater than or equal to than this value
- // which indicates that the vectors are oriented in the same direction.
- // When the vectors are oriented in the exactly same direction, the cosine is 1.0.
- // (In exactly opposite directions, the cosine is -1.0.)
- float pointerGestureSwipeTransitionAngleCosine;
-
- // The transition from PRESS to SWIPE gesture mode can only occur when the
- // fingers are no more than this far apart relative to the diagonal size of
- // the touch pad. For example, a ratio of 0.5 means that the fingers must be
- // no more than half the diagonal size of the touch pad apart.
- float pointerGestureSwipeMaxWidthRatio;
-
- // The gesture movement speed factor relative to the size of the display.
- // Movement speed applies when the fingers are moving in the same direction.
- // Without acceleration, a full swipe of the touch pad diagonal in movement mode
- // will cover this portion of the display diagonal.
- float pointerGestureMovementSpeedRatio;
-
- // The gesture zoom speed factor relative to the size of the display.
- // Zoom speed applies when the fingers are mostly moving relative to each other
- // to execute a scale gesture or similar.
- // Without acceleration, a full swipe of the touch pad diagonal in zoom mode
- // will cover this portion of the display diagonal.
- float pointerGestureZoomSpeedRatio;
-
- // True to show the location of touches on the touch screen as spots.
- bool showTouches;
-
- // True if pointer capture is enabled.
- bool pointerCapture;
-
- // The set of currently disabled input devices.
- SortedVector<int32_t> disabledDevices;
-
- InputReaderConfiguration() :
- virtualKeyQuietTime(0),
- pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
- wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
- pointerGesturesEnabled(true),
- pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
- pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
- pointerGestureTapInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
- pointerGestureTapSlop(10.0f), // 10 pixels
- pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
- pointerGestureMultitouchMinDistance(15), // 15 pixels
- pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
- pointerGestureSwipeMaxWidthRatio(0.25f),
- pointerGestureMovementSpeedRatio(0.8f),
- pointerGestureZoomSpeedRatio(0.3f),
- showTouches(false) { }
-
- bool getDisplayViewport(ViewportType viewportType, const String8* displayId,
- DisplayViewport* outViewport) const;
- void setPhysicalDisplayViewport(ViewportType viewportType, const DisplayViewport& viewport);
- void setVirtualDisplayViewports(const Vector<DisplayViewport>& viewports);
-
-
- void dump(std::string& dump) const;
- void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
-
-private:
- DisplayViewport mInternalDisplay;
- DisplayViewport mExternalDisplay;
- Vector<DisplayViewport> mVirtualDisplays;
-};
-
-
-struct TouchAffineTransformation {
- float x_scale;
- float x_ymix;
- float x_offset;
- float y_xmix;
- float y_scale;
- float y_offset;
-
- TouchAffineTransformation() :
- x_scale(1.0f), x_ymix(0.0f), x_offset(0.0f),
- y_xmix(0.0f), y_scale(1.0f), y_offset(0.0f) {
- }
-
- TouchAffineTransformation(float xscale, float xymix, float xoffset,
- float yxmix, float yscale, float yoffset) :
- x_scale(xscale), x_ymix(xymix), x_offset(xoffset),
- y_xmix(yxmix), y_scale(yscale), y_offset(yoffset) {
- }
-
- void applyTo(float& x, float& y) const;
-};
-
-
-/*
- * Input reader policy interface.
- *
- * The input reader policy is used by the input reader to interact with the Window Manager
- * and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI. This interface is also mocked in the unit tests.
- *
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
- */
-class InputReaderPolicyInterface : public virtual RefBase {
-protected:
- InputReaderPolicyInterface() { }
- virtual ~InputReaderPolicyInterface() { }
-
-public:
- /* Gets the input reader configuration. */
- 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;
-
- /* Notifies the input reader policy that some input devices have changed
- * and provides information about all current input devices.
- */
- virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
-
- /* Gets the keyboard layout for a particular input device. */
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) = 0;
-
- /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
- virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
-
- /* Gets the affine calibration associated with the specified device. */
- virtual TouchAffineTransformation getTouchAffineTransformation(
- const String8& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
-};
-
-
-/* Processes raw input events and sends cooked event data to an input listener. */
-class InputReaderInterface : public virtual RefBase {
-protected:
- InputReaderInterface() { }
- virtual ~InputReaderInterface() { }
-
-public:
- /* Dumps the state of the input reader.
- *
- * This method may be called on any thread (usually by the input manager). */
- virtual void dump(std::string& dump) = 0;
-
- /* Called by the heatbeat to ensures that the reader has not deadlocked. */
- virtual void monitor() = 0;
-
- /* Returns true if the input device is enabled. */
- virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
-
- /* Runs a single iteration of the processing loop.
- * Nominally reads and processes one incoming message from the EventHub.
- *
- * This method should be called on the input reader thread.
- */
- virtual void loopOnce() = 0;
-
- /* Gets information about all input devices.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0;
-
- /* Query current input state. */
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) = 0;
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw) = 0;
-
- /* Toggle Caps Lock */
- virtual void toggleCapsLockState(int32_t deviceId) = 0;
-
- /* Determine whether physical keys exist for the given framework-domain key codes. */
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
-
- /* Requests that a reconfiguration of all input devices.
- * The changes flag is a bitfield that indicates what has changed and whether
- * the input devices must all be reopened. */
- 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 cancelVibrate(int32_t deviceId, int32_t token) = 0;
-};
struct StylusState {
/* Time the stylus event was received. */
@@ -384,12 +90,14 @@
virtual void requestTimeoutAtTime(nsecs_t when) = 0;
virtual int32_t bumpGeneration() = 0;
- virtual void getExternalStylusDevices(Vector<InputDeviceInfo>& outDevices) = 0;
+ virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
virtual InputReaderPolicyInterface* getPolicy() = 0;
virtual InputListenerInterface* getListener() = 0;
virtual EventHubInterface* getEventHub() = 0;
+
+ virtual uint32_t getNextSequenceNum() = 0;
};
@@ -416,7 +124,7 @@
virtual void loopOnce();
- virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices);
+ virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices);
virtual bool isInputDeviceEnabled(int32_t deviceId);
@@ -438,6 +146,7 @@
ssize_t repeat, int32_t token);
virtual void cancelVibrate(int32_t deviceId, int32_t token);
+ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
protected:
// These members are protected so they can be instrumented by test cases.
virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
@@ -457,11 +166,12 @@
virtual void fadePointer();
virtual void requestTimeoutAtTime(nsecs_t when);
virtual int32_t bumpGeneration();
- virtual void getExternalStylusDevices(Vector<InputDeviceInfo>& outDevices);
+ virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices);
virtual void dispatchExternalStylusState(const StylusState& outState);
virtual InputReaderPolicyInterface* getPolicy();
virtual InputListenerInterface* getListener();
virtual EventHubInterface* getEventHub();
+ virtual uint32_t getNextSequenceNum();
} mContext;
friend class ContextImpl;
@@ -477,6 +187,9 @@
InputReaderConfiguration mConfig;
+ // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
+ uint32_t mNextSequenceNum;
+
// The event queue.
static const int EVENT_BUFFER_SIZE = 256;
RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
@@ -498,7 +211,7 @@
int32_t getGlobalMetaStateLocked();
void notifyExternalStylusPresenceChanged();
- void getExternalStylusDevicesLocked(Vector<InputDeviceInfo>& outDevices);
+ void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
void dispatchExternalStylusState(const StylusState& state);
void fadePointerLocked();
@@ -506,7 +219,7 @@
int32_t mGeneration;
int32_t bumpGenerationLocked();
- void getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices);
+ void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
nsecs_t mDisableVirtualKeysTimeout;
void disableVirtualKeysUntilLocked(nsecs_t time);
@@ -528,19 +241,6 @@
};
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(const sp<InputReaderInterface>& reader);
- virtual ~InputReaderThread();
-
-private:
- sp<InputReaderInterface> mReader;
-
- virtual bool threadLoop();
-};
-
-
/* Represents the state of a single input device. */
class InputDevice {
public:
@@ -552,18 +252,21 @@
inline int32_t getId() const { return mId; }
inline int32_t getControllerNumber() const { return mControllerNumber; }
inline int32_t getGeneration() const { return mGeneration; }
- inline const String8& getName() const { return mIdentifier.name; }
- inline const String8& getDescriptor() { return mIdentifier.descriptor; }
+ inline const std::string getName() const { return mIdentifier.name; }
+ inline const std::string getDescriptor() { return mIdentifier.descriptor; }
inline uint32_t getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
inline bool isExternal() { return mIsExternal; }
inline void setExternal(bool external) { mIsExternal = external; }
+ inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+ return mAssociatedDisplayPort;
+ }
inline void setMic(bool hasMic) { mHasMic = hasMic; }
inline bool hasMic() const { return mHasMic; }
- inline bool isIgnored() { return mMappers.isEmpty(); }
+ inline bool isIgnored() { return mMappers.empty(); }
bool isEnabled();
void setEnabled(bool enabled, nsecs_t when);
@@ -618,19 +321,21 @@
return value;
}
+ std::optional<int32_t> getAssociatedDisplay();
private:
InputReaderContext* mContext;
int32_t mId;
int32_t mGeneration;
int32_t mControllerNumber;
InputDeviceIdentifier mIdentifier;
- String8 mAlias;
+ std::string mAlias;
uint32_t mClasses;
- Vector<InputMapper*> mMappers;
+ std::vector<InputMapper*> mMappers;
uint32_t mSources;
bool mIsExternal;
+ std::optional<uint8_t> mAssociatedDisplayPort;
bool mHasMic;
bool mDropUntilNextSync;
@@ -773,6 +478,8 @@
RawAbsoluteAxisInfo slot;
RawPointerAxes();
+ inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
+ inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
void clear();
};
@@ -862,6 +569,69 @@
}
};
+/**
+ * Basic statistics information.
+ * Keep track of min, max, average, and standard deviation of the received samples.
+ * Used to report latency information about input events.
+ */
+struct LatencyStatistics {
+ float min;
+ float max;
+ // Sum of all samples
+ float sum;
+ // Sum of squares of all samples
+ float sum2;
+ // The number of samples
+ size_t count;
+ // The last time statistics were reported.
+ nsecs_t lastReportTime;
+
+ LatencyStatistics() {
+ reset(systemTime(SYSTEM_TIME_MONOTONIC));
+ }
+
+ inline void addValue(float x) {
+ if (x < min) {
+ min = x;
+ }
+ if (x > max) {
+ max = x;
+ }
+ sum += x;
+ sum2 += x * x;
+ count++;
+ }
+
+ // Get the average value. Should not be called if no samples have been added.
+ inline float mean() {
+ if (count == 0) {
+ return 0;
+ }
+ return sum / count;
+ }
+
+ // Get the standard deviation. Should not be called if no samples have been added.
+ inline float stdev() {
+ if (count == 0) {
+ return 0;
+ }
+ float average = mean();
+ return sqrt(sum2 / count - average * average);
+ }
+
+ /**
+ * Reset internal state. The variable 'when' is the time when the data collection started.
+ * Call this to start a new data collection window.
+ */
+ inline void reset(nsecs_t when) {
+ max = 0;
+ min = std::numeric_limits<float>::max();
+ sum = 0;
+ sum2 = 0;
+ count = 0;
+ lastReportTime = when;
+ }
+};
/* Keeps track of the state of single-touch protocol. */
class SingleTouchMotionAccumulator {
@@ -980,7 +750,7 @@
inline InputDevice* getDevice() { return mDevice; }
inline int32_t getDeviceId() { return mDevice->getId(); }
- inline const String8 getDeviceName() { return mDevice->getName(); }
+ inline const std::string getDeviceName() { return mDevice->getName(); }
inline InputReaderContext* getContext() { return mContext; }
inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
inline InputListenerInterface* getListener() { return mContext->getListener(); }
@@ -1010,7 +780,9 @@
virtual void updateExternalStylusState(const StylusState& state);
virtual void fadePointer();
-
+ virtual std::optional<int32_t> getAssociatedDisplay() {
+ return std::nullopt;
+ }
protected:
InputDevice* mDevice;
InputReaderContext* mContext;
@@ -1094,6 +866,9 @@
virtual void updateMetaState(int32_t keyCode);
private:
+ // The current viewport.
+ std::optional<DisplayViewport> mViewport;
+
struct KeyDown {
int32_t keyCode;
int32_t scanCode;
@@ -1102,9 +877,7 @@
uint32_t mSource;
int32_t mKeyboardType;
- int32_t mOrientation; // orientation for dpad keys
-
- Vector<KeyDown> mKeyDowns; // keys that are down
+ std::vector<KeyDown> mKeyDowns; // keys that are down
int32_t mMetaState;
nsecs_t mDownTime; // time of most recent key down
@@ -1120,7 +893,6 @@
// Immutable configuration parameters.
struct Parameters {
- bool hasAssociatedDisplay;
bool orientationAware;
bool handlesKeyRepeat;
} mParameters;
@@ -1128,6 +900,9 @@
void configureParameters();
void dumpParameters(std::string& dump);
+ int32_t getOrientation();
+ int32_t getDisplayId();
+
bool isKeyboardOrGamepadKey(int32_t scanCode);
bool isMediaKey(int32_t keyCode);
@@ -1161,6 +936,7 @@
virtual void fadePointer();
+ virtual std::optional<int32_t> getAssociatedDisplay();
private:
// Amount that trackball needs to move in order to generate a key event.
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -1254,7 +1030,7 @@
virtual void cancelTouch(nsecs_t when);
virtual void timeoutExpired(nsecs_t when);
virtual void updateExternalStylusState(const StylusState& state);
-
+ virtual std::optional<int32_t> getAssociatedDisplay();
protected:
CursorButtonAccumulator mCursorButtonAccumulator;
CursorScrollAccumulator mCursorScrollAccumulator;
@@ -1305,7 +1081,7 @@
bool associatedDisplayIsExternal;
bool orientationAware;
bool hasButtonUnderPad;
- String8 uniqueDisplayId;
+ std::string uniqueDisplayId;
enum GestureMode {
GESTURE_MODE_SINGLE_TOUCH,
@@ -1459,7 +1235,7 @@
}
};
- Vector<RawState> mRawStatesPending;
+ std::vector<RawState> mRawStatesPending;
RawState mCurrentRawState;
CookedState mCurrentCookedState;
RawState mLastRawState;
@@ -1486,7 +1262,7 @@
// The pointer controller, or null if the device is not a pointer.
sp<PointerControllerInterface> mPointerController;
- Vector<VirtualKey> mVirtualKeys;
+ std::vector<VirtualKey> mVirtualKeys;
virtual void configureParameters();
virtual void dumpParameters(std::string& dump);
@@ -1803,6 +1579,11 @@
VelocityControl mWheelXVelocityControl;
VelocityControl mWheelYVelocityControl;
+ // Latency statistics for touch events
+ struct LatencyStatistics mStatistics;
+
+ std::optional<DisplayViewport> findViewport();
+
void resetExternalStylus();
void clearStylusDataPendingFlags();
@@ -1870,6 +1651,8 @@
static void assignPointerIds(const RawState* last, RawState* current);
+ void reportEventForStatistics(nsecs_t evdevTime);
+
const char* modeToString(DeviceMode deviceMode);
};
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
new file mode 100644
index 0000000..f48a645
--- /dev/null
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 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 "InputReaderBase"
+
+//#define LOG_NDEBUG 0
+
+#include "InputReaderBase.h"
+
+#include <android/log.h>
+#include <android-base/stringprintf.h>
+
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+#define INDENT4 " "
+#define INDENT5 " "
+
+using android::base::StringPrintf;
+
+namespace android {
+
+// --- InputReaderThread ---
+
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+ Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+ mReader->loopOnce();
+ return true;
+}
+
+// --- InputReaderConfiguration ---
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
+ const std::string& uniqueDisplayId) const {
+ if (uniqueDisplayId.empty()) {
+ ALOGE("Empty string provided to %s", __func__);
+ return std::nullopt;
+ }
+ size_t count = 0;
+ std::optional<DisplayViewport> result = std::nullopt;
+ for (const DisplayViewport& currentViewport : mDisplays) {
+ if (uniqueDisplayId == currentViewport.uniqueId) {
+ result = std::make_optional(currentViewport);
+ count++;
+ }
+ }
+ if (count > 1) {
+ ALOGE("Found %zu viewports with uniqueId %s, but expected 1 at most",
+ count, uniqueDisplayId.c_str());
+ }
+ return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByType(ViewportType type)
+ const {
+ size_t count = 0;
+ std::optional<DisplayViewport> result = std::nullopt;
+ for (const DisplayViewport& currentViewport : mDisplays) {
+ // Return the first match
+ if (currentViewport.type == type && !result) {
+ result = std::make_optional(currentViewport);
+ count++;
+ }
+ }
+ if (count > 1) {
+ ALOGE("Found %zu viewports with type %s, but expected 1 at most",
+ count, viewportTypeToString(type));
+ }
+ return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByPort(
+ uint8_t displayPort) const {
+ for (const DisplayViewport& currentViewport : mDisplays) {
+ const std::optional<uint8_t>& physicalPort = currentViewport.physicalPort;
+ if (physicalPort && (*physicalPort == displayPort)) {
+ return std::make_optional(currentViewport);
+ }
+ }
+ return std::nullopt;
+}
+
+void InputReaderConfiguration::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
+ mDisplays = viewports;
+}
+
+void InputReaderConfiguration::dump(std::string& dump) const {
+ for (const DisplayViewport& viewport : mDisplays) {
+ dumpViewport(dump, viewport);
+ }
+}
+
+void InputReaderConfiguration::dumpViewport(std::string& dump, const DisplayViewport& viewport)
+ const {
+ dump += StringPrintf(INDENT4 "%s\n", viewport.toString().c_str());
+}
+
+
+// -- TouchAffineTransformation --
+void TouchAffineTransformation::applyTo(float& x, float& y) const {
+ float newX, newY;
+ newX = x * x_scale + y * x_ymix + x_offset;
+ newY = x * y_xmix + y * y_scale + y_offset;
+
+ x = newX;
+ y = newY;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/inputflinger/InputReaderFactory.cpp
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/inputflinger/InputReaderFactory.cpp
index e6ac6bf..3534f6b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/inputflinger/InputReaderFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "InputReaderFactory.h"
+#include "InputReader.h"
namespace android {
-namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+sp<InputReaderInterface> createInputReader(
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputListenerInterface>& listener) {
+ return new InputReader(new EventHub(), policy, listener);
+}
-} // namespace mock
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/InputReporter.cpp b/services/inputflinger/InputReporter.cpp
new file mode 100644
index 0000000..8d3153c
--- /dev/null
+++ b/services/inputflinger/InputReporter.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "InputReporterInterface.h"
+
+namespace android {
+
+// --- InputReporter ---
+
+class InputReporter : public InputReporterInterface {
+public:
+ void reportUnhandledKey(uint32_t sequenceNum) override;
+ void reportDroppedKey(uint32_t sequenceNum) override;
+};
+
+void InputReporter::reportUnhandledKey(uint32_t sequenceNum) {
+ // do nothing
+}
+
+void InputReporter::reportDroppedKey(uint32_t sequenceNum) {
+ // do nothing
+}
+
+sp<InputReporterInterface> createInputReporter() {
+ return new InputReporter();
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
deleted file mode 100644
index 3ae7972..0000000
--- a/services/inputflinger/InputWindow.cpp
+++ /dev/null
@@ -1,83 +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 "InputWindow"
-#define LOG_NDEBUG 0
-
-#include "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);
-}
-
-bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
- return touchableRegion.contains(x,y);
-}
-
-bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frameLeft && x < frameRight
- && 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;
-}
-
-bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
- return frameLeft < other->frameRight && frameRight > other->frameLeft
- && frameTop < other->frameBottom && frameBottom > other->frameTop;
-}
-
-
-// --- InputWindowHandle ---
-
-InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
- inputApplicationHandle(inputApplicationHandle), mInfo(NULL) {
-}
-
-InputWindowHandle::~InputWindowHandle() {
- delete mInfo;
-}
-
-void InputWindowHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = NULL;
- }
-}
-
-} // namespace android
diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/TouchVideoDevice.cpp
new file mode 100644
index 0000000..19c1313
--- /dev/null
+++ b/services/inputflinger/TouchVideoDevice.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 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 "TouchVideoDevice.h"
+
+#define LOG_TAG "TouchVideoDevice"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+
+TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
+ uint32_t height, uint32_t width,
+ const std::array<const int16_t*, NUM_BUFFERS>& readLocations) :
+ mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)),
+ mHeight(height), mWidth(width),
+ mReadLocations(readLocations) {
+ mFrames.reserve(MAX_QUEUE_SIZE);
+};
+
+std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePath) {
+ unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK));
+ if (fd.get() == INVALID_FD) {
+ ALOGE("Could not open video device %s: %s", devicePath.c_str(), strerror(errno));
+ return nullptr;
+ }
+
+ struct v4l2_capability cap;
+ int result = ioctl(fd.get(), VIDIOC_QUERYCAP, &cap);
+ if (result == -1) {
+ ALOGE("VIDIOC_QUERYCAP failed: %s", strerror(errno));
+ return nullptr;
+ }
+ if (!(cap.capabilities & V4L2_CAP_TOUCH)) {
+ ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. "
+ "Make sure device specifies V4L2_CAP_TOUCH");
+ return nullptr;
+ }
+ ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i",
+ cap.driver, cap.card, cap.bus_info, cap.version);
+ std::string name = reinterpret_cast<const char*>(cap.card);
+
+ struct v4l2_input v4l2_input_struct;
+ v4l2_input_struct.index = 0;
+ result = ioctl(fd.get(), VIDIOC_ENUMINPUT, &v4l2_input_struct);
+ if (result == -1) {
+ ALOGE("VIDIOC_ENUMINPUT failed: %s", strerror(errno));
+ return nullptr;
+ }
+
+ if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) {
+ ALOGE("Video device does not provide touch data. "
+ "Make sure device specifies V4L2_INPUT_TYPE_TOUCH.");
+ return nullptr;
+ }
+
+ struct v4l2_format v4l2_fmt;
+ v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ result = ioctl(fd.get(), VIDIOC_G_FMT, &v4l2_fmt);
+ if (result == -1) {
+ ALOGE("VIDIOC_G_FMT failed: %s", strerror(errno));
+ return nullptr;
+ }
+ const uint32_t height = v4l2_fmt.fmt.pix.height;
+ const uint32_t width = v4l2_fmt.fmt.pix.width;
+ ALOGI("Frame dimensions: height = %" PRIu32 " width = %" PRIu32, height, width);
+
+ struct v4l2_requestbuffers req = {};
+ req.count = NUM_BUFFERS;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+ // req.reserved is zeroed during initialization, which is required per v4l docs
+ result = ioctl(fd.get(), VIDIOC_REQBUFS, &req);
+ if (result == -1) {
+ ALOGE("VIDIOC_REQBUFS failed: %s", strerror(errno));
+ return nullptr;
+ }
+ if (req.count != NUM_BUFFERS) {
+ ALOGE("Requested %zu buffers, but driver responded with count=%i", NUM_BUFFERS, req.count);
+ return nullptr;
+ }
+
+ struct v4l2_buffer buf = {};
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ // buf.reserved and buf.reserved2 are zeroed during initialization, required per v4l docs
+ std::array<const int16_t*, NUM_BUFFERS> readLocations;
+ for (size_t i = 0; i < NUM_BUFFERS; i++) {
+ buf.index = i;
+ result = ioctl(fd.get(), VIDIOC_QUERYBUF, &buf);
+ if (result == -1) {
+ ALOGE("VIDIOC_QUERYBUF failed: %s", strerror(errno));
+ return nullptr;
+ }
+ if (buf.length != height * width * sizeof(int16_t)) {
+ ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")",
+ buf.length, buf.m.offset);
+ return nullptr;
+ }
+
+ readLocations[i] = static_cast<const int16_t*>(mmap(nullptr /* start anywhere */,
+ buf.length, PROT_READ /* required */, MAP_SHARED /* recommended */,
+ fd.get(), buf.m.offset));
+ if (readLocations[i] == MAP_FAILED) {
+ ALOGE("%s: map failed: %s", __func__, strerror(errno));
+ return nullptr;
+ }
+ }
+
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ result = ioctl(fd.get(), VIDIOC_STREAMON, &type);
+ if (result == -1) {
+ ALOGE("VIDIOC_STREAMON failed: %s", strerror(errno));
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < NUM_BUFFERS; i++) {
+ buf.index = i;
+ result = ioctl(fd.get(), VIDIOC_QBUF, &buf);
+ if (result == -1) {
+ ALOGE("VIDIOC_QBUF failed for buffer %zu: %s", i, strerror(errno));
+ return nullptr;
+ }
+ }
+ // Using 'new' to access a non-public constructor.
+ return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(
+ fd.release(), std::move(name), std::move(devicePath), height, width, readLocations));
+}
+
+size_t TouchVideoDevice::readAndQueueFrames() {
+ std::vector<TouchVideoFrame> frames = readFrames();
+ const size_t numFrames = frames.size();
+ if (numFrames == 0) {
+ // Likely an error occurred
+ return 0;
+ }
+ // Concatenate the vectors, then clip up to maximum size allowed
+ mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
+ std::make_move_iterator(frames.end()));
+ if (mFrames.size() > MAX_QUEUE_SIZE) {
+ ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
+ mFrames.size() - MAX_QUEUE_SIZE);
+ mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
+ }
+ return numFrames;
+}
+
+std::vector<TouchVideoFrame> TouchVideoDevice::consumeFrames() {
+ std::vector<TouchVideoFrame> frames = std::move(mFrames);
+ mFrames = {};
+ return frames;
+}
+
+std::optional<TouchVideoFrame> TouchVideoDevice::readFrame() {
+ struct v4l2_buffer buf = {};
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ int result = ioctl(mFd.get(), VIDIOC_DQBUF, &buf);
+ if (result == -1) {
+ // EAGAIN means we've reached the end of the read buffer, so it's expected.
+ if (errno != EAGAIN) {
+ ALOGE("VIDIOC_DQBUF failed: %s", strerror(errno));
+ }
+ return std::nullopt;
+ }
+ if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
+ // We use CLOCK_MONOTONIC for input events, so if the clocks don't match,
+ // we can't compare timestamps. Just log a warning, since this is a driver issue
+ ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC",
+ buf.timestamp.tv_sec, buf.timestamp.tv_usec);
+ }
+ std::vector<int16_t> data(mHeight * mWidth);
+ const int16_t* readFrom = mReadLocations[buf.index];
+ std::copy(readFrom, readFrom + mHeight * mWidth, data.begin());
+ TouchVideoFrame frame(mHeight, mWidth, std::move(data), buf.timestamp);
+
+ result = ioctl(mFd.get(), VIDIOC_QBUF, &buf);
+ if (result == -1) {
+ ALOGE("VIDIOC_QBUF failed: %s", strerror(errno));
+ }
+ return std::make_optional(std::move(frame));
+}
+
+/*
+ * This function should not be called unless buffer is ready! This must be checked with
+ * select, poll, epoll, or some other similar api first.
+ * The oldest frame will be at the beginning of the array.
+ */
+std::vector<TouchVideoFrame> TouchVideoDevice::readFrames() {
+ std::vector<TouchVideoFrame> frames;
+ while (true) {
+ std::optional<TouchVideoFrame> frame = readFrame();
+ if (!frame) {
+ break;
+ }
+ frames.push_back(std::move(*frame));
+ }
+ return frames;
+}
+
+TouchVideoDevice::~TouchVideoDevice() {
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int result = ioctl(mFd.get(), VIDIOC_STREAMOFF, &type);
+ if (result == -1) {
+ ALOGE("VIDIOC_STREAMOFF failed: %s", strerror(errno));
+ }
+ for (const int16_t* buffer : mReadLocations) {
+ void* bufferAddress = static_cast<void*>(const_cast<int16_t*>(buffer));
+ result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t));
+ if (result == -1) {
+ ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno));
+ }
+ }
+}
+
+std::string TouchVideoDevice::dump() const {
+ return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32
+ ", fd=%i, hasValidFd=%s",
+ mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(),
+ hasValidFd() ? "true" : "false");
+}
+
+} // namespace android
diff --git a/services/inputflinger/TouchVideoDevice.h b/services/inputflinger/TouchVideoDevice.h
new file mode 100644
index 0000000..0e7e2ef
--- /dev/null
+++ b/services/inputflinger/TouchVideoDevice.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 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 _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+
+#include <array>
+#include <android-base/unique_fd.h>
+#include <input/TouchVideoFrame.h>
+#include <optional>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace android {
+
+/**
+ * Represents a video device that uses v4l2 api to report touch heatmap data.
+ */
+class TouchVideoDevice {
+public:
+ /**
+ * Create a new TouchVideoDevice for the path provided.
+ * Return nullptr upon failure.
+ */
+ static std::unique_ptr<TouchVideoDevice> create(std::string devicePath);
+ ~TouchVideoDevice();
+
+ bool hasValidFd() const { return mFd.get() != INVALID_FD; }
+ /**
+ * Obtain the file descriptor associated with this video device.
+ * Could be used for adding to epoll.
+ */
+ int getFd() const { return mFd.get(); }
+ /**
+ * Get the name of this video device.
+ */
+ const std::string& getName() const { return mName; }
+ /**
+ * Get the file path of this video device.
+ */
+ const std::string& getPath() const { return mPath; }
+ /**
+ * Get the height of the heatmap frame
+ */
+ uint32_t getHeight() const { return mHeight; }
+ /**
+ * Get the width of the heatmap frame
+ */
+ uint32_t getWidth() const { return mWidth; }
+ /**
+ * Direct read of the frame. Stores the frame into internal buffer.
+ * Return the number of frames that were successfully read.
+ *
+ * This function should not be called unless buffer is ready!
+ * This must be checked with select, poll, epoll, or similar api first.
+ * If epoll indicates that there is data ready to read, but this function
+ * returns zero, then it is likely an error occurred.
+ */
+ size_t readAndQueueFrames();
+ /**
+ * Return all of the queued frames, and erase them from the local buffer.
+ * The returned frames are in the order that they were received from the
+ * v4l2 device, with the oldest frame at the index 0.
+ */
+ std::vector<TouchVideoFrame> consumeFrames();
+ /**
+ * Get string representation of this video device.
+ */
+ std::string dump() const;
+
+private:
+ android::base::unique_fd mFd;
+ std::string mName;
+ std::string mPath;
+
+ uint32_t mHeight;
+ uint32_t mWidth;
+
+ static constexpr int INVALID_FD = -1;
+ /**
+ * How many buffers to request for heatmap.
+ * The kernel driver will be allocating these buffers for us,
+ * and will provide memory locations to read these from.
+ */
+ static constexpr size_t NUM_BUFFERS = 3;
+ std::array<const int16_t*, NUM_BUFFERS> mReadLocations;
+ /**
+ * 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;
+ std::vector<TouchVideoFrame> mFrames;
+
+ /**
+ * The constructor is private because opening a v4l2 device requires many checks.
+ * To get a new TouchVideoDevice, use 'create' instead.
+ */
+ explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath,
+ uint32_t height, uint32_t width,
+ const std::array<const int16_t*, NUM_BUFFERS>& readLocations);
+ /**
+ * Read all currently available frames.
+ */
+ std::vector<TouchVideoFrame> readFrames();
+ /**
+ * Read a single frame. May return nullopt if no data is currently available for reading.
+ */
+ std::optional<TouchVideoFrame> readFrame();
+};
+} // namespace android
+#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 775dbdc..cbe0190 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -30,6 +30,9 @@
"libutils",
"libhardware",
],
+ static_libs: [
+ "libarect",
+ ],
cflags: [
"-Wall",
@@ -54,8 +57,9 @@
shared_libs: [
"libbinder",
"libinputflingerhost",
- "libutils",
+ "libutils"
],
-
- init_rc: ["inputflinger.rc"],
+ static_libs: [
+ "libarect",
+ ],
}
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index bd11d56..2f046c3 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -217,18 +217,18 @@
idi.product = id->productId;
idi.version = id->version;
- String8 configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
+ std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
- if (configFile.isEmpty()) {
+ if (configFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.",
- idi.name.string());
+ idi.name.c_str());
} else {
auto propMap = new input_property_map_t();
- status_t status = PropertyMap::load(configFile, &propMap->propertyMap);
+ status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
- idi.name.string());
+ idi.name.c_str());
delete propMap;
return nullptr;
}
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 39e69e5..d8b352c 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -24,6 +24,7 @@
#include <cutils/compiler.h>
#include <input/IInputFlinger.h>
+#include <input/ISetInputWindowsListener.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/StrongPointer.h>
@@ -39,6 +40,11 @@
InputFlinger() ANDROID_API;
virtual status_t dump(int fd, const Vector<String16>& args);
+ void setInputWindows(const std::vector<InputWindowInfo>&,
+ const sp<ISetInputWindowsListener>&) {}
+ void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {}
+ void registerInputChannel(const sp<InputChannel>&) {}
+ void unregisterInputChannel(const sp<InputChannel>&) {}
private:
virtual ~InputFlinger();
diff --git a/services/inputflinger/InputListener.h b/services/inputflinger/include/InputListener.h
similarity index 75%
rename from services/inputflinger/InputListener.h
rename to services/inputflinger/include/InputListener.h
index 77afb34..b51dcb6 100644
--- a/services/inputflinger/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -17,7 +17,10 @@
#ifndef _UI_INPUT_LISTENER_H
#define _UI_INPUT_LISTENER_H
+#include <vector>
+
#include <input/Input.h>
+#include <input/TouchVideoFrame.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
@@ -28,6 +31,14 @@
/* Superclass of all input event argument objects */
struct NotifyArgs {
+ uint32_t sequenceNum;
+ nsecs_t eventTime;
+
+ inline NotifyArgs() : sequenceNum(0), eventTime(0) { }
+
+ inline explicit NotifyArgs(uint32_t sequenceNum, nsecs_t eventTime) :
+ sequenceNum(sequenceNum), eventTime(eventTime) { }
+
virtual ~NotifyArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
@@ -36,11 +47,12 @@
/* Describes a configuration change event. */
struct NotifyConfigurationChangedArgs : public NotifyArgs {
- nsecs_t eventTime;
inline NotifyConfigurationChangedArgs() { }
- explicit NotifyConfigurationChangedArgs(nsecs_t eventTime);
+ bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
+
+ NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime);
NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
@@ -52,9 +64,9 @@
/* Describes a key event. */
struct NotifyKeyArgs : public NotifyArgs {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
uint32_t policyFlags;
int32_t action;
int32_t flags;
@@ -65,9 +77,11 @@
inline NotifyKeyArgs() { }
- NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
- int32_t metaState, nsecs_t downTime);
+ NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, nsecs_t downTime);
+
+ bool operator==(const NotifyKeyArgs& rhs) const;
NotifyKeyArgs(const NotifyKeyArgs& other);
@@ -79,17 +93,20 @@
/* Describes a motion event. */
struct NotifyMotionArgs : public NotifyArgs {
- nsecs_t eventTime;
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
uint32_t policyFlags;
int32_t action;
int32_t actionButton;
int32_t flags;
int32_t metaState;
int32_t buttonState;
+ /**
+ * Classification of the current touch gesture
+ */
+ MotionClassification classification;
int32_t edgeFlags;
- int32_t displayId;
/**
* A timestamp in the input device's time base, not the platform's.
* The units are microseconds since the last reset.
@@ -103,38 +120,44 @@
float xPrecision;
float yPrecision;
nsecs_t downTime;
+ std::vector<TouchVideoFrame> videoFrames;
inline NotifyMotionArgs() { }
- NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
+ NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags,
int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp, uint32_t pointerCount,
+ int32_t metaState, int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, nsecs_t downTime);
+ float xPrecision, float yPrecision, nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames);
NotifyMotionArgs(const NotifyMotionArgs& other);
virtual ~NotifyMotionArgs() { }
+ bool operator==(const NotifyMotionArgs& rhs) const;
+
virtual void notify(const sp<InputListenerInterface>& listener) const;
};
/* Describes a switch event. */
struct NotifySwitchArgs : public NotifyArgs {
- nsecs_t eventTime;
uint32_t policyFlags;
uint32_t switchValues;
uint32_t switchMask;
inline NotifySwitchArgs() { }
- NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
+ NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
uint32_t switchValues, uint32_t switchMask);
NotifySwitchArgs(const NotifySwitchArgs& other);
+ bool operator==(const NotifySwitchArgs rhs) const;
+
virtual ~NotifySwitchArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
@@ -144,15 +167,16 @@
/* Describes a device reset event, such as when a device is added,
* reconfigured, or removed. */
struct NotifyDeviceResetArgs : public NotifyArgs {
- nsecs_t eventTime;
int32_t deviceId;
inline NotifyDeviceResetArgs() { }
- NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId);
+ NotifyDeviceResetArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
+ bool operator==(const NotifyDeviceResetArgs& rhs) const;
+
virtual ~NotifyDeviceResetArgs() { }
virtual void notify(const sp<InputListenerInterface>& listener) const;
@@ -197,7 +221,7 @@
private:
sp<InputListenerInterface> mInnerListener;
- Vector<NotifyArgs*> mArgsQueue;
+ std::vector<NotifyArgs*> mArgsQueue;
};
} // namespace android
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
new file mode 100644
index 0000000..8ad5dd0
--- /dev/null
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2018 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_READER_BASE_H
+#define _UI_INPUT_READER_BASE_H
+
+#include "PointerControllerInterface.h"
+
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/DisplayViewport.h>
+#include <input/VelocityControl.h>
+#include <input/VelocityTracker.h>
+#include <utils/KeyedVector.h>
+#include <utils/Thread.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+
+#include <optional>
+#include <stddef.h>
+#include <unistd.h>
+#include <unordered_map>
+#include <vector>
+
+// Maximum supported size of a vibration pattern.
+// Must be at least 2.
+#define MAX_VIBRATE_PATTERN_SIZE 100
+
+// Maximum allowable delay value in a vibration pattern before
+// which the delay will be truncated.
+#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
+
+namespace android {
+
+/* Processes raw input events and sends cooked event data to an input listener. */
+class InputReaderInterface : public virtual RefBase {
+protected:
+ InputReaderInterface() { }
+ virtual ~InputReaderInterface() { }
+
+public:
+ /* Dumps the state of the input reader.
+ *
+ * This method may be called on any thread (usually by the input manager). */
+ virtual void dump(std::string& dump) = 0;
+
+ /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+ virtual void monitor() = 0;
+
+ /* Returns true if the input device is enabled. */
+ virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
+
+ /* Runs a single iteration of the processing loop.
+ * Nominally reads and processes one incoming message from the EventHub.
+ *
+ * This method should be called on the input reader thread.
+ */
+ virtual void loopOnce() = 0;
+
+ /* Gets information about all input devices.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) = 0;
+
+ /* Query current input state. */
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw) = 0;
+
+ /* Toggle Caps Lock */
+ virtual void toggleCapsLockState(int32_t deviceId) = 0;
+
+ /* Determine whether physical keys exist for the given framework-domain key codes. */
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+
+ /* Requests that a reconfiguration of all input devices.
+ * The changes flag is a bitfield that indicates what has changed and whether
+ * the input devices must all be reopened. */
+ 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 cancelVibrate(int32_t deviceId, int32_t token) = 0;
+
+ /* Return true if the device can send input events to the specified display. */
+ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+};
+
+/* Reads raw events from the event hub and processes them, endlessly. */
+class InputReaderThread : public Thread {
+public:
+ explicit InputReaderThread(const sp<InputReaderInterface>& reader);
+ virtual ~InputReaderThread();
+
+private:
+ sp<InputReaderInterface> mReader;
+
+ virtual bool threadLoop();
+};
+
+/*
+ * Input reader configuration.
+ *
+ * Specifies various options that modify the behavior of the input reader.
+ */
+struct InputReaderConfiguration {
+ // Describes changes that have occurred.
+ enum {
+ // The pointer speed changed.
+ CHANGE_POINTER_SPEED = 1 << 0,
+
+ // The pointer gesture control changed.
+ CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
+
+ // The display size or orientation changed.
+ CHANGE_DISPLAY_INFO = 1 << 2,
+
+ // The visible touches option changed.
+ CHANGE_SHOW_TOUCHES = 1 << 3,
+
+ // The keyboard layouts must be reloaded.
+ CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
+
+ // The device name alias supplied by the may have changed for some devices.
+ CHANGE_DEVICE_ALIAS = 1 << 5,
+
+ // The location calibration matrix changed.
+ CHANGE_TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
+
+ // The presence of an external stylus has changed.
+ CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
+
+ // The pointer capture mode has changed.
+ CHANGE_POINTER_CAPTURE = 1 << 8,
+
+ // The set of disabled input devices (disabledDevices) has changed.
+ CHANGE_ENABLED_STATE = 1 << 9,
+
+ // All devices must be reopened.
+ CHANGE_MUST_REOPEN = 1 << 31,
+ };
+
+ // Gets the amount of time to disable virtual keys after the screen is touched
+ // in order to filter out accidental virtual key presses due to swiping gestures
+ // or taps near the edge of the display. May be 0 to disable the feature.
+ nsecs_t virtualKeyQuietTime;
+
+ // The excluded device names for the platform.
+ // Devices with these names will be ignored.
+ std::vector<std::string> excludedDeviceNames;
+
+ // The associations between input ports and display ports.
+ // Used to determine which DisplayViewport should be tied to which InputDevice.
+ std::unordered_map<std::string, uint8_t> portAssociations;
+
+ // Velocity control parameters for mouse pointer movements.
+ VelocityControlParameters pointerVelocityControlParameters;
+
+ // Velocity control parameters for mouse wheel movements.
+ VelocityControlParameters wheelVelocityControlParameters;
+
+ // True if pointer gestures are enabled.
+ bool pointerGesturesEnabled;
+
+ // Quiet time between certain pointer gesture transitions.
+ // Time to allow for all fingers or buttons to settle into a stable state before
+ // starting a new gesture.
+ nsecs_t pointerGestureQuietInterval;
+
+ // The minimum speed that a pointer must travel for us to consider switching the active
+ // touch pointer to it during a drag. This threshold is set to avoid switching due
+ // to noise from a finger resting on the touch pad (perhaps just pressing it down).
+ float pointerGestureDragMinSwitchSpeed; // in pixels per second
+
+ // Tap gesture delay time.
+ // The time between down and up must be less than this to be considered a tap.
+ nsecs_t pointerGestureTapInterval;
+
+ // Tap drag gesture delay time.
+ // The time between the previous tap's up and the next down must be less than
+ // this to be considered a drag. Otherwise, the previous tap is finished and a
+ // new tap begins.
+ //
+ // Note that the previous tap will be held down for this entire duration so this
+ // interval must be shorter than the long press timeout.
+ nsecs_t pointerGestureTapDragInterval;
+
+ // The distance in pixels that the pointer is allowed to move from initial down
+ // to up and still be called a tap.
+ float pointerGestureTapSlop; // in pixels
+
+ // Time after the first touch points go down to settle on an initial centroid.
+ // This is intended to be enough time to handle cases where the user puts down two
+ // fingers at almost but not quite exactly the same time.
+ nsecs_t pointerGestureMultitouchSettleInterval;
+
+ // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
+ // at least two pointers have moved at least this far from their starting place.
+ float pointerGestureMultitouchMinDistance; // in pixels
+
+ // The transition from PRESS to SWIPE gesture mode can only occur when the
+ // cosine of the angle between the two vectors is greater than or equal to than this value
+ // which indicates that the vectors are oriented in the same direction.
+ // When the vectors are oriented in the exactly same direction, the cosine is 1.0.
+ // (In exactly opposite directions, the cosine is -1.0.)
+ float pointerGestureSwipeTransitionAngleCosine;
+
+ // The transition from PRESS to SWIPE gesture mode can only occur when the
+ // fingers are no more than this far apart relative to the diagonal size of
+ // the touch pad. For example, a ratio of 0.5 means that the fingers must be
+ // no more than half the diagonal size of the touch pad apart.
+ float pointerGestureSwipeMaxWidthRatio;
+
+ // The gesture movement speed factor relative to the size of the display.
+ // Movement speed applies when the fingers are moving in the same direction.
+ // Without acceleration, a full swipe of the touch pad diagonal in movement mode
+ // will cover this portion of the display diagonal.
+ float pointerGestureMovementSpeedRatio;
+
+ // The gesture zoom speed factor relative to the size of the display.
+ // Zoom speed applies when the fingers are mostly moving relative to each other
+ // to execute a scale gesture or similar.
+ // Without acceleration, a full swipe of the touch pad diagonal in zoom mode
+ // will cover this portion of the display diagonal.
+ float pointerGestureZoomSpeedRatio;
+
+ // True to show the location of touches on the touch screen as spots.
+ bool showTouches;
+
+ // True if pointer capture is enabled.
+ bool pointerCapture;
+
+ // The set of currently disabled input devices.
+ SortedVector<int32_t> disabledDevices;
+
+ InputReaderConfiguration() :
+ virtualKeyQuietTime(0),
+ pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
+ wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
+ pointerGesturesEnabled(true),
+ pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
+ pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
+ pointerGestureTapInterval(150 * 1000000LL), // 150 ms
+ pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
+ pointerGestureTapSlop(10.0f), // 10 pixels
+ pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
+ pointerGestureMultitouchMinDistance(15), // 15 pixels
+ pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+ pointerGestureSwipeMaxWidthRatio(0.25f),
+ pointerGestureMovementSpeedRatio(0.8f),
+ pointerGestureZoomSpeedRatio(0.3f),
+ showTouches(false), pointerCapture(false) { }
+
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
+ const;
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
+ void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
+
+
+ void dump(std::string& dump) const;
+ void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
+
+private:
+ std::vector<DisplayViewport> mDisplays;
+};
+
+struct TouchAffineTransformation {
+ float x_scale;
+ float x_ymix;
+ float x_offset;
+ float y_xmix;
+ float y_scale;
+ float y_offset;
+
+ TouchAffineTransformation() :
+ x_scale(1.0f), x_ymix(0.0f), x_offset(0.0f),
+ y_xmix(0.0f), y_scale(1.0f), y_offset(0.0f) {
+ }
+
+ TouchAffineTransformation(float xscale, float xymix, float xoffset,
+ float yxmix, float yscale, float yoffset) :
+ x_scale(xscale), x_ymix(xymix), x_offset(xoffset),
+ y_xmix(yxmix), y_scale(yscale), y_offset(yoffset) {
+ }
+
+ void applyTo(float& x, float& y) const;
+};
+
+/*
+ * Input reader policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI. This interface is also mocked in the unit tests.
+ *
+ * These methods must NOT re-enter the input reader since they may be called while
+ * holding the input reader lock.
+ */
+class InputReaderPolicyInterface : public virtual RefBase {
+protected:
+ InputReaderPolicyInterface() { }
+ virtual ~InputReaderPolicyInterface() { }
+
+public:
+ /* Gets the input reader configuration. */
+ 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;
+
+ /* Notifies the input reader policy that some input devices have changed
+ * and provides information about all current input devices.
+ */
+ virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
+
+ /* Gets the keyboard layout for a particular input device. */
+ virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier& identifier) = 0;
+
+ /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
+ virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
+
+ /* Gets the affine calibration associated with the specified device. */
+ virtual TouchAffineTransformation getTouchAffineTransformation(
+ const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_READER_COMMON_H
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/inputflinger/include/InputReaderFactory.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/inputflinger/include/InputReaderFactory.h
index e6ac6bf..9db6233 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/inputflinger/include/InputReaderFactory.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <utils/StrongPointer.h>
namespace android {
-namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+class InputReaderInterface;
+class InputReaderPolicyInterface;
+class InputListenerInterface;
-} // namespace mock
+sp<InputReaderInterface> createInputReader(
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputListenerInterface>& listener);
+
} // namespace android
diff --git a/services/inputflinger/include/InputReporterInterface.h b/services/inputflinger/include/InputReporterInterface.h
new file mode 100644
index 0000000..906d7f2
--- /dev/null
+++ b/services/inputflinger/include/InputReporterInterface.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_INPUT_REPORTER_INTERFACE_H
+#define _UI_INPUT_REPORTER_INTERFACE_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/*
+ * The interface used by the InputDispatcher to report information about input events after
+ * it is sent to the application, such as if a key is unhandled or dropped.
+ */
+class InputReporterInterface : public virtual RefBase {
+protected:
+ virtual ~InputReporterInterface() { }
+
+public:
+ // Report a key that was not handled by the system or apps.
+ // A key event is unhandled if:
+ // - The event was not handled and there is no fallback key; or
+ // - The event was not handled and it has a fallback key,
+ // but the fallback key was not handled.
+ virtual void reportUnhandledKey(uint32_t sequenceNum) = 0;
+
+ // Report a key that was dropped by InputDispatcher.
+ // A key can be dropped for several reasons. See the enum
+ // InputDispatcher::DropReason for details.
+ virtual void reportDroppedKey(uint32_t sequenceNum) = 0;
+};
+
+/*
+ * Factory method for InputReporter.
+ */
+sp<InputReporterInterface> createInputReporter();
+
+} // namespace android
+
+#endif // _UI_INPUT_REPORTER_INTERFACE_H
diff --git a/services/inputflinger/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
similarity index 95%
rename from services/inputflinger/PointerControllerInterface.h
rename to services/inputflinger/include/PointerControllerInterface.h
index e94dd94..0ff28e4 100644
--- a/services/inputflinger/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -94,10 +94,13 @@
* pressed (not hovering).
*/
virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits) = 0;
+ BitSet32 spotIdBits, int32_t displayId) = 0;
/* Removes all spots. */
virtual void clearSpots() = 0;
+
+ /* Gets the id of the display where the pointer should be shown. */
+ virtual int32_t getDisplayId() const = 0;
};
} // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index afaf139..1835449 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -3,16 +3,22 @@
cc_test {
name: "inputflinger_tests",
srcs: [
- "InputReader_test.cpp",
+ "BlockingQueue_test.cpp",
+ "TestInputListener.cpp",
+ "InputClassifier_test.cpp",
"InputDispatcher_test.cpp",
+ "InputReader_test.cpp",
],
cflags: [
"-Wall",
"-Werror",
+ "-Wextra",
"-Wno-unused-parameter",
],
shared_libs: [
+ "android.hardware.input.classifier@1.0",
"libbase",
+ "libbinder",
"libcutils",
"liblog",
"libutils",
@@ -21,6 +27,8 @@
"libui",
"libinput",
"libinputflinger",
+ "libinputreader",
+ "libinputflinger_base",
"libinputservice",
],
}
diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp
new file mode 100644
index 0000000..0dea8d7
--- /dev/null
+++ b/services/inputflinger/tests/BlockingQueue_test.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "../BlockingQueue.h"
+
+
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+
+// --- BlockingQueueTest ---
+
+/**
+ * Sanity check of basic pop and push operation.
+ */
+TEST(BlockingQueueTest, Queue_AddAndRemove) {
+ constexpr size_t capacity = 10;
+ BlockingQueue<int> queue(capacity);
+
+ ASSERT_TRUE(queue.push(1));
+ ASSERT_EQ(queue.pop(), 1);
+}
+
+/**
+ * Make sure the queue has strict capacity limits.
+ */
+TEST(BlockingQueueTest, Queue_ReachesCapacity) {
+ constexpr size_t capacity = 3;
+ BlockingQueue<int> queue(capacity);
+
+ // First 3 elements should be added successfully
+ ASSERT_TRUE(queue.push(1));
+ ASSERT_TRUE(queue.push(2));
+ ASSERT_TRUE(queue.push(3));
+ ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity;
+}
+
+/**
+ * Make sure the queue maintains FIFO order.
+ * Add elements and remove them, and check the order.
+ */
+TEST(BlockingQueueTest, Queue_isFIFO) {
+ constexpr size_t capacity = 10;
+ BlockingQueue<int> queue(capacity);
+
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_TRUE(queue.push(static_cast<int>(i)));
+ }
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_EQ(queue.pop(), static_cast<int>(i));
+ }
+}
+
+TEST(BlockingQueueTest, Queue_Clears) {
+ constexpr size_t capacity = 2;
+ BlockingQueue<int> queue(capacity);
+
+ queue.push(1);
+ queue.push(2);
+ queue.clear();
+ queue.push(3);
+ // Should no longer receive elements 1 and 2
+ ASSERT_EQ(3, queue.pop());
+}
+
+TEST(BlockingQueueTest, Queue_Erases) {
+ constexpr size_t capacity = 4;
+ BlockingQueue<int> queue(capacity);
+
+ queue.push(1);
+ queue.push(2);
+ queue.push(3);
+ queue.push(4);
+ // Erase elements 2 and 4
+ queue.erase([](int element) { return element == 2 || element == 4; });
+ // Should no longer receive elements 2 and 4
+ ASSERT_EQ(1, queue.pop());
+ ASSERT_EQ(3, queue.pop());
+}
+
+// --- BlockingQueueTest - Multiple threads ---
+
+TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) {
+ constexpr size_t capacity = 100; // large capacity to increase likelihood that threads overlap
+ BlockingQueue<int> queue(capacity);
+
+ // Fill queue from a different thread
+ std::thread fillQueue([&queue](){
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_TRUE(queue.push(static_cast<int>(i)));
+ }
+ });
+
+ // Make sure all elements are received in correct order
+ for (size_t i = 0; i < capacity; i++) {
+ ASSERT_EQ(queue.pop(), static_cast<int>(i));
+ }
+
+ fillQueue.join();
+}
+
+/**
+ * When the queue has no elements, and pop is called, it should block
+ * the current thread until an element is added to the queue (from another thread).
+ * Here we create a separate thread and call pop on an empty queue. Next,
+ * we check that the thread is blocked.
+ */
+TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) {
+ constexpr size_t capacity = 1;
+ BlockingQueue<int> queue(capacity);
+
+ std::atomic_bool hasReceivedElement = false;
+
+ // fill queue from a different thread
+ std::thread waitUntilHasElements([&queue, &hasReceivedElement](){
+ queue.pop(); // This should block until an element has been added
+ hasReceivedElement = true;
+ });
+
+ ASSERT_FALSE(hasReceivedElement);
+ queue.push(1);
+ waitUntilHasElements.join();
+ ASSERT_TRUE(hasReceivedElement);
+}
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
new file mode 100644
index 0000000..1651057
--- /dev/null
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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 "../InputClassifier.h"
+#include <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+
+using namespace android::hardware::input;
+
+namespace android {
+
+// --- InputClassifierTest ---
+
+static NotifyMotionArgs generateBasicMotionArgs() {
+ // Create a basic motion event for testing
+ PointerProperties properties;
+ properties.id = 0;
+ properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
+ static constexpr nsecs_t downTime = 2;
+ NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
+ AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
+ 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
+ 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/,
+ downTime, {}/*videoFrames*/);
+ return motionArgs;
+}
+
+class InputClassifierTest : public testing::Test {
+protected:
+ sp<InputClassifierInterface> mClassifier;
+ sp<TestInputListener> mTestListener;
+
+ virtual void SetUp() override {
+ mTestListener = new TestInputListener();
+ mClassifier = new InputClassifier(mTestListener);
+ }
+
+ virtual void TearDown() override {
+ mClassifier.clear();
+ mTestListener.clear();
+ }
+};
+
+/**
+ * Create a basic configuration change and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) {
+ // Create a basic configuration change and send to classifier
+ NotifyConfigurationChangedArgs args(1/*sequenceNum*/, 2/*eventTime*/);
+
+ mClassifier->notifyConfigurationChanged(&args);
+ NotifyConfigurationChangedArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
+ // Create a basic key event and send to classifier
+ NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
+ ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
+ AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+
+ mClassifier->notifyKey(&args);
+ NotifyKeyArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+
+/**
+ * Create a basic motion event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+ mClassifier->notifyMotion(&motionArgs);
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(motionArgs, args);
+}
+
+/**
+ * Create a basic switch event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) {
+ NotifySwitchArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*policyFlags*/, 4/*switchValues*/,
+ 5/*switchMask*/);
+
+ mClassifier->notifySwitch(&args);
+ NotifySwitchArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+/**
+ * Create a basic device reset event and send it to input classifier.
+ * Expect that the event is received by the next input stage, unmodified.
+ */
+TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) {
+ NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+
+ mClassifier->notifyDeviceReset(&args);
+ NotifyDeviceResetArgs outArgs;
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
+ ASSERT_EQ(args, outArgs);
+}
+
+// --- MotionClassifierTest ---
+
+class MotionClassifierTest : public testing::Test {
+protected:
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
+
+ virtual void SetUp() override {
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (service) {
+ mMotionClassifier = std::make_unique<MotionClassifier>(service);
+ }
+ }
+};
+
+/**
+ * Since MotionClassifier creates a new thread to communicate with HAL,
+ * it's not really expected to ever exit. However, for testing purposes,
+ * we need to ensure that it is able to exit cleanly.
+ * If the thread is not properly cleaned up, it will generate SIGABRT.
+ * The logic for exiting the thread and cleaning up the resources is inside
+ * the destructor. Here, we just make sure the destructor does not crash.
+ */
+TEST_F(MotionClassifierTest, Destructor_DoesNotCrash) {
+ mMotionClassifier = nullptr;
+}
+
+/**
+ * Make sure MotionClassifier can handle events that don't have any
+ * video frames.
+ */
+TEST_F(MotionClassifierTest, Classify_NoVideoFrames) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+ // We are not checking the return value, because we can't be making assumptions
+ // about the HAL operation, since it will be highly hardware-dependent
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+ }
+}
+
+/**
+ * Make sure nothing crashes when a videoFrame is sent.
+ */
+TEST_F(MotionClassifierTest, Classify_OneVideoFrame) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+ std::vector<int16_t> videoData = {1, 2, 3, 4};
+ timeval timestamp = { 1, 1};
+ TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+ motionArgs.videoFrames = {frame};
+
+ // We are not checking the return value, because we can't be making assumptions
+ // about the HAL operation, since it will be highly hardware-dependent
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+ }
+}
+
+/**
+ * Make sure nothing crashes when 2 videoFrames are sent.
+ */
+TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) {
+ NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+
+ std::vector<int16_t> videoData1 = {1, 2, 3, 4};
+ timeval timestamp1 = { 1, 1};
+ TouchVideoFrame frame1(2, 2, std::move(videoData1), timestamp1);
+
+ std::vector<int16_t> videoData2 = {6, 6, 6, 6};
+ timeval timestamp2 = { 1, 2};
+ TouchVideoFrame frame2(2, 2, std::move(videoData2), timestamp2);
+
+ motionArgs.videoFrames = {frame1, frame2};
+
+ // We are not checking the return value, because we can't be making assumptions
+ // about the HAL operation, since it will be highly hardware-dependent
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+ }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when it is reset.
+ */
+TEST_F(MotionClassifierTest, Reset_DoesNotCrash) {
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset());
+ }
+}
+
+/**
+ * Make sure MotionClassifier does not crash when a device is reset.
+ */
+TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) {
+ NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
+ if (mMotionClassifier) {
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args));
+ }
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index aa6df24..d63ff8c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,8 @@
#include "../InputDispatcher.h"
+#include <binder/Binder.h>
+
#include <gtest/gtest.h>
#include <linux/input.h>
@@ -28,7 +30,7 @@
static const int32_t DEVICE_ID = 1;
// An arbitrary display id.
-static const int32_t DISPLAY_ID = 0;
+static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
// An arbitrary injector pid / uid pair that has permission to inject events.
static const int32_t INJECTOR_PID = 999;
@@ -46,41 +48,105 @@
public:
FakeInputDispatcherPolicy() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ }
+
+ void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
+ ASSERT_TRUE(mInputEventFiltered)
+ << "Expected filterInputEvent() to have been called.";
+
+ ASSERT_EQ(mTime, args->eventTime)
+ << "Expected time of filtered event was not matched";
+ ASSERT_EQ(mAction, args->action)
+ << "Expected action of filtered event was not matched";
+ ASSERT_EQ(mDisplayId, args->displayId)
+ << "Expected displayId of filtered event was not matched";
+
+ reset();
+ }
+
+ void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
+ ASSERT_TRUE(mInputEventFiltered)
+ << "Expected filterInputEvent() to have been called.";
+
+ ASSERT_EQ(mTime, args->eventTime)
+ << "Expected time of filtered event was not matched";
+ ASSERT_EQ(mAction, args->action)
+ << "Expected action of filtered event was not matched";
+ ASSERT_EQ(mDisplayId, args->displayId)
+ << "Expected displayId of filtered event was not matched";
+
+ reset();
+ }
+
+ void assertFilterInputEventWasNotCalled() {
+ ASSERT_FALSE(mInputEventFiltered)
+ << "Expected filterInputEvent() to not have been called.";
}
private:
+ bool mInputEventFiltered;
+ nsecs_t mTime;
+ int32_t mAction;
+ int32_t mDisplayId;
+
virtual void notifyConfigurationChanged(nsecs_t) {
}
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
- const sp<InputWindowHandle>&,
+ const sp<IBinder>&,
const std::string&) {
return 0;
}
- virtual void notifyInputChannelBroken(const sp<InputWindowHandle>&) {
+ virtual void notifyInputChannelBroken(const sp<IBinder>&) {
+ }
+
+ virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {
}
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent*, uint32_t) {
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
+ mTime = keyEvent->getEventTime();
+ mAction = keyEvent->getAction();
+ mDisplayId = keyEvent->getDisplayId();
+ break;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
+ mTime = motionEvent->getEventTime();
+ mAction = motionEvent->getAction();
+ mDisplayId = motionEvent->getDisplayId();
+ break;
+ }
+ }
+
+ mInputEventFiltered = true;
return true;
}
virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) {
}
- virtual void interceptMotionBeforeQueueing(nsecs_t, uint32_t&) {
+ virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) {
}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>&,
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&,
const KeyEvent*, uint32_t) {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>&,
+ virtual bool dispatchUnhandledKey(const sp<IBinder>&,
const KeyEvent*, uint32_t, KeyEvent*) {
return false;
}
@@ -94,6 +160,13 @@
virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
return false;
}
+
+ void reset() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ }
};
@@ -103,13 +176,20 @@
protected:
sp<FakeInputDispatcherPolicy> mFakePolicy;
sp<InputDispatcher> mDispatcher;
+ sp<InputDispatcherThread> mDispatcherThread;
virtual void SetUp() {
mFakePolicy = new FakeInputDispatcherPolicy();
mDispatcher = new InputDispatcher(mFakePolicy);
+ mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ //Start InputDispatcher thread
+ mDispatcherThread = new InputDispatcherThread(mDispatcher);
+ mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY);
}
virtual void TearDown() {
+ mDispatcherThread->requestExit();
+ mDispatcherThread.clear();
mFakePolicy.clear();
mDispatcher.clear();
}
@@ -120,20 +200,20 @@
KeyEvent event;
// Rejects undefined key actions.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
/*action*/ -1, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
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, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -148,109 +228,686 @@
pointerCoords[i].clear();
}
+ // Some constants commonly used below
+ constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
+ constexpr int32_t metaState = AMETA_NONE;
+ constexpr MotionClassification classification = MotionClassification::NONE;
+
// Rejects undefined motion actions.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- /*action*/ -1, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too large.";
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too large.";
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 0, pointerProperties, pointerCoords);
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with 0 pointers.";
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
pointerProperties[0].id = -1;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
pointerProperties[0].id = 1;
pointerProperties[1].id = 1;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
- ARBITRARY_TIME, ARBITRARY_TIME,
- /*pointerCount*/ 2, pointerProperties, pointerCoords);
+ event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
- &event, DISPLAY_ID,
+ &event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
+// --- InputDispatcherTest SetInputWindowTest ---
+static const int32_t INJECT_EVENT_TIMEOUT = 500;
+static const int32_t DISPATCHING_TIMEOUT = 100;
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+ FakeApplicationHandle() {}
+ virtual ~FakeApplicationHandle() {}
+
+ virtual bool updateInfo() {
+ if (!mInfo) {
+ mInfo = new InputApplicationInfo();
+ }
+ mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+ return true;
+ }
+};
+
+class FakeInputReceiver {
+public:
+ void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
+ int32_t expectedFlags = 0) {
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+ &consumeSeq, &event);
+
+ ASSERT_EQ(OK, status)
+ << mName.c_str() << ": consumer consume should return OK.";
+ ASSERT_TRUE(event != nullptr)
+ << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(expectedEventType, event->getType())
+ << mName.c_str() << ": event type should match.";
+
+ ASSERT_EQ(expectedDisplayId, event->getDisplayId())
+ << mName.c_str() << ": event displayId should be the same as expected.";
+
+ int32_t flags;
+ switch (expectedEventType) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ KeyEvent* typedEvent = static_cast<KeyEvent*>(event);
+ flags = typedEvent->getFlags();
+ break;
+ }
+ case AINPUT_EVENT_TYPE_MOTION: {
+ MotionEvent* typedEvent = static_cast<MotionEvent*>(event);
+ flags = typedEvent->getFlags();
+ break;
+ }
+ default: {
+ FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
+ }
+ }
+ ASSERT_EQ(expectedFlags, flags)
+ << mName.c_str() << ": event flags should be the same as expected.";
+
+ status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+ ASSERT_EQ(OK, status)
+ << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+ }
+
+ void assertNoEvents() {
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+ &consumeSeq, &event);
+ ASSERT_NE(OK, status)
+ << mName.c_str()
+ << ": should not have received any events, so consume(..) should not return OK.";
+ }
+
+protected:
+ explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
+ const std::string name, int32_t displayId) :
+ mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
+ InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ mConsumer = new InputConsumer(mClientChannel);
+ }
+
+ virtual ~FakeInputReceiver() {
+ }
+
+ // return true if the event has been handled.
+ virtual bool handled() {
+ return false;
+ }
+
+ sp<InputDispatcher> mDispatcher;
+ sp<InputChannel> mServerChannel, mClientChannel;
+ sp<IBinder> mToken;
+ InputConsumer *mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+
+ std::string mName;
+ int32_t mDisplayId;
+};
+
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+
+ FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
+ FakeInputReceiver(dispatcher, name, displayId),
+ mFocused(false) {
+ mServerChannel->setToken(new BBinder());
+ mDispatcher->registerInputChannel(mServerChannel, displayId);
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+ }
+
+ virtual bool updateInfo() {
+ mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
+ mInfo.name = mName;
+ mInfo.layoutParamsFlags = 0;
+ mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.frameLeft = 0;
+ mInfo.frameTop = 0;
+ mInfo.frameRight = WIDTH;
+ mInfo.frameBottom = HEIGHT;
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.visible = true;
+ mInfo.canReceiveKeys = true;
+ mInfo.hasFocus = mFocused;
+ mInfo.hasWallpaper = false;
+ mInfo.paused = false;
+ mInfo.layer = 0;
+ mInfo.ownerPid = INJECTOR_PID;
+ mInfo.ownerUid = INJECTOR_UID;
+ mInfo.inputFeatures = 0;
+ mInfo.displayId = mDisplayId;
+
+ return true;
+ }
+
+ void setFocus() {
+ mFocused = true;
+ }
+
+ void releaseChannel() {
+ mServerChannel.clear();
+ InputWindowHandle::releaseChannel();
+ }
+protected:
+ virtual bool handled() {
+ return true;
+ }
+
+ bool mFocused;
+};
+
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ KeyEvent event;
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // Define a valid key down event.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
+ AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, 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);
+}
+
+static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
+ int32_t displayId) {
+ MotionEvent event;
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid motion down event.
+ event.initialize(DEVICE_ID, source, displayId,
+ AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
+ AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+ /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
+ pointerCoords);
+
+ // 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);
+}
+
+static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid key event.
+ NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
+
+ return args;
+}
+
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // Define a valid motion event.
+ NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
+ POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+ AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties,
+ pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime,
+ /* videoFrames */ {});
+
+ return args;
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
+ ADISPLAY_ID_DEFAULT);
+
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(window);
+
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Window should receive motion event.
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+}
+
+// The foreground window should receive the first touch down event.
+TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(windowTop);
+ inputWindowHandles.push_back(windowSecond);
+
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Top window should receive the touch down event. Second window should not receive anything.
+ windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ // Expect one focus window exist in display.
+ windowSecond->setFocus();
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(windowTop);
+ inputWindowHandles.push_back(windowSecond);
+
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Focused window should receive event.
+ windowTop->assertNoEvents();
+ windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
+ windowTop->setFocus();
+ windowSecond->setFocus();
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(windowTop);
+ inputWindowHandles.push_back(windowSecond);
+
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Top focused window should receive event.
+ windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+ sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ windowTop->setFocus();
+ windowSecond->setFocus();
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(windowTop);
+ inputWindowHandles.push_back(windowSecond);
+ // Release channel for window is no longer valid.
+ windowTop->releaseChannel();
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+
+ // Test inject a key down, should dispatch to a valid window.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+ // Top window is invalid, so it should not receive any input event.
+ windowTop->assertNoEvents();
+ windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+/* Test InputDispatcher for MultiDisplay */
+class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
+public:
+ static constexpr int32_t SECOND_DISPLAY_ID = 1;
+ virtual void SetUp() {
+ InputDispatcherTest::SetUp();
+
+ application1 = new FakeApplicationHandle();
+ windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
+ ADISPLAY_ID_DEFAULT);
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(windowInPrimary);
+ // Set focus window for primary display, but focused display would be second one.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
+ windowInPrimary->setFocus();
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+
+ application2 = new FakeApplicationHandle();
+ windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
+ SECOND_DISPLAY_ID);
+ // Set focus to second display window.
+ std::vector<sp<InputWindowHandle>> inputWindowHandles_Second;
+ inputWindowHandles_Second.push_back(windowInSecondary);
+ // Set focus display to second one.
+ mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ // Set focus window for second display.
+ mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
+ windowInSecondary->setFocus();
+ mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+ }
+
+ virtual void TearDown() {
+ InputDispatcherTest::TearDown();
+
+ application1.clear();
+ windowInPrimary.clear();
+ application2.clear();
+ windowInSecondary.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> application1;
+ sp<FakeWindowHandle> windowInPrimary;
+ sp<FakeApplicationHandle> application2;
+ sp<FakeWindowHandle> windowInSecondary;
+};
+
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
+ // Test touch down on primary display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowInSecondary->assertNoEvents();
+
+ // Test touch down on second display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+}
+
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
+ // Test inject a key down with display id specified.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT);
+ windowInSecondary->assertNoEvents();
+
+ // Test inject a key down without display id specified.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+
+ // Remove secondary display.
+ std::vector<sp<InputWindowHandle>> noWindows;
+ mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+
+ // Expect old focus should receive a cancel event.
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
+ AKEY_EVENT_FLAG_CANCELED);
+
+ // Test inject a key down, should timeout because of no target window.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+ windowInPrimary->assertNoEvents();
+ windowInSecondary->assertNoEvents();
+}
+
+class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
+public:
+ FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+ int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) {
+ mDispatcher->registerInputChannel(mServerChannel, displayId);
+ }
+};
+
+// Test per-display input monitors for motion event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
+ sp<FakeMonitorReceiver> monitorInPrimary =
+ new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ sp<FakeMonitorReceiver> monitorInSecondary =
+ new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ // Test touch down on primary display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowInSecondary->assertNoEvents();
+ monitorInSecondary->assertNoEvents();
+
+ // Test touch down on second display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+ monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+
+ // Test inject a non-pointer motion event.
+ // If specific a display, it will dispatch to the focused window of particular display,
+ // or it will dispatch to the focused window of focused display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+ monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+}
+
+// Test per-display input monitors for key event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
+ //Input monitor per display.
+ sp<FakeMonitorReceiver> monitorInPrimary =
+ new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ sp<FakeMonitorReceiver> monitorInSecondary =
+ new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ // Test inject a key down.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+class InputFilterTest : public InputDispatcherTest {
+protected:
+ static constexpr int32_t SECOND_DISPLAY_ID = 1;
+
+ void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+ NotifyMotionArgs motionArgs;
+
+ motionArgs = generateMotionArgs(
+ AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ mDispatcher->notifyMotion(&motionArgs);
+ motionArgs = generateMotionArgs(
+ AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ if (expectToBeFiltered) {
+ mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+ } else {
+ mFakePolicy->assertFilterInputEventWasNotCalled();
+ }
+ }
+
+ void testNotifyKey(bool expectToBeFiltered) {
+ NotifyKeyArgs keyArgs;
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+ mDispatcher->notifyKey(&keyArgs);
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
+ mDispatcher->notifyKey(&keyArgs);
+
+ if (expectToBeFiltered) {
+ mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+ } else {
+ mFakePolicy->assertFilterInputEventWasNotCalled();
+ }
+ }
+};
+
+// Test InputFilter for MotionEvent
+TEST_F(InputFilterTest, MotionEvent_InputFilter) {
+ // Since the InputFilter is disabled by default, check if touch events aren't filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+ // Test touch on both primary and second display, and check if both events are filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+
+ // Disable InputFilter
+ mDispatcher->setInputFilterEnabled(false);
+ // Test touch on both primary and second display, and check if both events aren't filtered.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+}
+
+// Test InputFilter for KeyEvent
+TEST_F(InputFilterTest, KeyEvent_InputFilter) {
+ // Since the InputFilter is disabled by default, check if key event aren't filtered.
+ testNotifyKey(/*expectToBeFiltered*/ false);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+ // Send a key event, and check if it is filtered.
+ testNotifyKey(/*expectToBeFiltered*/ true);
+
+ // Disable InputFilter
+ mDispatcher->setInputFilterEnabled(false);
+ // Send a key event, and check if it isn't filtered.
+ testNotifyKey(/*expectToBeFiltered*/ false);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 22f15a0..d353028 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -15,12 +15,13 @@
*/
#include "../InputReader.h"
+#include "TestInputListener.h"
-#include <inttypes.h>
-#include <utils/List.h>
#include <gtest/gtest.h>
+#include <inttypes.h>
#include <math.h>
+
namespace android {
// An arbitrary time value.
@@ -28,12 +29,14 @@
// 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 const char* VIRTUAL_DISPLAY_UNIQUE_ID = "Vr-display-unique-ID";
+static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
@@ -55,6 +58,7 @@
float mMinX, mMinY, mMaxX, mMaxY;
float mX, mY;
int32_t mButtonState;
+ int32_t mDisplayId;
protected:
virtual ~FakePointerController() { }
@@ -62,7 +66,7 @@
public:
FakePointerController() :
mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
- mButtonState(0) {
+ mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
}
void setBounds(float minX, float minY, float maxX, float maxY) {
@@ -73,6 +77,10 @@
mMaxY = maxY;
}
+ void setDisplayId(int32_t displayId) {
+ mDisplayId = displayId;
+ }
+
virtual void setPosition(float x, float y) {
mX = x;
mY = y;
@@ -91,6 +99,14 @@
*outY = mY;
}
+ virtual int32_t getDisplayId() const {
+ return mDisplayId;
+ }
+
+ const std::map<int32_t, std::vector<int32_t>>& getSpots() {
+ return mSpotsByDisplay;
+ }
+
private:
virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
*outMinX = mMinX;
@@ -118,11 +134,22 @@
virtual void setPresentation(Presentation) {
}
- virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32) {
+ virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+ int32_t displayId) {
+ std::vector<int32_t> newSpots;
+ // Add spots for fingers that are down.
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ newSpots.push_back(id);
+ }
+
+ mSpotsByDisplay[displayId] = newSpots;
}
virtual void clearSpots() {
}
+
+ std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
@@ -131,7 +158,8 @@
class FakeInputReaderPolicy : public InputReaderPolicyInterface {
InputReaderConfiguration mConfig;
KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
- Vector<InputDeviceInfo> mInputDevices;
+ std::vector<InputDeviceInfo> mInputDevices;
+ std::vector<DisplayViewport> mViewports;
TouchAffineTransformation transform;
protected:
@@ -141,23 +169,37 @@
FakeInputReaderPolicy() {
}
- void setDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- const String8& uniqueId) {
- DisplayViewport v = createDisplayViewport(displayId, width, height, orientation, uniqueId);
- // Set the size of both the internal and external display at the same time.
- mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL, v);
- mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, v);
+ virtual void clearViewports() {
+ mViewports.clear();
+ mConfig.setDisplayViewports(mViewports);
}
- void setVirtualDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- const String8& uniqueId) {
- Vector<DisplayViewport> viewports;
- viewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId));
- mConfig.setVirtualDisplayViewports(viewports);
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
+ return mConfig.getDisplayViewportByUniqueId(uniqueId);
+ }
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
+ return mConfig.getDisplayViewportByType(type);
}
- void addExcludedDeviceName(const String8& deviceName) {
- mConfig.excludedDeviceNames.push(deviceName);
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
+ return mConfig.getDisplayViewportByPort(displayPort);
+ }
+
+ void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+ const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
+ orientation, uniqueId, physicalPort, viewportType);
+ mViewports.push_back(viewport);
+ mConfig.setDisplayViewports(mViewports);
+ }
+
+ void addExcludedDeviceName(const std::string& deviceName) {
+ mConfig.excludedDeviceNames.push_back(deviceName);
+ }
+
+ void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
+ mConfig.portAssociations.insert({inputPort, displayPort});
}
void addDisabledDevice(int32_t deviceId) {
@@ -184,11 +226,11 @@
return &mConfig;
}
- const Vector<InputDeviceInfo>& getInputDevices() const {
+ const std::vector<InputDeviceInfo>& getInputDevices() const {
return mInputDevices;
}
- TouchAffineTransformation getTouchAffineTransformation(const String8& inputDeviceDescriptor,
+ TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
int32_t surfaceRotation) {
return transform;
}
@@ -201,9 +243,14 @@
mConfig.pointerCapture = enabled;
}
+ void setShowTouches(bool enabled) {
+ mConfig.showTouches = enabled;
+ }
+
private:
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const String8& uniqueId) {
+ int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+ ViewportType type) {
bool isRotated = (orientation == DISPLAY_ORIENTATION_90
|| orientation == DISPLAY_ORIENTATION_270);
DisplayViewport v;
@@ -220,6 +267,8 @@
v.deviceWidth = isRotated ? height : width;
v.deviceHeight = isRotated ? width : height;
v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
return v;
}
@@ -231,127 +280,19 @@
return mPointerControllers.valueFor(deviceId);
}
- virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) {
+ virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
mInputDevices = inputDevices;
}
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
- return NULL;
+ return nullptr;
}
- virtual String8 getDeviceAlias(const InputDeviceIdentifier&) {
- return String8::empty();
+ virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
+ return "";
}
};
-
-// --- FakeInputListener ---
-
-class FakeInputListener : public InputListenerInterface {
-private:
- List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
- List<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
- List<NotifyKeyArgs> mNotifyKeyArgsQueue;
- List<NotifyMotionArgs> mNotifyMotionArgsQueue;
- List<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
-protected:
- virtual ~FakeInputListener() { }
-
-public:
- FakeInputListener() {
- }
-
- void assertNotifyConfigurationChangedWasCalled(
- NotifyConfigurationChangedArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
- }
- mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
- }
-
- void assertNotifyConfigurationChangedWasNotCalled() {
- ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
- << "Expected notifyConfigurationChanged() to not have been called.";
- }
-
- void assertNotifyDeviceResetWasCalled(
- NotifyDeviceResetArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
- }
- mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
- }
-
- void assertNotifyDeviceResetWasNotCalled() {
- ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
- << "Expected notifyDeviceReset() to not have been called.";
- }
-
- void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
- << "Expected notifyKey() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyKeyArgsQueue.begin();
- }
- mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
- }
-
- void assertNotifyKeyWasNotCalled() {
- ASSERT_TRUE(mNotifyKeyArgsQueue.empty())
- << "Expected notifyKey() to not have been called.";
- }
-
- void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifyMotionArgsQueue.begin();
- }
- mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
- }
-
- void assertNotifyMotionWasNotCalled() {
- ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
- << "Expected notifyMotion() to not have been called.";
- }
-
- void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = NULL) {
- ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
- << "Expected notifySwitch() to have been called.";
- if (outEventArgs) {
- *outEventArgs = *mNotifySwitchArgsQueue.begin();
- }
- mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
- }
-
-private:
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- mNotifyConfigurationChangedArgsQueue.push_back(*args);
- }
-
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- mNotifyDeviceResetArgsQueue.push_back(*args);
- }
-
- virtual void notifyKey(const NotifyKeyArgs* args) {
- mNotifyKeyArgsQueue.push_back(*args);
- }
-
- virtual void notifyMotion(const NotifyMotionArgs* args) {
- mNotifyMotionArgsQueue.push_back(*args);
- }
-
- virtual void notifySwitch(const NotifySwitchArgs* args) {
- mNotifySwitchArgsQueue.push_back(*args);
- }
-};
-
-
// --- FakeEventHub ---
class FakeEventHub : public EventHubInterface {
@@ -373,7 +314,7 @@
KeyedVector<int32_t, KeyInfo> keysByScanCode;
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
KeyedVector<int32_t, bool> leds;
- Vector<VirtualKeyDefinition> virtualKeys;
+ std::vector<VirtualKeyDefinition> virtualKeys;
bool enabled;
status_t enable() {
@@ -392,8 +333,9 @@
};
KeyedVector<int32_t, Device*> mDevices;
- Vector<String8> mExcludedDevices;
+ std::vector<std::string> mExcludedDevices;
List<RawEvent> mEvents;
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
protected:
virtual ~FakeEventHub() {
@@ -405,7 +347,7 @@
public:
FakeEventHub() { }
- void addDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
Device* device = new Device(classes);
device->identifier.name = name;
mDevices.add(deviceId, device);
@@ -422,7 +364,7 @@
bool isDeviceEnabled(int32_t deviceId) {
Device* device = getDevice(deviceId);
- if (device == NULL) {
+ if (device == nullptr) {
ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
return false;
}
@@ -432,7 +374,7 @@
status_t enableDevice(int32_t deviceId) {
status_t result;
Device* device = getDevice(deviceId);
- if (device == NULL) {
+ if (device == nullptr) {
ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
return BAD_VALUE;
}
@@ -446,7 +388,7 @@
status_t disableDevice(int32_t deviceId) {
Device* device = getDevice(deviceId);
- if (device == NULL) {
+ if (device == nullptr) {
ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
return BAD_VALUE;
}
@@ -534,13 +476,13 @@
return device->leds.valueFor(led);
}
- Vector<String8>& getExcludedDevices() {
+ std::vector<std::string>& getExcludedDevices() {
return mExcludedDevices;
}
void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
Device* device = getDevice(deviceId);
- device->virtualKeys.push(definition);
+ device->virtualKeys.push_back(definition);
}
void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
@@ -558,6 +500,11 @@
}
}
+ void setVideoFrames(std::unordered_map<int32_t /*deviceId*/,
+ std::vector<TouchVideoFrame>> videoFrames) {
+ mVideoFrames = std::move(videoFrames);
+ }
+
void assertQueueIsEmpty() {
ASSERT_EQ(size_t(0), mEvents.size())
<< "Expected the event queue to be empty (fully consumed).";
@@ -566,7 +513,7 @@
private:
Device* getDevice(int32_t deviceId) const {
ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : NULL;
+ return index >= 0 ? mDevices.valueAt(index) : nullptr;
}
virtual uint32_t getDeviceClasses(int32_t deviceId) const {
@@ -651,14 +598,14 @@
return &device->keysByScanCode.valueAt(index);
}
}
- return NULL;
+ return nullptr;
}
virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const {
return NAME_NOT_FOUND;
}
- virtual void setExcludedDevices(const Vector<String8>& devices) {
+ virtual void setExcludedDevices(const std::vector<std::string>& devices) {
mExcludedDevices = devices;
}
@@ -672,6 +619,16 @@
return 1;
}
+ virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+ auto it = mVideoFrames.find(deviceId);
+ if (it != mVideoFrames.end()) {
+ std::vector<TouchVideoFrame> frames = std::move(it->second);
+ mVideoFrames.erase(deviceId);
+ return frames;
+ }
+ return {};
+ }
+
virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
Device* device = getDevice(deviceId);
if (device) {
@@ -771,17 +728,17 @@
}
virtual void getVirtualKeyDefinitions(int32_t deviceId,
- Vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
outVirtualKeys.clear();
Device* device = getDevice(deviceId);
if (device) {
- outVirtualKeys.appendVector(device->virtualKeys);
+ outVirtualKeys = device->virtualKeys;
}
}
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const {
- return NULL;
+ return nullptr;
}
virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) {
@@ -821,13 +778,14 @@
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
+ uint32_t mNextSequenceNum;
public:
FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mEventHub(eventHub), mPolicy(policy), mListener(listener),
- mGlobalMetaState(0) {
+ mGlobalMetaState(0), mNextSequenceNum(1) {
}
virtual ~FakeInputReaderContext() { }
@@ -884,13 +842,17 @@
return ++mGeneration;
}
- virtual void getExternalStylusDevices(Vector<InputDeviceInfo>& outDevices) {
+ virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
}
virtual void dispatchExternalStylusState(const StylusState&) {
}
+
+ virtual uint32_t getNextSequenceNum() {
+ return mNextSequenceNum++;
+ }
};
@@ -903,13 +865,14 @@
KeyedVector<int32_t, int32_t> mKeyCodeStates;
KeyedVector<int32_t, int32_t> mScanCodeStates;
KeyedVector<int32_t, int32_t> mSwitchStates;
- Vector<int32_t> mSupportedKeyCodes;
+ std::vector<int32_t> mSupportedKeyCodes;
RawEvent mLastEvent;
bool mConfigureWasCalled;
bool mResetWasCalled;
bool mProcessWasCalled;
+ std::optional<DisplayViewport> mViewport;
public:
FakeInputMapper(InputDevice* device, uint32_t sources) :
InputMapper(device),
@@ -940,7 +903,7 @@
mResetWasCalled = false;
}
- void assertProcessWasCalled(RawEvent* outLastEvent = NULL) {
+ void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
ASSERT_TRUE(mProcessWasCalled)
<< "Expected process() to have been called.";
if (outLastEvent) {
@@ -962,7 +925,7 @@
}
void addSupportedKeyCode(int32_t keyCode) {
- mSupportedKeyCodes.add(keyCode);
+ mSupportedKeyCodes.push_back(keyCode);
}
private:
@@ -978,8 +941,14 @@
}
}
- virtual void configure(nsecs_t, const InputReaderConfiguration*, uint32_t) {
+ virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
mConfigureWasCalled = true;
+
+ // Find the associated viewport if exist.
+ const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+ if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mViewport = config->getDisplayViewportByPort(*displayPort);
+ }
}
virtual void reset(nsecs_t) {
@@ -1026,6 +995,13 @@
virtual void fadePointer() {
}
+
+ virtual std::optional<int32_t> getAssociatedDisplay() {
+ if (mViewport) {
+ return std::make_optional(mViewport->displayId);
+ }
+ return std::nullopt;
+ }
};
@@ -1039,7 +1015,7 @@
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
InputReader(eventHub, policy, listener),
- mNextDevice(NULL) {
+ mNextDevice(nullptr) {
}
virtual ~InstrumentedInputReader() {
@@ -1052,10 +1028,11 @@
mNextDevice = device;
}
- InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const String8& name,
- uint32_t classes) {
+ InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
+ uint32_t classes, const std::string& location = "") {
InputDeviceIdentifier identifier;
identifier.name = name;
+ identifier.location = location;
int32_t generation = deviceId + 1;
return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
classes);
@@ -1066,7 +1043,7 @@
const InputDeviceIdentifier& identifier, uint32_t classes) {
if (mNextDevice) {
InputDevice* device = mNextDevice;
- mNextDevice = NULL;
+ mNextDevice = nullptr;
return device;
}
return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes);
@@ -1075,12 +1052,197 @@
friend class InputReaderTest;
};
+// --- InputReaderPolicyTest ---
+class InputReaderPolicyTest : public testing::Test {
+protected:
+ sp<FakeInputReaderPolicy> mFakePolicy;
+
+ virtual void SetUp() {
+ mFakePolicy = new FakeInputReaderPolicy();
+ }
+ virtual void TearDown() {
+ mFakePolicy.clear();
+ }
+};
+
+/**
+ * Check that empty set of viewports is an acceptable configuration.
+ * Also try to get internal viewport two different ways - by type and by uniqueId.
+ *
+ * There will be confusion if two viewports with empty uniqueId and identical type are present.
+ * Such configuration is not currently allowed.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetCleared) {
+ static const std::string uniqueId = "local:0";
+
+ // We didn't add any viewports yet, so there shouldn't be any.
+ std::optional<DisplayViewport> internalViewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_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);
+
+ // Check matching by uniqueId
+ internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
+ ASSERT_TRUE(internalViewport);
+ ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
+
+ // Check matching by viewport type
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ ASSERT_TRUE(internalViewport);
+ ASSERT_EQ(uniqueId, internalViewport->uniqueId);
+
+ mFakePolicy->clearViewports();
+ // Make sure nothing is found after clear
+ internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
+ ASSERT_FALSE(internalViewport);
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ ASSERT_FALSE(internalViewport);
+}
+
+TEST_F(InputReaderPolicyTest, Viewports_GetByType) {
+ const std::string internalUniqueId = "local:0";
+ const std::string externalUniqueId = "local:1";
+ const std::string virtualUniqueId1 = "virtual:2";
+ const std::string virtualUniqueId2 = "virtual:3";
+ constexpr int32_t virtualDisplayId1 = 2;
+ constexpr int32_t virtualDisplayId2 = 3;
+
+ // Add an internal viewport
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ // Add an external viewport
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
+ // Add an virtual viewport
+ mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+ // Add another virtual viewport
+ mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
+
+ // Check matching by type for internal
+ std::optional<DisplayViewport> internalViewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+ ASSERT_TRUE(internalViewport);
+ ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
+
+ // Check matching by type for external
+ std::optional<DisplayViewport> externalViewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
+ ASSERT_TRUE(externalViewport);
+ ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
+
+ // Check matching by uniqueId for virtual viewport #1
+ std::optional<DisplayViewport> virtualViewport1 =
+ mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
+ ASSERT_TRUE(virtualViewport1);
+ ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
+ ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
+ ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
+
+ // Check matching by uniqueId for virtual viewport #2
+ std::optional<DisplayViewport> virtualViewport2 =
+ mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
+ ASSERT_TRUE(virtualViewport2);
+ ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
+ ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
+ ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
+}
+
+
+/**
+ * We can have 2 viewports of the same kind. We can distinguish them by uniqueId, and confirm
+ * that lookup works by checking display id.
+ * Check that 2 viewports of each kind is possible, for all existing viewport types.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) {
+ const std::string uniqueId1 = "uniqueId1";
+ const std::string uniqueId2 = "uniqueId2";
+ constexpr int32_t displayId1 = 2;
+ constexpr int32_t displayId2 = 3;
+
+ std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL,
+ ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL};
+ for (const ViewportType& type : types) {
+ mFakePolicy->clearViewports();
+ // Add a viewport
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, type);
+ // Add another viewport
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, type);
+
+ // Check that correct display viewport was returned by comparing the display IDs.
+ std::optional<DisplayViewport> viewport1 =
+ mFakePolicy->getDisplayViewportByUniqueId(uniqueId1);
+ ASSERT_TRUE(viewport1);
+ ASSERT_EQ(displayId1, viewport1->displayId);
+ ASSERT_EQ(type, viewport1->type);
+
+ std::optional<DisplayViewport> viewport2 =
+ mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+ ASSERT_TRUE(viewport2);
+ ASSERT_EQ(displayId2, viewport2->displayId);
+ ASSERT_EQ(type, viewport2->type);
+
+ // When there are multiple viewports of the same kind, and uniqueId is not specified
+ // in the call to getDisplayViewport, then that situation is not supported.
+ // The viewports can be stored in any order, so we cannot rely on the order, since that
+ // is just implementation detail.
+ // However, we can check that it still returns *a* viewport, we just cannot assert
+ // which one specifically is returned.
+ std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewportByType(type);
+ ASSERT_TRUE(someViewport);
+ }
+}
+
+/**
+ * Check getDisplayViewportByPort
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
+ constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ const std::string uniqueId1 = "uniqueId1";
+ const std::string uniqueId2 = "uniqueId2";
+ constexpr int32_t displayId1 = 1;
+ constexpr int32_t displayId2 = 2;
+ const uint8_t hdmi1 = 0;
+ const uint8_t hdmi2 = 1;
+ const uint8_t hdmi3 = 2;
+
+ mFakePolicy->clearViewports();
+ // Add a viewport that's associated with some display port that's not of interest.
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, hdmi3, type);
+ // Add another viewport, connected to HDMI1 port
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, hdmi1, type);
+
+ // Check that correct display viewport was returned by comparing the display ports.
+ std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
+ ASSERT_TRUE(hdmi1Viewport);
+ ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+ ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+
+ // Check that we can still get the same viewport using the uniqueId
+ hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+ ASSERT_TRUE(hdmi1Viewport);
+ ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+ ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+ ASSERT_EQ(type, hdmi1Viewport->type);
+
+ // Check that we cannot find a port with "HDMI2", because we never added one
+ std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2);
+ ASSERT_FALSE(hdmi2Viewport);
+}
// --- InputReaderTest ---
class InputReaderTest : public testing::Test {
protected:
- sp<FakeInputListener> mFakeListener;
+ sp<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
sp<FakeEventHub> mFakeEventHub;
sp<InstrumentedInputReader> mReader;
@@ -1088,7 +1250,7 @@
virtual void SetUp() {
mFakeEventHub = new FakeEventHub();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
+ mFakeListener = new TestInputListener();
mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
}
@@ -1101,7 +1263,7 @@
mFakeEventHub.clear();
}
- void addDevice(int32_t deviceId, const String8& name, uint32_t classes,
+ void addDevice(int32_t deviceId, const std::string& name, uint32_t classes,
const PropertyMap* configuration) {
mFakeEventHub->addDevice(deviceId, name, classes);
@@ -1129,7 +1291,7 @@
}
FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
- const String8& name, uint32_t classes, uint32_t sources,
+ const std::string& name, uint32_t classes, uint32_t sources,
const PropertyMap* configuration) {
InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes);
FakeInputMapper* mapper = new FakeInputMapper(device, sources);
@@ -1141,17 +1303,18 @@
};
TEST_F(InputReaderTest, GetInputDevices) {
- ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
- INPUT_DEVICE_CLASS_KEYBOARD, NULL));
- ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"),
- 0, NULL)); // no classes so device will be ignored
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard",
+ INPUT_DEVICE_CLASS_KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
+ 0, nullptr)); // no classes so device will be ignored
- Vector<InputDeviceInfo> inputDevices;
+
+ std::vector<InputDeviceInfo> inputDevices;
mReader->getInputDevices(inputDevices);
ASSERT_EQ(1U, inputDevices.size());
ASSERT_EQ(1, inputDevices[0].getId());
- ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
+ ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
@@ -1160,7 +1323,7 @@
inputDevices = mFakePolicy->getInputDevices();
ASSERT_EQ(1U, inputDevices.size());
ASSERT_EQ(1, inputDevices[0].getId());
- ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string());
+ ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
@@ -1169,14 +1332,14 @@
TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
constexpr int32_t deviceId = 1;
constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
- InputDevice* device = mReader->newDevice(deviceId, 0, String8("fake"), deviceClass);
+ InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
// Must add at least one mapper or the device will be ignored!
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
device->addMapper(mapper);
mReader->setNextDevice(device);
- addDevice(deviceId, String8("fake"), deviceClass, NULL);
+ addDevice(deviceId, "fake", deviceClass, nullptr);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(NULL));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1207,9 +1370,9 @@
}
TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+ FakeInputMapper* mapper = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
@@ -1234,9 +1397,9 @@
}
TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+ FakeInputMapper* mapper = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
@@ -1261,9 +1424,9 @@
}
TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+ FakeInputMapper* mapper = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
@@ -1288,9 +1451,10 @@
}
TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+ FakeInputMapper* mapper = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+
mapper->addSupportedKeyCode(AKEYCODE_A);
mapper->addSupportedKeyCode(AKEYCODE_B);
@@ -1323,7 +1487,7 @@
}
TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
- addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD, NULL);
+ addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
NotifyConfigurationChangedArgs args;
@@ -1332,9 +1496,9 @@
}
TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
- FakeInputMapper* mapper = NULL;
- ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
- INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
+ FakeInputMapper* mapper = nullptr;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
mReader->loopOnce();
@@ -1349,6 +1513,69 @@
ASSERT_EQ(1, event.value);
}
+TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
+ constexpr int32_t deviceId = 1;
+ constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
+ // Must add at least one mapper or the device will be ignored!
+ FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
+ device->addMapper(mapper);
+ mReader->setNextDevice(device);
+ addDevice(deviceId, "fake", deviceClass, nullptr);
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ uint32_t prevSequenceNum = resetArgs.sequenceNum;
+
+ disableDevice(deviceId, device);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
+ prevSequenceNum = resetArgs.sequenceNum;
+
+ enableDevice(deviceId, device);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
+ prevSequenceNum = resetArgs.sequenceNum;
+
+ disableDevice(deviceId, device);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
+ prevSequenceNum = resetArgs.sequenceNum;
+}
+
+TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
+ constexpr int32_t deviceId = 1;
+ constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ const char* DEVICE_LOCATION = "USB1";
+ InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
+ DEVICE_LOCATION);
+ FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
+ device->addMapper(mapper);
+ mReader->setNextDevice(device);
+ addDevice(deviceId, "fake", deviceClass, nullptr);
+
+ const uint8_t hdmi1 = 1;
+
+ // Associated touch screen with second display.
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+
+ // Add default and second display.
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+ mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ mReader->loopOnce();
+
+ // Check device.
+ ASSERT_EQ(deviceId, device->getId());
+ ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
+ ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+}
+
// --- InputDeviceTest ---
@@ -1362,7 +1589,7 @@
sp<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeInputListener> mFakeListener;
+ sp<TestInputListener> mFakeListener;
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
@@ -1370,10 +1597,10 @@
virtual void SetUp() {
mFakeEventHub = new FakeEventHub();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
+ mFakeListener = new TestInputListener();
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
- mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
+ mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
@@ -1399,7 +1626,7 @@
TEST_F(InputDeviceTest, ImmutableProperties) {
ASSERT_EQ(DEVICE_ID, mDevice->getId());
- ASSERT_STREQ(DEVICE_NAME, mDevice->getName());
+ ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
}
@@ -1427,7 +1654,7 @@
InputDeviceInfo info;
mDevice->getDeviceInfo(&info);
ASSERT_EQ(DEVICE_ID, info.getId());
- ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
+ ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType());
ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources());
@@ -1497,7 +1724,7 @@
InputDeviceInfo info;
mDevice->getDeviceInfo(&info);
ASSERT_EQ(DEVICE_ID, info.getId());
- ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string());
+ ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType());
ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources());
@@ -1549,6 +1776,7 @@
class InputMapperTest : public testing::Test {
protected:
static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
static const int32_t DEVICE_ID;
static const int32_t DEVICE_GENERATION;
static const int32_t DEVICE_CONTROLLER_NUMBER;
@@ -1556,21 +1784,22 @@
sp<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<FakeInputListener> mFakeListener;
+ sp<TestInputListener> mFakeListener;
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
virtual void SetUp() {
mFakeEventHub = new FakeEventHub();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new FakeInputListener();
+ mFakeListener = new TestInputListener();
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
+ identifier.location = DEVICE_LOCATION;
mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
- mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
+ mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
}
virtual void TearDown() {
@@ -1582,7 +1811,7 @@
}
void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
+ mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
}
void configureDevice(uint32_t changes) {
@@ -1596,22 +1825,22 @@
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation) {
- mFakePolicy->setDisplayViewport(displayId, width, height, orientation, String8::empty());
+ int32_t orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType viewportType) {
+ mFakePolicy->addDisplayViewport(
+ displayId, width, height, orientation, uniqueId, physicalPort, viewportType);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
- void setVirtualDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const String8& uniqueId) {
- mFakePolicy->setVirtualDisplayViewport(displayId, width, height, orientation, uniqueId);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ void clearViewports() {
+ mFakePolicy->clearViewports();
}
- static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
+ static void process(InputMapper* mapper, nsecs_t when, int32_t type,
int32_t code, int32_t value) {
RawEvent event;
event.when = when;
- event.deviceId = deviceId;
+ event.deviceId = mapper->getDeviceId();
event.type = type;
event.code = code;
event.value = value;
@@ -1621,7 +1850,7 @@
static void assertMotionRange(const InputDeviceInfo& info,
int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
- ASSERT_TRUE(range != NULL) << "Axis: " << axis << " Source: " << source;
+ ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
@@ -1655,6 +1884,7 @@
};
const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
const int32_t InputMapperTest::DEVICE_ID = 1;
const int32_t InputMapperTest::DEVICE_GENERATION = 2;
const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1689,10 +1919,10 @@
SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
addMapperAndConfigure(mapper);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_HEADPHONE_INSERT, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
+ process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+ process(mapper, ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
NotifySwitchArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
@@ -1708,21 +1938,33 @@
class KeyboardInputMapperTest : public InputMapperTest {
protected:
+ const std::string UNIQUE_ID = "local:0";
+
+ void prepareDisplay(int32_t orientation);
+
void testDPadKeyRotation(KeyboardInputMapper* mapper,
int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
};
+/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
+ * orientation.
+ */
+void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
+}
+
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
- int32_t originalScanCode, int32_t, int32_t rotatedKeyCode) {
+ int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
NotifyKeyArgs args;
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(originalScanCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(originalScanCode, args.scanCode);
@@ -1749,8 +1991,7 @@
addMapperAndConfigure(mapper);
// Key down by scan code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -1765,8 +2006,7 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1780,10 +2020,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key down by usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, 0, 1);
+ process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ process(mapper, ARBITRARY_TIME, EV_KEY, 0, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1797,10 +2035,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, 0, 0);
+ process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, 0, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1814,10 +2050,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key down with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_UNKNOWN, 1);
+ process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1831,10 +2065,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, KEY_UNKNOWN, 0);
+ process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -1860,8 +2092,7 @@
ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
// Metakey down.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_LEFTSHIFT, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
@@ -1869,22 +2100,19 @@
ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
// Key down.
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
- EV_KEY, KEY_A, 1);
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
// Key up.
- process(mapper, ARBITRARY_TIME + 2, DEVICE_ID,
- EV_KEY, KEY_A, 0);
+ process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
// Metakey up.
- process(mapper, ARBITRARY_TIME + 3, DEVICE_ID,
- EV_KEY, KEY_LEFTSHIFT, 0);
+ process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_NONE, args.metaState);
ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
@@ -1901,9 +2129,7 @@
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
addMapperAndConfigure(mapper);
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90);
+ prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1925,9 +2151,7 @@
addConfigurationProperty("keyboard.orientationAware", "1");
addMapperAndConfigure(mapper);
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0);
+ prepareDisplay(DISPLAY_ORIENTATION_0);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1937,9 +2161,8 @@
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90);
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1949,9 +2172,8 @@
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_180);
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_180);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1961,9 +2183,8 @@
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_270);
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_270);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1976,26 +2197,81 @@
// Special case: if orientation changes while key is down, we still emit the same keycode
// in the key up as we did in the key down.
NotifyKeyArgs args;
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_270);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1);
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_270);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_180);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0);
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_180);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
}
+TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) {
+ // If the keyboard is not orientation aware,
+ // key events should not be associated with a specific display id
+ mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+ NotifyKeyArgs args;
+
+ // Display id should be ADISPLAY_ID_NONE without any display configuration.
+ 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);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
+
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ 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);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
+}
+
+TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
+ // If the keyboard is orientation aware,
+ // key events should be associated with the internal viewport
+ mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addConfigurationProperty("keyboard.orientationAware", "1");
+ addMapperAndConfigure(mapper);
+ NotifyKeyArgs args;
+
+ // Display id should be ADISPLAY_ID_NONE without any display configuration.
+ // ^--- already checked by the previous test
+
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_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);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(DISPLAY_ID, args.displayId);
+
+ constexpr int32_t newDisplayId = 2;
+ clearViewports();
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_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);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(newDisplayId, args.displayId);
+}
+
TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
@@ -2052,60 +2328,48 @@
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState());
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState());
// Toggle caps lock off.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState());
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
// Toggle num lock off.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
// Toggle scroll lock off.
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID,
- EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
@@ -2125,11 +2389,18 @@
InputMapperTest::SetUp();
mFakePointerController = new FakePointerController();
- mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
+ mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
}
void testMotionRotation(CursorInputMapper* mapper,
int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+
+ void prepareDisplay(int32_t orientation) {
+ const std::string uniqueId = "local:0";
+ const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ orientation, uniqueId, NO_PORT, viewportType);
+ }
};
const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -2138,9 +2409,9 @@
int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, originalX);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, originalY);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, originalY);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2174,8 +2445,8 @@
mapper->populateDeviceInfo(&info);
// Initially there may not be a valid motion range.
- ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
- ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
@@ -2226,8 +2497,8 @@
// Button press.
// Mostly testing non x/y behavior here so we don't need to check again elsewhere.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -2267,8 +2538,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Button release. Should have same down time.
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -2316,16 +2587,16 @@
NotifyMotionArgs args;
// Motion in X but not Y.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Motion in Y but not X.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2340,8 +2611,8 @@
NotifyMotionArgs args;
// Button press.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2353,8 +2624,8 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Button release.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2374,10 +2645,10 @@
NotifyMotionArgs args;
// Combined X, Y and Button.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2391,9 +2662,9 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Move X, Y a bit while pressed.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 2);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 2);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2401,8 +2672,8 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Release Button.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -2419,9 +2690,7 @@
addConfigurationProperty("cursor.mode", "navigation");
addMapperAndConfigure(mapper);
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90);
+ prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -2438,8 +2707,7 @@
addConfigurationProperty("cursor.orientationAware", "1");
addMapperAndConfigure(mapper);
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
+ prepareDisplay(DISPLAY_ORIENTATION_0);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -2449,8 +2717,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90);
+ prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
@@ -2460,8 +2727,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180);
+ prepareDisplay(DISPLAY_ORIENTATION_180);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
@@ -2471,8 +2737,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270);
+ prepareDisplay(DISPLAY_ORIENTATION_270);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
@@ -2496,8 +2761,8 @@
NotifyKeyArgs keyArgs;
// press BTN_LEFT, release BTN_LEFT
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
@@ -2512,8 +2777,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -2536,9 +2801,9 @@
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
@@ -2565,8 +2830,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
@@ -2581,16 +2846,16 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
@@ -2607,8 +2872,8 @@
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// press BTN_BACK, release BTN_BACK
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2627,8 +2892,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -2648,8 +2913,8 @@
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
// press BTN_SIDE, release BTN_SIDE
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -2668,8 +2933,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -2689,8 +2954,8 @@
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
// press BTN_FORWARD, release BTN_FORWARD
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2709,8 +2974,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -2730,8 +2995,8 @@
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
// press BTN_EXTRA, release BTN_EXTRA
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -2750,8 +3015,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -2782,9 +3047,9 @@
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -2811,9 +3076,9 @@
NotifyMotionArgs args;
// Move.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -2822,8 +3087,8 @@
ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f));
// Button press.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -2836,8 +3101,8 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Button release.
- process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME + 2, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
@@ -2850,9 +3115,9 @@
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Another move.
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 30);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 40);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 30);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 40);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -2871,9 +3136,9 @@
ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -2882,6 +3147,30 @@
ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
}
+TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+ CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+ addMapperAndConfigure(mapper);
+
+ // Setup PointerController for second display.
+ constexpr int32_t SECOND_DISPLAY_ID = 1;
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+ mFakePointerController->setButtonState(0);
+ mFakePointerController->setDisplayId(SECOND_DISPLAY_ID);
+
+ NotifyMotionArgs args;
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+ 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_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
// --- TouchInputMapperTest ---
@@ -2917,6 +3206,9 @@
static const VirtualKeyDefinition VIRTUAL_KEYS[2];
+ const std::string UNIQUE_ID = "local:0";
+ const std::string SECONDARY_UNIQUE_ID = "local:1";
+
enum Axes {
POSITION = 1 << 0,
TOUCH = 1 << 1,
@@ -2931,7 +3223,8 @@
TOOL_TYPE = 1 << 10,
};
- void prepareDisplay(int32_t orientation);
+ void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+ void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
void prepareVirtualDisplay(int32_t orientation);
void prepareVirtualKeys();
void prepareLocationCalibration();
@@ -2984,13 +3277,20 @@
{ KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
};
-void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
+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);
+}
+
+void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
+ setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
}
void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
- setVirtualDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
- VIRTUAL_DISPLAY_HEIGHT, orientation, String8(VIRTUAL_DISPLAY_UNIQUE_ID));
+ setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
+ VIRTUAL_DISPLAY_HEIGHT, orientation,
+ VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
}
void TouchInputMapperTest::prepareVirtualKeys() {
@@ -3089,48 +3389,48 @@
}
void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 1);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, y);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
}
void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, y);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
}
void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0);
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
}
void SingleTouchInputMapperTest::processPressure(
SingleTouchInputMapper* mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_PRESSURE, pressure);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
}
void SingleTouchInputMapperTest::processToolMajor(
SingleTouchInputMapper* mapper, int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
}
void SingleTouchInputMapperTest::processDistance(
SingleTouchInputMapper* mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_DISTANCE, distance);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
}
void SingleTouchInputMapperTest::processTilt(
SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TILT_X, tiltX);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TILT_Y, tiltY);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY);
}
void SingleTouchInputMapperTest::processKey(
SingleTouchInputMapper* mapper, int32_t code, int32_t value) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
+ process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
}
void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
}
@@ -3719,6 +4019,7 @@
NotifyMotionArgs args;
// Rotation 0.
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_0);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -3732,6 +4033,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
// Rotation 90.
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_90);
processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
processSync(mapper);
@@ -3745,6 +4047,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
// Rotation 180.
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_180);
processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -3758,6 +4061,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
// Rotation 270.
+ clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_270);
processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -4132,7 +4436,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
- // airbrush
+ // air-brush
processKey(mapper, BTN_TOOL_PENCIL, 0);
processKey(mapper, BTN_TOOL_AIRBRUSH, 1);
processSync(mapper);
@@ -4441,75 +4745,75 @@
void MultiTouchInputMapperTest::processPosition(
MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_X, x);
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_Y, y);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
}
void MultiTouchInputMapperTest::processTouchMajor(
MultiTouchInputMapper* mapper, int32_t touchMajor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
}
void MultiTouchInputMapperTest::processTouchMinor(
MultiTouchInputMapper* mapper, int32_t touchMinor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
}
void MultiTouchInputMapperTest::processToolMajor(
MultiTouchInputMapper* mapper, int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
}
void MultiTouchInputMapperTest::processToolMinor(
MultiTouchInputMapper* mapper, int32_t toolMinor) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
}
void MultiTouchInputMapperTest::processOrientation(
MultiTouchInputMapper* mapper, int32_t orientation) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_ORIENTATION, orientation);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
}
void MultiTouchInputMapperTest::processPressure(
MultiTouchInputMapper* mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, pressure);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
}
void MultiTouchInputMapperTest::processDistance(
MultiTouchInputMapper* mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_DISTANCE, distance);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
}
void MultiTouchInputMapperTest::processId(
MultiTouchInputMapper* mapper, int32_t id) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, id);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
}
void MultiTouchInputMapperTest::processSlot(
MultiTouchInputMapper* mapper, int32_t slot) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_SLOT, slot);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
}
void MultiTouchInputMapperTest::processToolType(
MultiTouchInputMapper* mapper, int32_t toolType) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+ process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
}
void MultiTouchInputMapperTest::processKey(
MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, value);
+ process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
}
void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_MSC, MSC_TIMESTAMP, value);
+ process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, value);
}
void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
}
void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
- process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
}
@@ -5639,7 +5943,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
- // airbrush
+ // air-brush
processKey(mapper, BTN_TOOL_PENCIL, 0);
processKey(mapper, BTN_TOOL_AIRBRUSH, 1);
processSync(mapper);
@@ -5944,5 +6248,257 @@
ASSERT_EQ(0U, args.deviceTimestamp);
}
+/**
+ * Set the input device port <--> display port associations, and check that the
+ * events are routed to the display that matches the display port.
+ * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ const std::string usb2 = "USB2";
+ const uint8_t hdmi1 = 0;
+ const uint8_t hdmi2 = 1;
+ const std::string secondaryUniqueId = "uniqueId2";
+ constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+ mFakePolicy->addInputPortAssociation(usb2, hdmi2);
+
+ // We are intentionally not adding the viewport for display 1 yet. Since the port association
+ // for this input device is specified, and the matching viewport is not present,
+ // the input device should be disabled (at the mapper level).
+
+ // Add viewport for display 2 on hdmi2
+ prepareSecondaryDisplay(type, hdmi2);
+ // Send a touch event
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Add viewport for display 1 on hdmi1
+ prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ // Send a touch event again
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(DISPLAY_ID, args.displayId);
+}
+
+/**
+ * Expect fallback to internal viewport if device is external and external viewport is not present.
+ */
+TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ mDevice->setExternal(true);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+
+ NotifyMotionArgs motionArgs;
+
+ // Expect the event to be sent to the internal viewport,
+ // because an external viewport is not present.
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ 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);
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
+ // Setup PointerController for second display.
+ sp<FakePointerController> fakePointerController = new FakePointerController();
+ fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ fakePointerController->setPosition(100, 200);
+ fakePointerController->setButtonState(0);
+ fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ // Check source is mouse that would obtain the PointerController.
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+
+ NotifyMotionArgs motionArgs;
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+ ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
+ // Setup the first touch screen device.
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION | ID | SLOT);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addMapperAndConfigure(mapper);
+
+ // Create the second touch screen device, and enable multi fingers.
+ const std::string USB2 = "USB2";
+ const int32_t SECOND_DEVICE_ID = 2;
+ InputDeviceIdentifier identifier;
+ identifier.name = DEVICE_NAME;
+ identifier.location = USB2;
+ InputDevice* device2 = new InputDevice(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+ DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+ mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
+ mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
+ 0 /*flat*/, 0 /*fuzz*/);
+ mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
+ 0 /*flat*/, 0 /*fuzz*/);
+ mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX,
+ 0 /*flat*/, 0 /*fuzz*/);
+ mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX,
+ 0 /*flat*/, 0 /*fuzz*/);
+ mFakeEventHub->setAbsoluteAxisValue(SECOND_DEVICE_ID, ABS_MT_SLOT, 0 /*value*/);
+ mFakeEventHub->addConfigurationProperty(SECOND_DEVICE_ID, String8("touch.deviceType"),
+ String8("touchScreen"));
+
+ // Setup the second touch screen device.
+ MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2);
+ device2->addMapper(mapper2);
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+ device2->reset(ARBITRARY_TIME);
+
+ // Setup PointerController.
+ sp<FakePointerController> fakePointerController = new FakePointerController();
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController);
+
+ // Setup policy for associated displays and show touches.
+ const uint8_t hdmi1 = 0;
+ const uint8_t hdmi2 = 1;
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+ mFakePolicy->addInputPortAssociation(USB2, hdmi2);
+ mFakePolicy->setShowTouches(true);
+
+ // Create displays.
+ prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL, hdmi2);
+
+ // Default device will reconfigure above, need additional reconfiguration for another device.
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Two fingers down at default display.
+ int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
+ processPosition(mapper, x1, y1);
+ processId(mapper, 1);
+ processSlot(mapper, 1);
+ processPosition(mapper, x2, y2);
+ processId(mapper, 2);
+ processSync(mapper);
+
+ std::map<int32_t, std::vector<int32_t>>::const_iterator iter =
+ fakePointerController->getSpots().find(DISPLAY_ID);
+ ASSERT_TRUE(iter != fakePointerController->getSpots().end());
+ ASSERT_EQ(size_t(2), iter->second.size());
+
+ // Two fingers down at second display.
+ processPosition(mapper2, x1, y1);
+ processId(mapper2, 1);
+ processSlot(mapper2, 1);
+ processPosition(mapper2, x2, y2);
+ processId(mapper2, 2);
+ processSync(mapper2);
+
+ iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID);
+ ASSERT_TRUE(iter != fakePointerController->getSpots().end());
+ ASSERT_EQ(size_t(2), iter->second.size());
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ addMapperAndConfigure(mapper);
+
+ NotifyMotionArgs motionArgs;
+ // Unrotated video frame
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+ std::vector<TouchVideoFrame> frames{frame};
+ mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(frames, motionArgs.videoFrames);
+
+ // Subsequent touch events should not have any videoframes
+ // This is implemented separately in FakeEventHub,
+ // but that should match the behaviour of TouchVideoDevice.
+ processPosition(mapper, 200, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames);
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addMapperAndConfigure(mapper);
+ // Unrotated video frame
+ TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+ NotifyMotionArgs motionArgs;
+
+ // Test all 4 orientations
+ for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
+ DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
+ clearViewports();
+ prepareDisplay(orientation);
+ std::vector<TouchVideoFrame> frames{frame};
+ mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ frames[0].rotate(orientation);
+ ASSERT_EQ(frames, motionArgs.videoFrames);
+ }
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addMapperAndConfigure(mapper);
+ // Unrotated video frames. There's no rule that they must all have the same dimensions,
+ // so mix these.
+ TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+ TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3});
+ TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4});
+ std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
+ NotifyMotionArgs motionArgs;
+
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+ mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ std::for_each(frames.begin(), frames.end(),
+ [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); });
+ ASSERT_EQ(frames, motionArgs.videoFrames);
+}
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
new file mode 100644
index 0000000..3ee33f1
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "TestInputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+TestInputListener::TestInputListener() { }
+
+TestInputListener::~TestInputListener() { }
+
+void TestInputListener::assertNotifyConfigurationChangedWasCalled(
+ NotifyConfigurationChangedArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
+ << "Expected notifyConfigurationChanged() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
+ }
+ mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
+ ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
+ << "Expected notifyConfigurationChanged() to not have been called.";
+}
+
+void TestInputListener::assertNotifyDeviceResetWasCalled(
+ NotifyDeviceResetArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
+ << "Expected notifyDeviceReset() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
+ }
+ mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
+ ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
+ << "Expected notifyDeviceReset() to not have been called.";
+}
+
+void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyKeyArgsQueue.begin();
+ }
+ mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyKeyWasNotCalled() {
+ ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+}
+
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifyMotionArgsQueue.begin();
+ }
+ mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
+}
+
+void TestInputListener::assertNotifyMotionWasNotCalled() {
+ ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
+ << "Expected notifyMotion() to not have been called.";
+}
+
+void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
+ ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
+ << "Expected notifySwitch() to have been called.";
+ if (outEventArgs) {
+ *outEventArgs = *mNotifySwitchArgsQueue.begin();
+ }
+ mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+}
+
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+ mNotifyConfigurationChangedArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+ mNotifyDeviceResetArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
+ mNotifyKeyArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
+ mNotifyMotionArgsQueue.push_back(*args);
+}
+
+void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
+ mNotifySwitchArgsQueue.push_back(*args);
+ }
+
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
new file mode 100644
index 0000000..085d343
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_TEST_INPUT_LISTENER_H
+#define _UI_TEST_INPUT_LISTENER_H
+
+#include <gtest/gtest.h>
+#include "InputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+class TestInputListener : public InputListenerInterface {
+private:
+ std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
+ std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
+ std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
+ std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
+ std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;
+
+protected:
+ virtual ~TestInputListener();
+
+public:
+ TestInputListener();
+
+ void assertNotifyConfigurationChangedWasCalled(
+ NotifyConfigurationChangedArgs* outEventArgs = nullptr);
+
+ void assertNotifyConfigurationChangedWasNotCalled();
+
+ void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr);
+
+ void assertNotifyDeviceResetWasNotCalled();
+
+ void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
+
+ void assertNotifyKeyWasNotCalled();
+
+ void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+
+ void assertNotifyMotionWasNotCalled();
+
+ void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
+
+private:
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+
+ virtual void notifyKey(const NotifyKeyArgs* args);
+
+ virtual void notifyMotion(const NotifyMotionArgs* args);
+
+ virtual void notifySwitch(const NotifySwitchArgs* args);
+};
+
+} // namespace android
+#endif
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index a7f3a52..33a2747 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -41,18 +41,21 @@
"liblog",
"libbinder",
"libsensor",
+ "libsensorprivacy",
"libcrypto",
"libbase",
"libhidlbase",
"libhidltransport",
"libhwbinder",
+ "libfmq",
"android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
],
static_libs: ["android.hardware.sensors@1.0-convert"],
- // our public headers depend on libsensor
- export_shared_lib_headers: ["libsensor"],
+ // our public headers depend on libsensor and libsensorprivacy
+ export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
}
cc_binary {
@@ -62,6 +65,7 @@
shared_libs: [
"libsensorservice",
+ "libsensorprivacy",
"libbinder",
"libutils",
],
diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp
index d8e5b29..14f9a12 100644
--- a/services/sensorservice/BatteryService.cpp
+++ b/services/sensorservice/BatteryService.cpp
@@ -94,7 +94,7 @@
bool BatteryService::checkService() {
if (mBatteryStatService == nullptr) {
const sp<IServiceManager> sm(defaultServiceManager());
- if (sm != NULL) {
+ if (sm != nullptr) {
const String16 name("batterystats");
mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
}
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 1025a88..207b097 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -26,24 +26,32 @@
namespace {
constexpr size_t LOG_SIZE = 10;
+ constexpr size_t LOG_SIZE_MED = 30; // debugging for slower sensors
constexpr size_t LOG_SIZE_LARGE = 50; // larger samples for debugging
}// unnamed namespace
RecentEventLogger::RecentEventLogger(int sensorType) :
mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)),
- mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) {
+ mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false),
+ mIsLastEventCurrent(false) {
// blank
}
void RecentEventLogger::addEvent(const sensors_event_t& event) {
std::lock_guard<std::mutex> lk(mLock);
mRecentEvents.emplace(event);
+ mIsLastEventCurrent = true;
}
bool RecentEventLogger::isEmpty() const {
return mRecentEvents.size() == 0;
}
+void RecentEventLogger::setLastEventStale() {
+ std::lock_guard<std::mutex> lk(mLock);
+ mIsLastEventCurrent = false;
+}
+
std::string RecentEventLogger::dump() const {
std::lock_guard<std::mutex> lk(mLock);
@@ -84,10 +92,10 @@
}
}
-bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const {
+bool RecentEventLogger::populateLastEventIfCurrent(sensors_event_t *event) const {
std::lock_guard<std::mutex> lk(mLock);
- if (mRecentEvents.size()) {
+ if (mIsLastEventCurrent && mRecentEvents.size()) {
// Index 0 contains the latest event emplace()'ed
*event = mRecentEvents[0].mEvent;
return true;
@@ -98,10 +106,16 @@
size_t RecentEventLogger::logSizeBySensorType(int sensorType) {
- return (sensorType == SENSOR_TYPE_STEP_COUNTER ||
- sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
- sensorType == SENSOR_TYPE_ACCELEROMETER ||
- sensorType == SENSOR_TYPE_LIGHT) ? LOG_SIZE_LARGE : LOG_SIZE;
+ if (sensorType == SENSOR_TYPE_STEP_COUNTER ||
+ sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
+ sensorType == SENSOR_TYPE_ACCELEROMETER ||
+ sensorType == SENSOR_TYPE_LIGHT) {
+ return LOG_SIZE_LARGE;
+ }
+ if (sensorType == SENSOR_TYPE_PROXIMITY) {
+ return LOG_SIZE_MED;
+ }
+ return LOG_SIZE;
}
RecentEventLogger::SensorEventLog::SensorEventLog(const sensors_event_t& e) : mEvent(e) {
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index bf1f655..67378b7 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -37,8 +37,13 @@
public:
explicit RecentEventLogger(int sensorType);
void addEvent(const sensors_event_t& event);
- bool populateLastEvent(sensors_event_t *event) const;
+
+ // Populate event with the last recorded sensor event if it is not stale. An event is
+ // considered stale if the sensor has become deactivated since the event was recorded.
+ // returns true on success, false if no recent event is available or the last event is stale
+ bool populateLastEventIfCurrent(sensors_event_t *event) const;
bool isEmpty() const;
+ void setLastEventStale();
virtual ~RecentEventLogger() {}
// Dumpable interface
@@ -59,6 +64,7 @@
RingBuffer<SensorEventLog> mRecentEvents;
bool mMaskData;
+ bool mIsLastEventCurrent;
private:
static size_t logSizeBySensorType(int sensorType);
diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp
index 7b00f4d..f2ea02e 100644
--- a/services/sensorservice/RotationVectorSensor.cpp
+++ b/services/sensorservice/RotationVectorSensor.cpp
@@ -94,7 +94,7 @@
return "GeoMag Rotation Vector Sensor";
default:
assert(0);
- return NULL;
+ return nullptr;
}
}
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 115a983..189ae36 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include "SensorDevice.h"
+
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+#include "android/hardware/sensors/2.0/types.h"
#include "SensorService.h"
#include <android-base/logging.h>
@@ -26,9 +30,14 @@
#include <cinttypes>
#include <thread>
+using namespace android::hardware::sensors;
using namespace android::hardware::sensors::V1_0;
using namespace android::hardware::sensors::V1_0::implementation;
+using android::hardware::sensors::V2_0::ISensorsCallback;
+using android::hardware::sensors::V2_0::EventQueueFlagBits;
+using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
using android::hardware::hidl_vec;
+using android::hardware::Return;
using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
namespace android {
@@ -51,12 +60,55 @@
}
}
+template<typename EnumType>
+constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+ return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+// Used internally by the framework to wake the Event FMQ. These values must start after
+// the last value of EventQueueFlagBits
+enum EventQueueFlagBitsInternal : uint32_t {
+ INTERNAL_WAKE = 1 << 16,
+};
+
+void SensorsHalDeathReceivier::serviceDied(
+ uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
+ ALOGW("Sensors HAL died, attempting to reconnect.");
+ SensorDevice::getInstance().prepareForReconnect();
+}
+
+struct SensorsCallback : public ISensorsCallback {
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<SensorInfo> &dynamicSensorsAdded) override {
+ return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded);
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override {
+ return SensorDevice::getInstance().onDynamicSensorsDisconnected(
+ dynamicSensorHandlesRemoved);
+ }
+};
+
SensorDevice::SensorDevice()
- : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) {
+ : mHidlTransportErrors(20),
+ mRestartWaiter(new HidlServiceRegistrationWaiter()),
+ mEventQueueFlag(nullptr),
+ mWakeLockQueueFlag(nullptr),
+ mReconnecting(false) {
if (!connectHidlService()) {
return;
}
+ initializeSensorList();
+
+ mIsDirectReportSupported =
+ (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+}
+
+void SensorDevice::initializeSensorList() {
float minPowerMa = 0.001; // 1 microAmp
checkReturn(mSensors->getSensorsList(
@@ -81,37 +133,208 @@
checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
}
}));
+}
- mIsDirectReportSupported =
- (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+SensorDevice::~SensorDevice() {
+ if (mEventQueueFlag != nullptr) {
+ hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+ mEventQueueFlag = nullptr;
+ }
+
+ if (mWakeLockQueueFlag != nullptr) {
+ hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+ mWakeLockQueueFlag = nullptr;
+ }
}
bool SensorDevice::connectHidlService() {
+ HalConnectionStatus status = connectHidlServiceV2_0();
+ if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+ status = connectHidlServiceV1_0();
+ }
+ return (status == HalConnectionStatus::CONNECTED);
+}
+
+SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() {
// SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
size_t retry = 10;
+ HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
while (retry-- > 0) {
- mSensors = ISensors::getService();
- if (mSensors == nullptr) {
+ sp<V1_0::ISensors> sensors = V1_0::ISensors::getService();
+ if (sensors == nullptr) {
// no sensor hidl service found
+ connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
break;
}
+ mSensors = new SensorServiceUtil::SensorsWrapperV1_0(sensors);
mRestartWaiter->reset();
// Poke ISensor service. If it has lingering connection from previous generation of
// system server, it will kill itself. There is no intention to handle the poll result,
// which will be done since the size is 0.
if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
// ok to continue
+ connectionStatus = HalConnectionStatus::CONNECTED;
break;
}
// hidl service is restarting, pointer is invalid.
mSensors = nullptr;
+ connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
mRestartWaiter->wait();
}
- return (mSensors != nullptr);
+
+ return connectionStatus;
+}
+
+SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() {
+ HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+ sp<V2_0::ISensors> sensors = V2_0::ISensors::getService();
+
+ if (sensors == nullptr) {
+ connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+ } else {
+ mSensors = new SensorServiceUtil::SensorsWrapperV2_0(sensors);
+
+ mEventQueue = std::make_unique<EventMessageQueue>(
+ SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+ true /* configureEventFlagWord */);
+
+ mWakeLockQueue = std::make_unique<WakeLockQueue>(
+ SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+ true /* configureEventFlagWord */);
+
+ hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+ hardware::EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag);
+
+ hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+ hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
+ &mWakeLockQueueFlag);
+
+ CHECK(mSensors != nullptr && mEventQueue != nullptr &&
+ mWakeLockQueue != nullptr && mEventQueueFlag != nullptr &&
+ mWakeLockQueueFlag != nullptr);
+
+ status_t status = StatusFromResult(checkReturn(mSensors->initialize(
+ *mEventQueue->getDesc(),
+ *mWakeLockQueue->getDesc(),
+ new SensorsCallback())));
+
+ if (status != NO_ERROR) {
+ connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+ ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
+ } else {
+ connectionStatus = HalConnectionStatus::CONNECTED;
+ mSensorsHalDeathReceiver = new SensorsHalDeathReceivier();
+ sensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
+ }
+ }
+
+ return connectionStatus;
+}
+
+void SensorDevice::prepareForReconnect() {
+ mReconnecting = true;
+
+ // Wake up the polling thread so it returns and allows the SensorService to initiate
+ // a reconnect.
+ mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+}
+
+void SensorDevice::reconnect() {
+ Mutex::Autolock _l(mLock);
+ mSensors = nullptr;
+
+ auto previousActivations = mActivationCount;
+ auto previousSensorList = mSensorList;
+
+ mActivationCount.clear();
+ mSensorList.clear();
+
+ if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) {
+ initializeSensorList();
+
+ if (sensorHandlesChanged(previousSensorList, mSensorList)) {
+ LOG_ALWAYS_FATAL("Sensor handles changed, cannot re-enable sensors.");
+ } else {
+ reactivateSensors(previousActivations);
+ }
+ }
+ mReconnecting = false;
+}
+
+bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
+ const Vector<sensor_t>& newSensorList) {
+ bool didChange = false;
+
+ if (oldSensorList.size() != newSensorList.size()) {
+ didChange = true;
+ }
+
+ for (size_t i = 0; i < newSensorList.size() && !didChange; i++) {
+ bool found = false;
+ const sensor_t& newSensor = newSensorList[i];
+ for (size_t j = 0; j < oldSensorList.size() && !found; j++) {
+ const sensor_t& prevSensor = oldSensorList[j];
+ if (prevSensor.handle == newSensor.handle) {
+ found = true;
+ if (!sensorIsEquivalent(prevSensor, newSensor)) {
+ didChange = true;
+ }
+ }
+ }
+
+ if (!found) {
+ // Could not find the new sensor in the old list of sensors, the lists must
+ // have changed.
+ didChange = true;
+ }
+ }
+ return didChange;
+}
+
+bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) {
+ bool equivalent = true;
+ if (prevSensor.handle != newSensor.handle ||
+ (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
+ (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
+ (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
+ (prevSensor.version != newSensor.version) ||
+ (prevSensor.type != newSensor.type) ||
+ (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
+ (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
+ (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
+ (prevSensor.minDelay != newSensor.minDelay) ||
+ (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
+ (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
+ (prevSensor.maxDelay != newSensor.maxDelay) ||
+ (prevSensor.flags != newSensor.flags)) {
+ equivalent = false;
+ }
+ return equivalent;
+}
+
+void SensorDevice::reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations) {
+ for (size_t i = 0; i < mSensorList.size(); i++) {
+ int handle = mSensorList[i].handle;
+ ssize_t activationIndex = previousActivations.indexOfKey(handle);
+ if (activationIndex < 0 || previousActivations[activationIndex].numActiveClients() <= 0) {
+ continue;
+ }
+
+ const Info& info = previousActivations[activationIndex];
+ for (size_t j = 0; j < info.batchParams.size(); j++) {
+ const BatchParams& batchParams = info.batchParams[j];
+ status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */,
+ batchParams.mTSample, batchParams.mTBatch);
+
+ if (res == NO_ERROR) {
+ activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */);
+ }
+ }
+ }
}
void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
@@ -173,6 +396,19 @@
ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
if (mSensors == nullptr) return NO_INIT;
+ ssize_t eventsRead = 0;
+ if (mSensors->supportsMessageQueues()) {
+ eventsRead = pollFmq(buffer, count);
+ } else if (mSensors->supportsPolling()) {
+ eventsRead = pollHal(buffer, count);
+ } else {
+ ALOGE("Must support polling or FMQ");
+ eventsRead = -1;
+ }
+ return eventsRead;
+}
+
+ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) {
ssize_t err;
int numHidlTransportErrors = 0;
bool hidlTransportError = false;
@@ -208,7 +444,7 @@
if(numHidlTransportErrors > 0) {
ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
- HidlTransportErrorLog errLog(time(NULL), numHidlTransportErrors);
+ HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
mHidlTransportErrors.add(errLog);
mTotalHidlTransportErrors++;
}
@@ -216,6 +452,89 @@
return err;
}
+ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
+ ssize_t eventsRead = 0;
+ size_t availableEvents = mEventQueue->availableToRead();
+
+ if (availableEvents == 0) {
+ uint32_t eventFlagState = 0;
+
+ // Wait for events to become available. This is necessary so that the Event FMQ's read() is
+ // able to be called with the correct number of events to read. If the specified number of
+ // events is not available, then read() would return no events, possibly introducing
+ // additional latency in delivering events to applications.
+ mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
+ asBaseType(INTERNAL_WAKE), &eventFlagState);
+ availableEvents = mEventQueue->availableToRead();
+
+ if ((eventFlagState & asBaseType(EventQueueFlagBits::READ_AND_PROCESS)) &&
+ availableEvents == 0) {
+ ALOGW("Event FMQ wake without any events");
+ }
+
+ if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
+ ALOGD("Event FMQ internal wake, returning from poll with no events");
+ return DEAD_OBJECT;
+ }
+ }
+
+ size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
+ if (eventsToRead > 0) {
+ if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) {
+ // Notify the Sensors HAL that sensor events have been read. This is required to support
+ // the use of writeBlocking by the Sensors HAL.
+ mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
+
+ for (size_t i = 0; i < eventsToRead; i++) {
+ convertToSensorEvent(mEventBuffer[i], &buffer[i]);
+ }
+ eventsRead = eventsToRead;
+ } else {
+ ALOGW("Failed to read %zu events, currently %zu events available",
+ eventsToRead, availableEvents);
+ }
+ }
+
+ return eventsRead;
+}
+
+Return<void> SensorDevice::onDynamicSensorsConnected(
+ const hidl_vec<SensorInfo> &dynamicSensorsAdded) {
+ // Allocate a sensor_t structure for each dynamic sensor added and insert
+ // it into the dictionary of connected dynamic sensors keyed by handle.
+ for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
+ const SensorInfo &info = dynamicSensorsAdded[i];
+
+ auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+ CHECK(it == mConnectedDynamicSensors.end());
+
+ sensor_t *sensor = new sensor_t();
+ convertToSensor(info, sensor);
+
+ mConnectedDynamicSensors.insert(
+ std::make_pair(sensor->handle, sensor));
+ }
+
+ return Return<void>();
+}
+
+Return<void> SensorDevice::onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) {
+ (void) dynamicSensorHandlesRemoved;
+ // TODO: Currently dynamic sensors do not seem to be removed
+ return Return<void>();
+}
+
+void SensorDevice::writeWakeLockHandled(uint32_t count) {
+ if (mSensors != nullptr && mSensors->supportsMessageQueues()) {
+ if (mWakeLockQueue->write(&count)) {
+ mWakeLockQueueFlag->wake(asBaseType(WakeLockQueueFlagBits::DATA_WRITTEN));
+ } else {
+ ALOGW("Failed to write wake lock handled");
+ }
+ }
+}
+
void SensorDevice::autoDisable(void *ident, int handle) {
Mutex::Autolock _l(mLock);
ssize_t activationIndex = mActivationCount.indexOfKey(handle);
@@ -230,10 +549,15 @@
status_t SensorDevice::activate(void* ident, int handle, int enabled) {
if (mSensors == nullptr) return NO_INIT;
- status_t err(NO_ERROR);
+ Mutex::Autolock _l(mLock);
+ return activateLocked(ident, handle, enabled);
+}
+
+status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) {
bool actuateHardware = false;
- Mutex::Autolock _l(mLock);
+ status_t err(NO_ERROR);
+
ssize_t activationIndex = mActivationCount.indexOfKey(handle);
if (activationIndex < 0) {
ALOGW("Handle %d cannot be found in activation record", handle);
@@ -333,6 +657,11 @@
ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
Mutex::Autolock _l(mLock);
+ return batchLocked(ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+}
+
+status_t SensorDevice::batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
ssize_t activationIndex = mActivationCount.indexOfKey(handle);
if (activationIndex < 0) {
ALOGW("Handle %d cannot be found in activation record", handle);
@@ -563,7 +892,7 @@
// ---------------------------------------------------------------------------
-int SensorDevice::Info::numActiveClients() {
+int SensorDevice::Info::numActiveClients() const {
SensorDevice& device(SensorDevice::getInstance());
int num = 0;
for (size_t i = 0; i < batchParams.size(); ++i) {
@@ -651,19 +980,9 @@
const hidl_vec<Event> &src,
const hidl_vec<SensorInfo> &dynamicSensorsAdded,
sensors_event_t *dst) {
- // Allocate a sensor_t structure for each dynamic sensor added and insert
- // it into the dictionary of connected dynamic sensors keyed by handle.
- for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
- const SensorInfo &info = dynamicSensorsAdded[i];
- auto it = mConnectedDynamicSensors.find(info.sensorHandle);
- CHECK(it == mConnectedDynamicSensors.end());
-
- sensor_t *sensor = new sensor_t;
- convertToSensor(info, sensor);
-
- mConnectedDynamicSensors.insert(
- std::make_pair(sensor->handle, sensor));
+ if (dynamicSensorsAdded.size() > 0) {
+ onDynamicSensorsConnected(dynamicSensorsAdded);
}
for (size_t i = 0; i < src.size(); ++i) {
@@ -672,8 +991,12 @@
}
void SensorDevice::handleHidlDeath(const std::string & detail) {
- // restart is the only option at present.
- LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+ if (!SensorDevice::getInstance().mSensors->supportsMessageQueues()) {
+ // restart is the only option at present.
+ LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+ } else {
+ ALOGD("ISensors HAL died, death recipient will attempt reconnect");
+ }
}
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 6d75051..2a69654 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -19,20 +19,22 @@
#include "SensorDeviceUtils.h"
#include "SensorServiceUtils.h"
+#include "SensorsWrapper.h"
+#include <fmq/MessageQueue.h>
+#include <sensor/SensorEventQueue.h>
#include <sensor/Sensor.h>
#include <stdint.h>
#include <sys/types.h>
#include <utils/KeyedVector.h>
#include <utils/Singleton.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <string>
#include <unordered_map>
#include <algorithm> //std::max std::min
-#include "android/hardware/sensors/1.0/ISensors.h"
-
#include "RingBuffer.h"
// ---------------------------------------------------------------------------
@@ -40,8 +42,13 @@
namespace android {
// ---------------------------------------------------------------------------
+class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& service) override;
+};
-class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable {
+class SensorDevice : public Singleton<SensorDevice>,
+ public SensorServiceUtil::Dumpable {
public:
class HidlTransportErrorLog {
public:
@@ -69,6 +76,10 @@
int mCount; // number of transport errors observed
};
+ ~SensorDevice();
+ void prepareForReconnect();
+ void reconnect();
+
ssize_t getSensorList(sensor_t const** list);
void handleDynamicSensorConnection(int handle, bool connected);
@@ -76,6 +87,7 @@
int getHalDeviceVersion() const;
ssize_t poll(sensors_event_t* buffer, size_t count);
+ void writeWakeLockHandled(uint32_t count);
status_t activate(void* ident, int handle, int enabled);
status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
@@ -98,12 +110,22 @@
status_t injectSensorData(const sensors_event_t *event);
void notifyConnectionDestroyed(void *ident);
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ hardware::Return<void> onDynamicSensorsConnected(
+ const hardware::hidl_vec<hardware::sensors::V1_0::SensorInfo> &dynamicSensorsAdded);
+ hardware::Return<void> onDynamicSensorsDisconnected(
+ const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved);
+
+ bool isReconnecting() const {
+ return mReconnecting;
+ }
+
// Dumpable
virtual std::string dump() const;
private:
friend class Singleton<SensorDevice>;
- sp<hardware::sensors::V1_0::ISensors> mSensors;
+ sp<SensorServiceUtil::ISensorsWrapper> mSensors;
Vector<sensor_t> mSensorList;
std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
@@ -151,7 +173,7 @@
// the removed ident. If index >=0, ident is present and successfully removed.
ssize_t removeBatchParamsForIdent(void* ident);
- int numActiveClients();
+ int numActiveClients() const;
};
DefaultKeyedVector<int, Info> mActivationCount;
@@ -163,6 +185,26 @@
SortedVector<void *> mDisabledClients;
SensorDevice();
bool connectHidlService();
+ void initializeSensorList();
+ void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations);
+ static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
+ const Vector<sensor_t>& newSensorList);
+ static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor);
+
+ enum HalConnectionStatus {
+ CONNECTED, // Successfully connected to the HAL
+ DOES_NOT_EXIST, // Could not find the HAL
+ FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
+ UNKNOWN,
+ };
+ HalConnectionStatus connectHidlServiceV1_0();
+ HalConnectionStatus connectHidlServiceV2_0();
+
+ ssize_t pollHal(sensors_event_t* buffer, size_t count);
+ ssize_t pollFmq(sensors_event_t* buffer, size_t count);
+ status_t activateLocked(void* ident, int handle, int enabled);
+ status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs);
static void handleHidlDeath(const std::string &detail);
template<typename T>
@@ -189,6 +231,19 @@
sensors_event_t *dst);
bool mIsDirectReportSupported;
+
+ typedef hardware::MessageQueue<Event, hardware::kSynchronizedReadWrite> EventMessageQueue;
+ typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
+ std::unique_ptr<EventMessageQueue> mEventQueue;
+ std::unique_ptr<WakeLockQueue> mWakeLockQueue;
+
+ hardware::EventFlag* mEventQueueFlag;
+ hardware::EventFlag* mWakeLockQueueFlag;
+
+ std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
+
+ sp<SensorsHalDeathReceivier> mSensorsHalDeathReceiver;
+ std::atomic_bool mReconnecting;
};
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 538d728..cd0ea5d 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -100,7 +100,7 @@
return NO_ERROR;
}
- if (mService->isOperationRestricted(mOpPackageName)) {
+ if (!mService->isOperationPermitted(mOpPackageName)) {
return PERMISSION_DENIED;
}
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 956844f..b66cbcf 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -31,9 +31,10 @@
const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
const String16& opPackageName, bool hasSensorAccess)
: mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
- mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(NULL),
- mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName), mOpPackageName(opPackageName),
- mDestroyed(false), mHasSensorAccess(hasSensorAccess) {
+ mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
+ mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
+ mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false),
+ mHasSensorAccess(hasSensorAccess) {
mChannel = new BitTube(mService->mSocketBufferSize);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@@ -55,8 +56,8 @@
}
mService->cleanupConnection(this);
- if (mEventCache != NULL) {
- delete mEventCache;
+ if (mEventCache != nullptr) {
+ delete[] mEventCache;
}
mDestroyed = true;
}
@@ -200,7 +201,7 @@
// Add the file descriptor to the Looper for receiving acknowledegments if the app has
// registered for wake-up sensors OR for sending events in the cache.
- int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, NULL);
+ int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, nullptr);
if (ret == 1) {
ALOGD_IF(DEBUG_CONNECTIONS, "%p addFd fd=%d", this, mChannel->getSendFd());
mHasLooperCallbacks = true;
@@ -224,7 +225,7 @@
wp<const SensorEventConnection> const * mapFlushEventsToConnections) {
// filter out events not for this connection
- sensors_event_t* sanitizedBuffer = nullptr;
+ std::unique_ptr<sensors_event_t[]> sanitizedBuffer;
int count = 0;
Mutex::Autolock _l(mConnectionLock);
@@ -278,7 +279,7 @@
}
} else {
// Regular sensor event, just copy it to the scratch buffer.
- if (mHasSensorAccess) {
+ if (hasSensorAccess()) {
scratch[count++] = buffer[i];
}
}
@@ -289,11 +290,12 @@
buffer[i].meta_data.sensor == sensor_handle)));
}
} else {
- if (mHasSensorAccess) {
+ if (hasSensorAccess()) {
scratch = const_cast<sensors_event_t *>(buffer);
count = numEvents;
} else {
- scratch = sanitizedBuffer = new sensors_event_t[numEvents];
+ sanitizedBuffer.reset(new sensors_event_t[numEvents]);
+ scratch = sanitizedBuffer.get();
for (size_t i = 0; i < numEvents; i++) {
if (buffer[i].type == SENSOR_TYPE_META_DATA) {
scratch[count++] = buffer[i++];
@@ -305,7 +307,6 @@
sendPendingFlushEventsLocked();
// Early return if there are no events for this connection.
if (count == 0) {
- delete sanitizedBuffer;
return status_t(NO_ERROR);
}
@@ -315,39 +316,12 @@
if (mCacheSize != 0) {
// There are some events in the cache which need to be sent first. Copy this buffer to
// the end of cache.
- if (mCacheSize + count <= mMaxCacheSize) {
- memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
- mCacheSize += count;
- } else {
- // Check if any new sensors have registered on this connection which may have increased
- // the max cache size that is desired.
- if (mCacheSize + count < computeMaxCacheSizeLocked()) {
- reAllocateCacheLocked(scratch, count);
- delete sanitizedBuffer;
- return status_t(NO_ERROR);
- }
- // Some events need to be dropped.
- int remaningCacheSize = mMaxCacheSize - mCacheSize;
- if (remaningCacheSize != 0) {
- memcpy(&mEventCache[mCacheSize], scratch,
- remaningCacheSize * sizeof(sensors_event_t));
- }
- int numEventsDropped = count - remaningCacheSize;
- countFlushCompleteEventsLocked(mEventCache, numEventsDropped);
- // Drop the first "numEventsDropped" in the cache.
- memmove(mEventCache, &mEventCache[numEventsDropped],
- (mCacheSize - numEventsDropped) * sizeof(sensors_event_t));
-
- // Copy the remainingEvents in scratch buffer to the end of cache.
- memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize,
- numEventsDropped * sizeof(sensors_event_t));
- }
- delete sanitizedBuffer;
+ appendEventsToCacheLocked(scratch, count);
return status_t(NO_ERROR);
}
int index_wake_up_event = -1;
- if (mHasSensorAccess) {
+ if (hasSensorAccess()) {
index_wake_up_event = findWakeUpSensorEventLocked(scratch, count);
if (index_wake_up_event >= 0) {
scratch[index_wake_up_event].flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
@@ -373,18 +347,17 @@
--mTotalAcksNeeded;
#endif
}
- if (mEventCache == NULL) {
+ if (mEventCache == nullptr) {
mMaxCacheSize = computeMaxCacheSizeLocked();
mEventCache = new sensors_event_t[mMaxCacheSize];
mCacheSize = 0;
}
- memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
- mCacheSize += count;
+ // Save the events so that they can be written later
+ appendEventsToCacheLocked(scratch, count);
// Add this file descriptor to the looper to get a callback when this fd is available for
// writing.
updateLooperRegistrationLocked(mService->getLooper());
- delete sanitizedBuffer;
return size;
}
@@ -394,7 +367,6 @@
}
#endif
- delete sanitizedBuffer;
return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
@@ -403,6 +375,10 @@
mHasSensorAccess = hasAccess;
}
+bool SensorService::SensorEventConnection::hasSensorAccess() {
+ return mHasSensorAccess && !mService->mSensorPrivacyPolicy->isSensorPrivacyEnabled();
+}
+
void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t const* scratch,
int count) {
sensors_event_t *eventCache_new;
@@ -415,12 +391,66 @@
ALOGD_IF(DEBUG_CONNECTIONS, "reAllocateCacheLocked maxCacheSize=%d %d", mMaxCacheSize,
new_cache_size);
- delete mEventCache;
+ delete[] mEventCache;
mEventCache = eventCache_new;
mCacheSize += count;
mMaxCacheSize = new_cache_size;
}
+void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_event_t const* events,
+ int count) {
+ if (count <= 0) {
+ return;
+ } else if (mCacheSize + count <= mMaxCacheSize) {
+ // The events fit within the current cache: add them
+ memcpy(&mEventCache[mCacheSize], events, count * sizeof(sensors_event_t));
+ mCacheSize += count;
+ } else if (mCacheSize + count <= computeMaxCacheSizeLocked()) {
+ // The events fit within a resized cache: resize the cache and add the events
+ reAllocateCacheLocked(events, count);
+ } else {
+ // The events do not fit within the cache: drop the oldest events.
+ int freeSpace = mMaxCacheSize - mCacheSize;
+
+ // Drop up to the currently cached number of events to make room for new events
+ int cachedEventsToDrop = std::min(mCacheSize, count - freeSpace);
+
+ // New events need to be dropped if there are more new events than the size of the cache
+ int newEventsToDrop = std::max(0, count - mMaxCacheSize);
+
+ // Determine the number of new events to copy into the cache
+ int eventsToCopy = std::min(mMaxCacheSize, count);
+
+ constexpr nsecs_t kMinimumTimeBetweenDropLogNs = 2 * 1000 * 1000 * 1000; // 2 sec
+ if (events[0].timestamp - mTimeOfLastEventDrop > kMinimumTimeBetweenDropLogNs) {
+ ALOGW("Dropping %d cached events (%d/%d) to save %d/%d new events. %d events previously"
+ " dropped", cachedEventsToDrop, mCacheSize, mMaxCacheSize, eventsToCopy,
+ count, mEventsDropped);
+ mEventsDropped = 0;
+ mTimeOfLastEventDrop = events[0].timestamp;
+ } else {
+ // Record the number dropped
+ mEventsDropped += cachedEventsToDrop + newEventsToDrop;
+ }
+
+ // Check for any flush complete events in the events that will be dropped
+ countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop);
+ countFlushCompleteEventsLocked(events, newEventsToDrop);
+
+ // Only shift the events if they will not all be overwritten
+ if (eventsToCopy != mMaxCacheSize) {
+ memmove(mEventCache, &mEventCache[cachedEventsToDrop],
+ (mCacheSize - cachedEventsToDrop) * sizeof(sensors_event_t));
+ }
+ mCacheSize -= cachedEventsToDrop;
+
+ // Copy the events into the cache
+ memcpy(&mEventCache[mCacheSize], &events[newEventsToDrop],
+ eventsToCopy * sizeof(sensors_event_t));
+ mCacheSize += eventsToCopy;
+ }
+}
+
void SensorService::SensorEventConnection::sendPendingFlushEventsLocked() {
ASensorEvent flushCompleteEvent;
memset(&flushCompleteEvent, 0, sizeof(flushCompleteEvent));
@@ -465,7 +495,7 @@
for (int numEventsSent = 0; numEventsSent < mCacheSize;) {
const int numEventsToWrite = helpers::min(mCacheSize - numEventsSent, maxWriteSize);
int index_wake_up_event = -1;
- if (mHasSensorAccess) {
+ if (hasSensorAccess()) {
index_wake_up_event =
findWakeUpSensorEventLocked(mEventCache + numEventsSent, numEventsToWrite);
if (index_wake_up_event >= 0) {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 032721e..7077880 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -53,7 +53,7 @@
bool hasSensorAccess);
status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch,
- wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL);
+ wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr);
bool hasSensor(int32_t handle) const;
bool hasAnySensor() const;
bool hasOneShotSensors() const;
@@ -108,6 +108,10 @@
// size, reallocate memory and copy over events from the older cache.
void reAllocateCacheLocked(sensors_event_t const* scratch, int count);
+ // Add the events to the cache. If the cache would be exceeded, drop events at the beginning of
+ // the cache.
+ void appendEventsToCacheLocked(sensors_event_t const* events, int count);
+
// LooperCallback method. If there is data to read on this fd, it is an ack from the app that it
// has read events from a wake up sensor, decrement mWakeLockRefCount. If this fd is available
// for writing send the data from the cache.
@@ -126,6 +130,10 @@
void updateLooperRegistration(const sp<Looper>& looper); void
updateLooperRegistrationLocked(const sp<Looper>& looper);
+ // Returns whether sensor access is available based on both the uid being active and sensor
+ // privacy not being enabled.
+ bool hasSensorAccess();
+
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
@@ -161,6 +169,8 @@
sensors_event_t *mEventCache;
int mCacheSize, mMaxCacheSize;
+ int64_t mTimeOfLastEventDrop;
+ int mEventsDropped;
String8 mPackageName;
const String16 mOpPackageName;
#if DEBUG_CONNECTIONS
diff --git a/services/sensorservice/SensorRecord.cpp b/services/sensorservice/SensorRecord.cpp
index 53fb9de..7c4c6a2 100644
--- a/services/sensorservice/SensorRecord.cpp
+++ b/services/sensorservice/SensorRecord.cpp
@@ -71,7 +71,7 @@
if (mPendingFlushConnections.size() > 0) {
return mPendingFlushConnections[0];
}
- return NULL;
+ return nullptr;
}
void SensorService::SensorRecord::clearAllPendingFlushConnections() {
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index bba8372..5411515 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -47,7 +47,7 @@
mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
- time_t rawtime = time(NULL);
+ time_t rawtime = time(nullptr);
struct tm * timeinfo = localtime(&rawtime);
mHour = static_cast<int8_t>(timeinfo->tm_hour);
mMin = static_cast<int8_t>(timeinfo->tm_min);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 8e9e7fd..3fbd61e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -29,6 +29,7 @@
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <sensor/SensorEventQueue.h>
+#include <sensorprivacy/SensorPrivacyManager.h>
#include <utils/SystemClock.h>
#include "BatteryService.h"
@@ -47,6 +48,7 @@
#include "SensorRecord.h"
#include "SensorRegistrationInfo.h"
+#include <ctime>
#include <inttypes.h>
#include <math.h>
#include <sched.h>
@@ -87,6 +89,7 @@
: mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
mWakeLockAcquired(false) {
mUidPolicy = new UidPolicy(this);
+ mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
}
bool SensorService::initializeHmacKey() {
@@ -250,7 +253,7 @@
// it to maxSystemSocketBufferSize if necessary.
FILE *fp = fopen("/proc/sys/net/core/wmem_max", "r");
char line[128];
- if (fp != NULL && fgets(line, sizeof(line), fp) != NULL) {
+ if (fp != nullptr && fgets(line, sizeof(line), fp) != nullptr) {
line[sizeof(line) - 1] = '\0';
size_t maxSystemSocketBufferSize;
sscanf(line, "%zu", &maxSystemSocketBufferSize);
@@ -285,6 +288,9 @@
// Start watching UID changes to apply policy.
mUidPolicy->registerSelf();
+
+ // Start watching sensor privacy changes
+ mSensorPrivacyPolicy->registerSelf();
}
}
}
@@ -295,7 +301,7 @@
{
Mutex::Autolock _l(mLock);
for (size_t i = 0 ; i < activeConnections.size(); i++) {
- if (activeConnections[i] != 0 && activeConnections[i]->getUid() == uid) {
+ if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) {
activeConnections[i]->setSensorAccess(hasAccess);
}
}
@@ -306,7 +312,7 @@
int handle = s->getSensor().getHandle();
int type = s->getSensor().getType();
if (mSensors.add(handle, s, isDebug, isVirtual)){
- mRecentEvent.emplace(handle, new RecentEventLogger(type));
+ mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
return s->getSensor();
} else {
return mSensors.getNonSensor();
@@ -337,6 +343,7 @@
delete entry.second;
}
mUidPolicy->unregisterSelf();
+ mSensorPrivacyPolicy->unregisterSelf();
}
status_t SensorService::dump(int fd, const Vector<String16>& args) {
@@ -363,35 +370,16 @@
}
mCurrentOperatingMode = RESTRICTED;
- // temporarily stop all sensor direct report
- for (auto &i : mDirectConnections) {
- sp<SensorDirectConnection> connection(i.promote());
- if (connection != nullptr) {
- connection->stopAll(true /* backupRecord */);
- }
- }
-
- dev.disableAllSensors();
- // Clear all pending flush connections for all active sensors. If one of the active
- // connections has called flush() and the underlying sensor has been disabled before a
- // flush complete event is returned, we need to remove the connection from this queue.
- for (size_t i=0 ; i< mActiveSensors.size(); ++i) {
- mActiveSensors.valueAt(i)->clearAllPendingFlushConnections();
- }
+ // temporarily stop all sensor direct report and disable sensors
+ disableAllSensorsLocked();
mWhiteListedPackage.setTo(String8(args[1]));
return status_t(NO_ERROR);
} else if (args.size() == 1 && args[0] == String16("enable")) {
// If currently in restricted mode, reset back to NORMAL mode else ignore.
if (mCurrentOperatingMode == RESTRICTED) {
mCurrentOperatingMode = NORMAL;
- dev.enableAllSensors();
- // recover all sensor direct report
- for (auto &i : mDirectConnections) {
- sp<SensorDirectConnection> connection(i.promote());
- if (connection != nullptr) {
- connection->recoverAll();
- }
- }
+ // enable sensors and recover all sensor direct report
+ enableAllSensorsLocked();
}
if (mCurrentOperatingMode == DATA_INJECTION) {
resetToNormalModeLocked();
@@ -423,6 +411,11 @@
} else {
// Default dump the sensor list and debugging information.
//
+ timespec curTime;
+ clock_gettime(CLOCK_REALTIME, &curTime);
+ struct tm* timeinfo = localtime(&(curTime.tv_sec));
+ result.appendFormat("Captured at: %02d:%02d:%02d.%03d\n", timeinfo->tm_hour,
+ timeinfo->tm_min, timeinfo->tm_sec, (int)ns2ms(curTime.tv_nsec));
result.append("Sensor Device:\n");
result.append(SensorDevice::getInstance().dump().c_str());
@@ -471,11 +464,13 @@
case DATA_INJECTION:
result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string());
}
+ result.appendFormat("Sensor Privacy: %s\n",
+ mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
result.appendFormat("%zd active connections\n", mActiveConnections.size());
for (size_t i=0 ; i < mActiveConnections.size() ; i++) {
sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != 0) {
+ if (connection != nullptr) {
result.appendFormat("Connection Number: %zu \n", i);
connection->dump(result);
}
@@ -513,6 +508,52 @@
return NO_ERROR;
}
+void SensorService::disableAllSensors() {
+ Mutex::Autolock _l(mLock);
+ disableAllSensorsLocked();
+}
+
+void SensorService::disableAllSensorsLocked() {
+ SensorDevice& dev(SensorDevice::getInstance());
+ for (auto &i : mDirectConnections) {
+ sp<SensorDirectConnection> connection(i.promote());
+ if (connection != nullptr) {
+ connection->stopAll(true /* backupRecord */);
+ }
+ }
+ dev.disableAllSensors();
+ // Clear all pending flush connections for all active sensors. If one of the active
+ // connections has called flush() and the underlying sensor has been disabled before a
+ // flush complete event is returned, we need to remove the connection from this queue.
+ for (size_t i=0 ; i< mActiveSensors.size(); ++i) {
+ mActiveSensors.valueAt(i)->clearAllPendingFlushConnections();
+ }
+}
+
+void SensorService::enableAllSensors() {
+ Mutex::Autolock _l(mLock);
+ enableAllSensorsLocked();
+}
+
+void SensorService::enableAllSensorsLocked() {
+ // sensors should only be enabled if the operating state is not restricted and sensor
+ // privacy is not enabled.
+ if (mCurrentOperatingMode == RESTRICTED || mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
+ ALOGW("Sensors cannot be enabled: mCurrentOperatingMode = %d, sensor privacy = %s",
+ mCurrentOperatingMode,
+ mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
+ return;
+ }
+ SensorDevice& dev(SensorDevice::getInstance());
+ dev.enableAllSensors();
+ for (auto &i : mDirectConnections) {
+ sp<SensorDirectConnection> connection(i.promote());
+ if (connection != nullptr) {
+ connection->recoverAll();
+ }
+ }
+}
+
// NOTE: This is a remote API - make sure all args are validated
status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) {
if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
@@ -627,8 +668,13 @@
do {
ssize_t count = device.poll(mSensorEventBuffer, numEventMax);
if (count < 0) {
- ALOGE("sensor poll failed (%s)", strerror(-count));
- break;
+ if(count == DEAD_OBJECT && device.isReconnecting()) {
+ device.reconnect();
+ continue;
+ } else {
+ ALOGE("sensor poll failed (%s)", strerror(-count));
+ break;
+ }
}
// Reset sensors_event_t.flags to zero for all events in the buffer.
@@ -651,16 +697,18 @@
// sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should
// not be interleaved with decrementing SensorEventConnection::mWakeLockRefCount and
// releasing the wakelock.
- bool bufferHasWakeUpEvent = false;
+ uint32_t wakeEvents = 0;
for (int i = 0; i < count; i++) {
if (isWakeUpSensorEvent(mSensorEventBuffer[i])) {
- bufferHasWakeUpEvent = true;
- break;
+ wakeEvents++;
}
}
- if (bufferHasWakeUpEvent && !mWakeLockAcquired) {
- setWakeLockAcquiredLocked(true);
+ if (wakeEvents > 0) {
+ if (!mWakeLockAcquired) {
+ setWakeLockAcquiredLocked(true);
+ }
+ device.writeWakeLockHandled(wakeEvents);
}
recordLastValueLocked(mSensorEventBuffer, count);
@@ -722,11 +770,11 @@
// on the hardware sensor. mapFlushEventsToConnections[i] will be the
// SensorEventConnection mapped to the corresponding flush_complete_event in
// mSensorEventBuffer[i] if such a mapping exists (NULL otherwise).
- mMapFlushEventsToConnections[i] = NULL;
+ mMapFlushEventsToConnections[i] = nullptr;
if (mSensorEventBuffer[i].type == SENSOR_TYPE_META_DATA) {
const int sensor_handle = mSensorEventBuffer[i].meta_data.sensor;
SensorRecord* rec = mActiveSensors.valueFor(sensor_handle);
- if (rec != NULL) {
+ if (rec != nullptr) {
mMapFlushEventsToConnections[i] = rec->getFirstPendingFlushConnection();
rec->removeFirstPendingFlushConnection();
}
@@ -770,7 +818,7 @@
size_t numConnections = activeConnections.size();
for (size_t i=0 ; i < numConnections; ++i) {
- if (activeConnections[i] != NULL) {
+ if (activeConnections[i] != nullptr) {
activeConnections[i]->removeSensor(handle);
}
}
@@ -783,7 +831,7 @@
bool needsWakeLock = false;
size_t numConnections = activeConnections.size();
for (size_t i=0 ; i < numConnections; ++i) {
- if (activeConnections[i] != 0) {
+ if (activeConnections[i] != nullptr) {
activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
mMapFlushEventsToConnections);
needsWakeLock |= activeConnections[i]->needsWakeLock();
@@ -816,7 +864,7 @@
{
Mutex::Autolock _l(mLock);
for (size_t i=0 ; i < activeConnections.size(); ++i) {
- if (activeConnections[i] != 0) {
+ if (activeConnections[i] != nullptr) {
activeConnections[i]->resetWakeLockRefCount();
}
}
@@ -1021,15 +1069,15 @@
int requestedMode, const String16& opPackageName) {
// Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION.
if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
- return NULL;
+ return nullptr;
}
Mutex::Autolock _l(mLock);
// To create a client in DATA_INJECTION mode to inject data, SensorService should already be
// operating in DI mode.
if (requestedMode == DATA_INJECTION) {
- if (mCurrentOperatingMode != DATA_INJECTION) return NULL;
- if (!isWhiteListedPackage(packageName)) return NULL;
+ if (mCurrentOperatingMode != DATA_INJECTION) return nullptr;
+ if (!isWhiteListedPackage(packageName)) return nullptr;
}
uid_t uid = IPCThreadState::self()->getCallingUid();
@@ -1063,6 +1111,12 @@
const native_handle *resource) {
Mutex::Autolock _l(mLock);
+ // No new direct connections are allowed when sensor privacy is enabled
+ if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
+ ALOGE("Cannot create new direct connections when sensor privacy is enabled");
+ return nullptr;
+ }
+
struct sensors_direct_mem_t mem = {
.type = type,
.format = format,
@@ -1277,6 +1331,15 @@
ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection");
mActiveSensors.removeItemsAt(i, 1);
mActiveVirtualSensors.erase(handle);
+
+ // If this is the last connection, then mark the RecentEventLogger as stale. This is
+ // critical for on-change events since the previous event is sent to a client if the
+ // sensor is already active. If two clients request the sensor at the same time, one
+ // of the clients would receive a stale event.
+ auto logger = mRecentEvent.find(handle);
+ if (logger != mRecentEvent.end()) {
+ logger->second->setLastEventStale();
+ }
delete rec;
size--;
} else {
@@ -1325,7 +1388,7 @@
}
SensorRecord* rec = mActiveSensors.valueFor(handle);
- if (rec == 0) {
+ if (rec == nullptr) {
rec = new SensorRecord(connection);
mActiveSensors.add(handle, rec);
if (sensor->isVirtual()) {
@@ -1343,16 +1406,17 @@
auto logger = mRecentEvent.find(handle);
if (logger != mRecentEvent.end()) {
sensors_event_t event;
- // It is unlikely that this buffer is empty as the sensor is already active.
- // One possible corner case may be two applications activating an on-change
- // sensor at the same time.
- if(logger->second->populateLastEvent(&event)) {
+ // Verify that the last sensor event was generated from the current activation
+ // of the sensor. If not, it is possible for an on-change sensor to receive a
+ // sensor event that is stale if two clients re-activate the sensor
+ // simultaneously.
+ if(logger->second->populateLastEventIfCurrent(&event)) {
event.sensor = handle;
if (event.version == sizeof(sensors_event_t)) {
if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) {
setWakeLockAcquiredLocked(true);
}
- connection->sendEvents(&event, 1, NULL);
+ connection->sendEvents(&event, 1, nullptr);
if (!connection->needsWakeLock() && mWakeLockAcquired) {
checkWakeLockStateLocked();
}
@@ -1534,7 +1598,7 @@
status_t err_flush = sensor->flush(connection.get(), handle);
if (err_flush == NO_ERROR) {
SensorRecord* rec = mActiveSensors.valueFor(handle);
- if (rec != NULL) rec->addPendingFlushConnection(connection);
+ if (rec != nullptr) rec->addPendingFlushConnection(connection);
}
err = (err_flush != NO_ERROR) ? err_flush : err;
}
@@ -1592,7 +1656,7 @@
bool releaseLock = true;
for (size_t i=0 ; i<mActiveConnections.size() ; i++) {
sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != 0) {
+ if (connection != nullptr) {
if (connection->needsWakeLock()) {
releaseLock = false;
break;
@@ -1617,7 +1681,7 @@
Mutex::Autolock _l(mLock);
for (size_t i=0 ; i < mActiveConnections.size(); ++i) {
sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != 0) {
+ if (connection != nullptr) {
activeConnections->add(connection);
}
}
@@ -1627,13 +1691,13 @@
return (packageName.contains(mWhiteListedPackage.string()));
}
-bool SensorService::isOperationRestricted(const String16& opPackageName) {
+bool SensorService::isOperationPermitted(const String16& opPackageName) {
Mutex::Autolock _l(mLock);
- if (mCurrentOperatingMode != RESTRICTED) {
+ if (mCurrentOperatingMode == RESTRICTED) {
String8 package(opPackageName);
- return !isWhiteListedPackage(package);
+ return isWhiteListedPackage(package);
}
- return false;
+ return true;
}
void SensorService::UidPolicy::registerSelf() {
@@ -1730,4 +1794,31 @@
return mActiveUids.find(uid) != mActiveUids.end();
}
+void SensorService::SensorPrivacyPolicy::registerSelf() {
+ SensorPrivacyManager spm;
+ mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled();
+ spm.addSensorPrivacyListener(this);
+}
+
+void SensorService::SensorPrivacyPolicy::unregisterSelf() {
+ SensorPrivacyManager spm;
+ spm.removeSensorPrivacyListener(this);
+}
+
+bool SensorService::SensorPrivacyPolicy::isSensorPrivacyEnabled() {
+ return mSensorPrivacyEnabled;
+}
+
+binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) {
+ mSensorPrivacyEnabled = enabled;
+ sp<SensorService> service = mService.promote();
+ if (service != nullptr) {
+ if (enabled) {
+ service->disableAllSensors();
+ } else {
+ service->enableAllSensors();
+ }
+ }
+ return binder::Status::ok();
+}
}; // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index f71723d..fbfe05d 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -26,6 +26,7 @@
#include <sensor/ISensorServer.h>
#include <sensor/ISensorEventConnection.h>
#include <sensor/Sensor.h>
+#include "android/hardware/BnSensorPrivacyListener.h"
#include <utils/AndroidThreads.h>
#include <utils/KeyedVector.h>
@@ -59,7 +60,6 @@
namespace android {
// ---------------------------------------------------------------------------
class SensorInterface;
-using namespace SensorServiceUtil;
class SensorService :
public BinderService<SensorService>,
@@ -118,6 +118,8 @@
void onUidGone(uid_t uid, bool disabled);
void onUidActive(uid_t uid);
void onUidIdle(uid_t uid, bool disabled);
+ void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
+ int64_t procStateSeq __unused) {}
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
@@ -131,6 +133,30 @@
std::unordered_map<uid_t, bool> mOverrideUids;
};
+ // Sensor privacy allows a user to disable access to all sensors on the device. When
+ // enabled sensor privacy will prevent all apps, including active apps, from accessing
+ // sensors, they will not receive trigger nor on-change events, flush event behavior
+ // does not change, and recurring events are the same as the first one delivered when
+ // sensor privacy was enabled. All sensor direct connections will be stopped as well
+ // and new direct connections will not be allowed while sensor privacy is enabled.
+ // Once sensor privacy is disabled access to sensors will be restored for active
+ // apps, previously stopped direct connections will be restarted, and new direct
+ // connections will be allowed again.
+ class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener {
+ public:
+ explicit SensorPrivacyPolicy(wp<SensorService> service) : mService(service) {}
+ void registerSelf();
+ void unregisterSelf();
+
+ bool isSensorPrivacyEnabled();
+
+ binder::Status onSensorPrivacyChanged(bool enabled);
+
+ private:
+ wp<SensorService> mService;
+ std::atomic_bool mSensorPrivacyEnabled;
+ };
+
enum Mode {
// The regular operating mode where any application can register/unregister/call flush on
// sensors.
@@ -246,7 +272,7 @@
// allowed to register for or call flush on sensors. Typically only cts test packages are
// allowed.
bool isWhiteListedPackage(const String8& packageName);
- bool isOperationRestricted(const String16& opPackageName);
+ bool isOperationPermitted(const String16& opPackageName);
// Reset the state of SensorService to NORMAL mode.
status_t resetToNormalMode();
@@ -274,10 +300,17 @@
// Prints the shell command help
status_t printHelp(int out);
+ // temporarily stops all active direct connections and disables all sensors
+ void disableAllSensors();
+ void disableAllSensorsLocked();
+ // restarts the previously stopped direct connections and enables all sensors
+ void enableAllSensors();
+ void enableAllSensorsLocked();
+
static uint8_t sHmacGlobalKey[128];
static bool sHmacGlobalKeyIsValid;
- SensorList mSensors;
+ SensorServiceUtil::SensorList mSensors;
status_t mInitCheck;
// Socket buffersize used to initialize BitTube. This size depends on whether batching is
@@ -294,7 +327,7 @@
bool mWakeLockAcquired;
sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
wp<const SensorEventConnection> * mMapFlushEventsToConnections;
- std::unordered_map<int, RecentEventLogger*> mRecentEvent;
+ std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
SortedVector< wp<SensorDirectConnection> > mDirectConnections;
Mode mCurrentOperatingMode;
@@ -308,6 +341,7 @@
Vector<SensorRegistrationInfo> mLastNSensorRegistrations;
sp<UidPolicy> mUidPolicy;
+ sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
};
} // namespace android
diff --git a/services/sensorservice/SensorsWrapper.h b/services/sensorservice/SensorsWrapper.h
new file mode 100644
index 0000000..d1a7234
--- /dev/null
+++ b/services/sensorservice/SensorsWrapper.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 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_SENSORS_WRAPPER_H
+#define ANDROID_SENSORS_WRAPPER_H
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+
+#include <utils/LightRefBase.h>
+
+namespace android {
+namespace SensorServiceUtil {
+
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::sensors::V1_0::Event;
+using ::android::hardware::sensors::V1_0::ISensors;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::RateLevel;
+using ::android::hardware::sensors::V1_0::Result;
+using ::android::hardware::sensors::V1_0::SharedMemInfo;
+using ::android::hardware::sensors::V2_0::ISensorsCallback;
+
+/*
+ * The ISensorsWrapper interface includes all function from supported Sensors HAL versions. This
+ * allows for the SensorDevice to use the ISensorsWrapper interface to interact with the Sensors
+ * HAL regardless of the current version of the Sensors HAL that is loaded. Each concrete
+ * instantiation of ISensorsWrapper must correspond to a specific Sensors HAL version. This design
+ * is beneficial because only the functions that change between Sensors HAL versions must be newly
+ * newly implemented, any previously implemented function that does not change may remain the same.
+ *
+ * Functions that exist across all versions of the Sensors HAL should be implemented as pure
+ * virtual functions which forces the concrete instantiations to implement the functions.
+ *
+ * Functions that do not exist across all versions of the Sensors HAL should include a default
+ * implementation that generates an error if called. The default implementation should never
+ * be called and must be overridden by Sensors HAL versions that support the function.
+ */
+class ISensorsWrapper : public VirtualLightRefBase {
+public:
+ virtual bool supportsPolling() const = 0;
+
+ virtual bool supportsMessageQueues() const = 0;
+
+ virtual Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) = 0;
+
+ virtual Return<Result> setOperationMode(OperationMode mode) = 0;
+
+ virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
+
+ virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) = 0;
+
+ virtual Return<Result> flush(int32_t sensorHandle) = 0;
+
+ virtual Return<Result> injectSensorData(const Event& event) = 0;
+
+ virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) = 0;
+
+ virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+ RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) = 0;
+
+ virtual Return<void> poll(int32_t maxCount, ISensors::poll_cb _hidl_cb) {
+ (void)maxCount;
+ (void)_hidl_cb;
+ // TODO (b/111070257): Generate an assert-level error since this should never be called
+ // directly
+ return Return<void>();
+ }
+
+ virtual Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc,
+ const MQDescriptorSync<uint32_t>& wakeLockDesc,
+ const ::android::sp<ISensorsCallback>& callback) {
+ (void)eventQueueDesc;
+ (void)wakeLockDesc;
+ (void)callback;
+ // TODO (b/111070257): Generate an assert-level error since this should never be called
+ // directly
+ return Result::INVALID_OPERATION;
+ }
+};
+
+template<typename T>
+class SensorsWrapperBase : public ISensorsWrapper {
+public:
+ SensorsWrapperBase(sp<T> sensors) :
+ mSensors(sensors) { };
+
+ Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) override {
+ return mSensors->getSensorsList(_hidl_cb);
+ }
+
+ Return<Result> setOperationMode(OperationMode mode) override {
+ return mSensors->setOperationMode(mode);
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ return mSensors->activate(sensorHandle, enabled);
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override {
+ return mSensors->flush(sensorHandle);
+ }
+
+ Return<Result> injectSensorData(const Event& event) override {
+ return mSensors->injectSensorData(event);
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) override {
+ return mSensors->registerDirectChannel(mem, _hidl_cb);
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return mSensors->unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+ RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) override {
+ return mSensors->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+protected:
+ sp<T> mSensors;
+};
+
+class SensorsWrapperV1_0 : public SensorsWrapperBase<hardware::sensors::V1_0::ISensors> {
+public:
+ SensorsWrapperV1_0(sp<hardware::sensors::V1_0::ISensors> sensors) :
+ SensorsWrapperBase(sensors) { };
+
+ bool supportsPolling() const override {
+ return true;
+ }
+
+ bool supportsMessageQueues() const override {
+ return false;
+ }
+
+ Return<void> poll(int32_t maxCount,
+ hardware::sensors::V1_0::ISensors::poll_cb _hidl_cb) override {
+ return mSensors->poll(maxCount, _hidl_cb);
+ }
+};
+
+class SensorsWrapperV2_0 : public SensorsWrapperBase<hardware::sensors::V2_0::ISensors> {
+public:
+ SensorsWrapperV2_0(sp<hardware::sensors::V2_0::ISensors> sensors)
+ : SensorsWrapperBase(sensors) { };
+
+ bool supportsPolling() const override {
+ return false;
+ }
+
+ bool supportsMessageQueues() const override {
+ return true;
+ }
+
+ Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc,
+ const MQDescriptorSync<uint32_t>& wakeLockDesc,
+ const ::android::sp<ISensorsCallback>& callback) override {
+ return mSensors->initialize(eventQueueDesc, wakeLockDesc, callback);
+ }
+};
+
+}; // namespace SensorServiceUtil
+}; // namespace android
+
+#endif // ANDROID_SENSORS_WRAPPER_H
diff --git a/services/sensorservice/hidl/EventQueue.cpp b/services/sensorservice/hidl/EventQueue.cpp
index ff20066..b781744 100644
--- a/services/sensorservice/hidl/EventQueue.cpp
+++ b/services/sensorservice/hidl/EventQueue.cpp
@@ -64,7 +64,7 @@
mInternalQueue(internalQueue) {
mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
- new EventQueueLooperCallback(internalQueue, callback), NULL /* data */);
+ new EventQueueLooperCallback(internalQueue, callback), nullptr /* data */);
}
void EventQueue::onLastStrongRef(const void *id) {
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index fee6da1..9380600 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -157,7 +157,7 @@
JavaVMAttachArgs args{
.version = JNI_VERSION_1_2,
.name = POLL_THREAD_NAME,
- .group = NULL
+ .group = nullptr
};
JNIEnv* env;
if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
diff --git a/services/surfaceflinger/AllowedDisplayConfigs.h b/services/surfaceflinger/AllowedDisplayConfigs.h
new file mode 100644
index 0000000..7ca62ea
--- /dev/null
+++ b/services/surfaceflinger/AllowedDisplayConfigs.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 <log/log.h>
+#include <vector>
+
+/*
+ * Used to represent the Display Configurations allowed to be set by SurfaceFlinger
+ */
+class AllowedDisplayConfigs {
+private:
+ // Defining ConstructorTag as private to prevent instantiating this class from outside
+ // while still allowing it to be constructed by std::make_unique
+ struct ConstructorTag {};
+
+public:
+ AllowedDisplayConfigs(ConstructorTag) {}
+
+ class Builder {
+ public:
+ Builder()
+ : mAllowedDisplayConfigs(std::make_unique<AllowedDisplayConfigs>(ConstructorTag{})) {}
+
+ std::unique_ptr<const AllowedDisplayConfigs> build() {
+ return std::move(mAllowedDisplayConfigs);
+ }
+
+ // add a config to the allowed config set
+ Builder& addConfig(int32_t config) {
+ mAllowedDisplayConfigs->addConfig(config);
+ return *this;
+ }
+
+ private:
+ std::unique_ptr<AllowedDisplayConfigs> mAllowedDisplayConfigs;
+ };
+
+ bool isConfigAllowed(int32_t config) const {
+ return (std::find(mConfigs.begin(), mConfigs.end(), config) != mConfigs.end());
+ }
+
+ void getAllowedConfigs(std::vector<int32_t>* outConfigs) const {
+ if (outConfigs) {
+ *outConfigs = mConfigs;
+ }
+ }
+
+private:
+ // add a config to the allowed config set
+ void addConfig(int32_t config) { mConfigs.push_back(config); }
+
+ std::vector<int32_t> mConfigs;
+};
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5d4ac23..ac1d492 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -1,9 +1,9 @@
cc_defaults {
name: "surfaceflinger_defaults",
cflags: [
- "-DLOG_TAG=\"SurfaceFlinger\"",
"-Wall",
"-Werror",
+ "-Wformat",
"-Wthread-safety",
"-Wunused",
"-Wunreachable-code",
@@ -14,6 +14,7 @@
name: "libsurfaceflinger_defaults",
defaults: ["surfaceflinger_defaults"],
cflags: [
+ "-DLOG_TAG=\"SurfaceFlinger\"",
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
],
@@ -23,8 +24,11 @@
"android.hardware.configstore@1.0",
"android.hardware.configstore@1.1",
"android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.3",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
"libbase",
@@ -43,15 +47,20 @@
"libhwbinder",
"liblayers_proto",
"liblog",
+ "libnativewindow",
"libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
"libsync",
"libtimestats_proto",
"libui",
+ "libinput",
"libutils",
+ "libSurfaceFlingerProperties",
],
static_libs: [
+ "libcompositionengine",
+ "librenderengine",
"libserviceutils",
"libtrace_proto",
"libvr_manager",
@@ -60,14 +69,20 @@
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
+ "android.hardware.graphics.composer@2.3-command-buffer",
],
export_static_lib_headers: [
+ "libcompositionengine",
+ "librenderengine",
"libserviceutils",
],
export_shared_lib_headers: [
"android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.3",
"android.hardware.power@1.3",
"libhidlbase",
"libhidltransport",
@@ -75,6 +90,18 @@
],
}
+cc_defaults {
+ name: "libsurfaceflinger_production_defaults",
+ defaults: ["libsurfaceflinger_defaults"],
+ cflags: [
+ "-fvisibility=hidden",
+ "-fwhole-program-vtables", // requires ThinLTO
+ ],
+ lto: {
+ thin: true,
+ },
+}
+
cc_library_headers {
name: "libsurfaceflinger_headers",
export_include_dirs: ["."],
@@ -87,78 +114,72 @@
srcs: [
"BufferLayer.cpp",
"BufferLayerConsumer.cpp",
+ "BufferQueueLayer.cpp",
+ "BufferStateLayer.cpp",
+ "BufferStateLayerCache.cpp",
"Client.cpp",
"ColorLayer.cpp",
"ContainerLayer.cpp",
"DisplayDevice.cpp",
"DisplayHardware/ComposerHal.cpp",
+ "DisplayHardware/DisplayIdentification.cpp",
"DisplayHardware/FramebufferSurface.cpp",
"DisplayHardware/HWC2.cpp",
"DisplayHardware/HWComposer.cpp",
- "DisplayHardware/HWComposerBufferCache.cpp",
"DisplayHardware/PowerAdvisor.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
- "DispSync.cpp",
"Effects/Daltonizer.cpp",
- "EventControlThread.cpp",
"EventLog/EventLog.cpp",
- "EventThread.cpp",
"FrameTracker.cpp",
"Layer.cpp",
"LayerProtoHelper.cpp",
"LayerRejecter.cpp",
"LayerStats.cpp",
"LayerVector.cpp",
- "MessageQueue.cpp",
"MonitoredProducer.cpp",
+ "NativeWindowSurface.cpp",
"RenderArea.cpp",
- "RenderEngine/Description.cpp",
- "RenderEngine/GLES20RenderEngine.cpp",
- "RenderEngine/GLExtensions.cpp",
- "RenderEngine/Image.cpp",
- "RenderEngine/Mesh.cpp",
- "RenderEngine/Program.cpp",
- "RenderEngine/ProgramCache.cpp",
- "RenderEngine/RenderEngine.cpp",
- "RenderEngine/Surface.cpp",
- "RenderEngine/Texture.cpp",
+ "RegionSamplingThread.cpp",
+ "Scheduler/DispSync.cpp",
+ "Scheduler/DispSyncSource.cpp",
+ "Scheduler/EventControlThread.cpp",
+ "Scheduler/EventThread.cpp",
+ "Scheduler/IdleTimer.cpp",
+ "Scheduler/LayerHistory.cpp",
+ "Scheduler/MessageQueue.cpp",
+ "Scheduler/Scheduler.cpp",
+ "Scheduler/SchedulerUtils.cpp",
+ "Scheduler/PhaseOffsets.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceInterceptor.cpp",
"SurfaceTracing.cpp",
"TimeStats/TimeStats.cpp",
- "Transform.cpp",
+ "TransactionCompletedThread.cpp",
],
}
cc_library_shared {
+ // Please use libsurfaceflinger_defaults to configure how the sources are
+ // built, so the same settings can be used elsewhere.
name: "libsurfaceflinger",
- defaults: ["libsurfaceflinger_defaults"],
- cflags: [
- "-fvisibility=hidden",
- "-Werror=format",
- ],
+ defaults: ["libsurfaceflinger_production_defaults"],
srcs: [
":libsurfaceflinger_sources",
+
+ // Note: SurfaceFlingerFactory is not in the default sources so that it
+ // can be easily replaced.
+ "SurfaceFlingerFactory.cpp",
],
logtags: ["EventLog/EventLogTags.logtags"],
- include_dirs: [
- "frameworks/native/vulkan/vkjson",
- "frameworks/native/vulkan/include",
- ],
- cppflags: [
- "-fwhole-program-vtables", // requires ThinLTO
- ],
- lto: {
- thin: true,
- },
}
-cc_binary {
- name: "surfaceflinger",
+cc_defaults {
+ name: "libsurfaceflinger_binary",
defaults: ["surfaceflinger_defaults"],
- init_rc: ["surfaceflinger.rc"],
- srcs: ["main_surfaceflinger.cpp"],
+ cflags: [
+ "-DLOG_TAG=\"SurfaceFlinger\"",
+ ],
whole_static_libs: [
"libsigchain",
],
@@ -167,15 +188,17 @@
"android.hardware.configstore-utils",
"android.hardware.configstore@1.0",
"android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
"libbinder",
"libcutils",
"libdisplayservicehidl",
"libhidlbase",
"libhidltransport",
+ "libinput",
"liblayers_proto",
"liblog",
"libprocessgroup",
- "libsurfaceflinger",
+ "libsync",
"libtimestats_proto",
"libutils",
],
@@ -184,19 +207,22 @@
"libtrace_proto",
],
ldflags: ["-Wl,--export-dynamic"],
+}
- // TODO(b/71715793): These version-scripts are required due to the use of
- // whole_static_libs to pull in libsigchain. To work, the files had to be
- // locally duplicated from their original location
- // $ANDROID_ROOT/art/sigchainlib/
- multilib: {
- lib32: {
- version_script: "version-script32.txt",
- },
- lib64: {
- version_script: "version-script64.txt",
- },
- },
+filegroup {
+ name: "surfaceflinger_binary_sources",
+ srcs: ["main_surfaceflinger.cpp"],
+}
+
+cc_binary {
+ name: "surfaceflinger",
+ defaults: ["libsurfaceflinger_binary"],
+ init_rc: ["surfaceflinger.rc"],
+ srcs: [":surfaceflinger_binary_sources"],
+ shared_libs: [
+ "libsurfaceflinger",
+ "libSurfaceFlingerProperties",
+ ],
}
subdirs = [
@@ -204,3 +230,28 @@
"TimeStats/timestatsproto",
"tests",
]
+
+cc_library_shared {
+ name: "libSurfaceFlingerProperties",
+ srcs: [
+ "SurfaceFlingerProperties.cpp",
+ "sysprop/*.sysprop",
+ ],
+ shared_libs: [
+ "android.hardware.configstore-utils",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
+ "android.hardware.graphics.common@1.2",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libui",
+ "libutils",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.graphics.common@1.2",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ ],
+}
diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
index 3e9d443..97028a8 100644
--- a/services/surfaceflinger/Barrier.h
+++ b/services/surfaceflinger/Barrier.h
@@ -18,46 +18,40 @@
#define ANDROID_BARRIER_H
#include <stdint.h>
-#include <sys/types.h>
-#include <utils/threads.h>
+#include <condition_variable>
+#include <mutex>
namespace android {
class Barrier
{
public:
- inline Barrier() : state(CLOSED) { }
- inline ~Barrier() { }
-
// Release any threads waiting at the Barrier.
// Provides release semantics: preceding loads and stores will be visible
// to other threads before they wake up.
void open() {
- Mutex::Autolock _l(lock);
- state = OPENED;
- cv.broadcast();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mIsOpen = true;
+ mCondition.notify_all();
}
// Reset the Barrier, so wait() will block until open() has been called.
void close() {
- Mutex::Autolock _l(lock);
- state = CLOSED;
+ std::lock_guard<std::mutex> lock(mMutex);
+ mIsOpen = false;
}
// Wait until the Barrier is OPEN.
// Provides acquire semantics: no subsequent loads or stores will occur
// until wait() returns.
void wait() const {
- Mutex::Autolock _l(lock);
- while (state == CLOSED) {
- cv.wait(lock);
- }
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this]() NO_THREAD_SAFETY_ANALYSIS { return mIsOpen; });
}
private:
- enum { OPENED, CLOSED };
- mutable Mutex lock;
- mutable Condition cv;
- volatile int state;
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mCondition;
+ int mIsOpen GUARDED_BY(mMutex){false};
};
}; // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 707cb42..1b2b180 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -19,77 +19,64 @@
#define LOG_TAG "BufferLayer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "BufferLayer.h"
-#include "Colorizer.h"
-#include "DisplayDevice.h"
-#include "LayerRejecter.h"
-#include "clz.h"
+#include <cmath>
+#include <cstdlib>
+#include <mutex>
-#include "RenderEngine/RenderEngine.h"
-
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <cutils/compiler.h>
+#include <cutils/native_handle.h>
+#include <cutils/properties.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
-
+#include <renderengine/RenderEngine.h>
#include <ui/DebugUtils.h>
-
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/NativeHandle.h>
#include <utils/StopWatch.h>
#include <utils/Trace.h>
-#include <cutils/compiler.h>
-#include <cutils/native_handle.h>
-#include <cutils/properties.h>
+#include "BufferLayer.h"
+#include "Colorizer.h"
+#include "DisplayDevice.h"
+#include "LayerRejecter.h"
-#include <math.h>
-#include <stdlib.h>
-#include <mutex>
+#include "TimeStats/TimeStats.h"
namespace android {
-BufferLayer::BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
- uint32_t w, uint32_t h, uint32_t flags)
- : Layer(flinger, client, name, w, h, flags),
- mConsumer(nullptr),
- mTextureName(UINT32_MAX),
- mFormat(PIXEL_FORMAT_NONE),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mBufferLatched(false),
- mPreviousFrameNumber(0),
- mUpdateTexImageFailed(false),
- mRefreshPending(false) {
- ALOGV("Creating Layer %s", name.string());
+BufferLayer::BufferLayer(const LayerCreationArgs& args)
+ : Layer(args),
+ mTextureName(args.flinger->getNewTexture()),
+ mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
+ compositionengine::LayerCreationArgs{this})} {
+ ALOGV("Creating Layer %s", args.name.string());
- mTextureName = mFlinger->getNewTexture();
- mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName);
+ mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
- if (flags & ISurfaceComposerClient::eNonPremultiplied) mPremultipliedAlpha = false;
-
- mCurrentState.requested = mCurrentState.active;
-
- // drawing state & current state are identical
- mDrawingState = mCurrentState;
+ mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+ mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
}
BufferLayer::~BufferLayer() {
mFlinger->deleteTextureAsync(mTextureName);
-
- if (!getBE().mHwcLayers.empty()) {
- ALOGE("Found stale hardware composer layers when destroying "
- "surface flinger layer %s",
- mName.string());
- destroyAllHwcLayers();
- }
+ mFlinger->mTimeStats->onDestroy(getSequence());
}
void BufferLayer::useSurfaceDamage() {
if (mFlinger->mForceFullDamage) {
surfaceDamageRegion = Region::INVALID_REGION;
} else {
- surfaceDamageRegion = mConsumer->getSurfaceDamage();
+ surfaceDamageRegion = getDrawingSurfaceDamage();
}
}
@@ -97,44 +84,29 @@
surfaceDamageRegion.clear();
}
-bool BufferLayer::isProtected() const {
- const sp<GraphicBuffer>& buffer(getBE().compositionInfo.mBuffer);
- return (buffer != 0) &&
- (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+bool BufferLayer::isOpaque(const Layer::State& s) const {
+ // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+ // layer's opaque flag.
+ if ((mSidebandStream == nullptr) && (mActiveBuffer == nullptr)) {
+ return false;
+ }
+
+ // if the layer has the opaque flag, then we're always opaque,
+ // otherwise we use the current buffer's format.
+ return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
}
bool BufferLayer::isVisible() const {
return !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
- (getBE().compositionInfo.mBuffer != nullptr ||
- getBE().compositionInfo.hwc.sidebandStream != nullptr);
+ (mActiveBuffer != nullptr || mSidebandStream != nullptr);
}
bool BufferLayer::isFixedSize() const {
return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE;
}
-status_t BufferLayer::setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) {
- uint32_t const maxSurfaceDims =
- min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims());
-
- // never allow a surface larger than what our underlying GL implementation
- // can handle.
- if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) {
- ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
- return BAD_VALUE;
- }
-
- mFormat = format;
-
- mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false;
- mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false;
- mCurrentOpacity = getOpacityForFormat(format);
-
- mConsumer->setDefaultBufferSize(w, h);
- mConsumer->setDefaultBufferFormat(format);
- mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-
- return NO_ERROR;
+bool BufferLayer::usesSourceCrop() const {
+ return true;
}
static constexpr mat4 inverseOrientation(uint32_t transform) {
@@ -155,14 +127,14 @@
return inverse(tr);
}
-/*
- * onDraw will draw the current layer onto the presentable buffer
- */
-void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) const {
+bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer) {
ATRACE_CALL();
-
- if (CC_UNLIKELY(getBE().compositionInfo.mBuffer == 0)) {
+ Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion,
+ supportProtectedContent, layer);
+ if (CC_UNLIKELY(mActiveBuffer == 0)) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
// SurfaceView because the WindowManager can't know when the client
@@ -179,37 +151,33 @@
finished = true;
return;
}
- under.orSelf(renderArea.getTransform().transform(layer->visibleRegion));
+ under.orSelf(layer->visibleRegion);
});
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
- clearWithOpenGL(renderArea, 0, 0, 0, 1);
+ clearRegion.orSelf(holes);
}
- return;
+ return false;
}
-
- // Bind the current buffer to the GL texture, and wait for it to be
- // ready for us to draw into.
- status_t err = mConsumer->bindTextureImage();
- if (err != NO_ERROR) {
- ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
- // Go ahead and draw the buffer anyway; no matter what we do the screen
- // is probably going to have something visibly wrong.
- }
-
- bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure());
-
- auto& engine(mFlinger->getRenderEngine());
-
+ bool blackOutLayer =
+ (isProtected() && !supportProtectedContent) || (isSecure() && !renderArea.isSecure());
+ const State& s(getDrawingState());
if (!blackOutLayer) {
+ layer.source.buffer.buffer = mActiveBuffer;
+ layer.source.buffer.isOpaque = isOpaque(s);
+ layer.source.buffer.fence = mActiveBufferFence;
+ layer.source.buffer.textureName = mTextureName;
+ layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+ layer.source.buffer.isY410BT2020 = isHdrY410();
// TODO: we could be more subtle with isFixedSize()
- const bool useFiltering = needsFiltering(renderArea) || isFixedSize();
+ const bool useFiltering = needsFiltering(renderArea.getDisplayDevice()) ||
+ renderArea.needsFiltering() || isFixedSize();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
- mConsumer->setFilteringEnabled(useFiltering);
- mConsumer->getTransformMatrix(textureMatrix);
+ setFilteringEnabled(useFiltering);
+ getDrawingTransformMatrix(textureMatrix);
if (getTransformToDisplayInverse()) {
/*
@@ -239,66 +207,142 @@
memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
}
- // Set things up for texturing.
- mTexture.setDimensions(getBE().compositionInfo.mBuffer->getWidth(),
- getBE().compositionInfo.mBuffer->getHeight());
- mTexture.setFiltering(useFiltering);
- mTexture.setMatrix(textureMatrix);
+ const Rect win{getBounds()};
+ float bufferWidth = getBufferSize(s).getWidth();
+ float bufferHeight = getBufferSize(s).getHeight();
- engine.setupLayerTexturing(mTexture);
+ // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+ // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+ // ignore them.
+ if (!getBufferSize(s).isValid()) {
+ bufferWidth = float(win.right) - float(win.left);
+ bufferHeight = float(win.bottom) - float(win.top);
+ }
+
+ const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+ const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+ const float translateY = float(win.top) / bufferHeight;
+ const float translateX = float(win.left) / bufferWidth;
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
+ mat4::translate(vec4(-.5, -.5, 0, 1)) *
+ mat4::translate(vec4(translateX, translateY, 0, 1)) *
+ mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
+
+ layer.source.buffer.useTextureFiltering = useFiltering;
+ layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
} else {
- engine.setupLayerBlackedOut();
- }
- drawWithOpenGL(renderArea, useIdentityTransform);
- engine.disableTexturing();
-}
-
-void BufferLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
- mConsumer->setReleaseFence(releaseFence);
-}
-
-void BufferLayer::abandon() {
- mConsumer->abandon();
-}
-
-bool BufferLayer::shouldPresentNow(const DispSync& dispSync) const {
- if (mSidebandStreamChanged || mAutoRefresh) {
- return true;
+ // If layer is blacked out, force alpha to 1 so that we draw a black color
+ // layer.
+ layer.source.buffer.buffer = nullptr;
+ layer.alpha = 1.0;
}
- Mutex::Autolock lock(mQueueItemLock);
- if (mQueueItems.empty()) {
- return false;
- }
- auto timestamp = mQueueItems[0].mTimestamp;
- nsecs_t expectedPresent = mConsumer->computeExpectedPresent(dispSync);
-
- // Ignore timestamps more than a second in the future
- bool isPlausible = timestamp < (expectedPresent + s2ns(1));
- ALOGW_IF(!isPlausible,
- "[%s] Timestamp %" PRId64 " seems implausible "
- "relative to expectedPresent %" PRId64,
- mName.string(), timestamp, expectedPresent);
-
- bool isDue = timestamp < expectedPresent;
- return isDue || !isPlausible;
+ return true;
}
-void BufferLayer::setTransformHint(uint32_t orientation) const {
- mConsumer->setTransformHint(orientation);
+bool BufferLayer::isHdrY410() const {
+ // pixel format is HDR Y410 masquerading as RGBA_1010102
+ return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ &&
+ getDrawingApi() == NATIVE_WINDOW_API_MEDIA &&
+ mActiveBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice,
+ const ui::Transform& transform, const Rect& viewport,
+ int32_t supportedPerFrameMetadata,
+ const ui::Dataspace targetDataspace) {
+ RETURN_IF_NO_HWC_LAYER(displayDevice);
+
+ // Apply this display's projection's viewport to the visible region
+ // before giving it to the HWC HAL.
+ Region visible = transform.transform(visibleRegion.intersect(viewport));
+
+ const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
+
+ auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
+ auto error = hwcLayer->setVisibleRegion(visible);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ visible.dump(LOG_TAG);
+ }
+ outputLayer->editState().visibleRegion = visible;
+
+ auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+
+ error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ surfaceDamageRegion.dump(LOG_TAG);
+ }
+ layerCompositionState.surfaceDamage = surfaceDamageRegion;
+
+ // Sideband layers
+ if (layerCompositionState.sidebandStream.get()) {
+ setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::SIDEBAND);
+ ALOGV("[%s] Requesting Sideband composition", mName.string());
+ error = hwcLayer->setSidebandStream(layerCompositionState.sidebandStream->handle());
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
+ layerCompositionState.sidebandStream->handle(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+ layerCompositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+ return;
+ }
+
+ // Device or Cursor layers
+ if (mPotentialCursor) {
+ ALOGV("[%s] Requesting Cursor composition", mName.string());
+ setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CURSOR);
+ } else {
+ ALOGV("[%s] Requesting Device composition", mName.string());
+ setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);
+ }
+
+ ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN
+ ? targetDataspace
+ : mCurrentDataSpace;
+ error = hwcLayer->setDataspace(dataspace);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ const HdrMetadata& metadata = getDrawingHdrMetadata();
+ error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, metadata);
+ if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
+ ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ error = hwcLayer->setColorTransform(getColorTransform());
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+ layerCompositionState.dataspace = mCurrentDataSpace;
+ layerCompositionState.colorTransform = getColorTransform();
+ layerCompositionState.hdrMetadata = metadata;
+
+ setHwcLayerBuffer(displayDevice);
}
bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
if (mBufferLatched) {
Mutex::Autolock lock(mFrameEventHistoryMutex);
- mFrameEventHistory.addPreComposition(mCurrentFrameNumber,
- refreshStartTime);
+ mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
}
mRefreshPending = false;
- return mQueuedFrames > 0 || mSidebandStreamChanged ||
- mAutoRefresh;
+ return hasReadyFrame();
}
-bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence,
+
+bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId,
+ const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
const CompositorTiming& compositorTiming) {
// mFrameLatencyNeeded is true when a new frame was latched for the
@@ -308,18 +352,18 @@
// Update mFrameEventHistory.
{
Mutex::Autolock lock(mFrameEventHistoryMutex);
- mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence,
- presentFence, compositorTiming);
+ mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, presentFence,
+ compositorTiming);
}
// Update mFrameTracker.
- nsecs_t desiredPresentTime = mConsumer->getTimestamp();
+ nsecs_t desiredPresentTime = getDesiredPresentTime();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
- const std::string layerName(getName().c_str());
- mTimeStats.setDesiredTime(layerName, mCurrentFrameNumber, desiredPresentTime);
+ const int32_t layerID = getSequence();
+ mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
- std::shared_ptr<FenceTime> frameReadyFence = mConsumer->getCurrentFenceTime();
+ std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
if (frameReadyFence->isValid()) {
mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
} else {
@@ -329,14 +373,13 @@
}
if (presentFence->isValid()) {
- mTimeStats.setPresentFence(layerName, mCurrentFrameNumber, presentFence);
+ mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
- } else {
+ } else if (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(HWC_DISPLAY_PRIMARY);
- mTimeStats.setPresentTime(layerName, mCurrentFrameNumber, actualPresentTime);
+ const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
+ mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
mFrameTracker.setActualPresentTime(actualPresentTime);
}
@@ -345,58 +388,17 @@
return true;
}
-std::vector<OccupancyTracker::Segment> BufferLayer::getOccupancyHistory(bool forceFlush) {
- std::vector<OccupancyTracker::Segment> history;
- status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
- if (result != NO_ERROR) {
- ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(), result);
- return {};
- }
- return history;
-}
-
-bool BufferLayer::getTransformToDisplayInverse() const {
- return mConsumer->getTransformToDisplayInverse();
-}
-
-void BufferLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
- if (!mConsumer->releasePendingBuffer()) {
- return;
- }
-
- auto releaseFenceTime =
- std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence());
- mReleaseTimeline.updateSignalTimes();
- mReleaseTimeline.push(releaseFenceTime);
-
- Mutex::Autolock lock(mFrameEventHistoryMutex);
- if (mPreviousFrameNumber != 0) {
- mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
- std::move(releaseFenceTime));
- }
-}
-
-Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
ATRACE_CALL();
- if (android_atomic_acquire_cas(true, false, &mSidebandStreamChanged) == 0) {
- // mSidebandStreamChanged was true
- mSidebandStream = mConsumer->getSidebandStream();
- // replicated in LayerBE until FE/BE is ready to be synchronized
- getBE().compositionInfo.hwc.sidebandStream = mSidebandStream;
- if (getBE().compositionInfo.hwc.sidebandStream != nullptr) {
- setTransactionFlags(eTransactionNeeded);
- mFlinger->setTransactionFlags(eTraversalNeeded);
- }
- recomputeVisibleRegions = true;
+ bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
- const State& s(getDrawingState());
- return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
+ if (refreshRequired) {
+ return refreshRequired;
}
- Region outDirtyRegion;
- if (mQueuedFrames <= 0 && !mAutoRefresh) {
- return outDirtyRegion;
+ if (!hasReadyFrame()) {
+ return false;
}
// if we've already called updateTexImage() without going through
@@ -405,119 +407,41 @@
// compositionComplete() call.
// we'll trigger an update in onPreComposition().
if (mRefreshPending) {
- return outDirtyRegion;
+ return false;
}
// If the head buffer's acquire fence hasn't signaled yet, return and
// try again later
- if (!headFenceHasSignaled()) {
+ if (!fenceHasSignaled()) {
mFlinger->signalLayerUpdate();
- return outDirtyRegion;
+ return false;
}
// Capture the old state of the layer for comparisons later
const State& s(getDrawingState());
const bool oldOpacity = isOpaque(s);
- sp<GraphicBuffer> oldBuffer = getBE().compositionInfo.mBuffer;
+ sp<GraphicBuffer> oldBuffer = mActiveBuffer;
if (!allTransactionsSignaled()) {
mFlinger->signalLayerUpdate();
- return outDirtyRegion;
+ return false;
}
- // This boolean is used to make sure that SurfaceFlinger's shadow copy
- // of the buffer queue isn't modified when the buffer queue is returning
- // BufferItem's that weren't actually queued. This can happen in shared
- // buffer mode.
- bool queuedBuffer = false;
- LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
- getProducerStickyTransform() != 0, mName.string(),
- mOverrideScalingMode, mFreezeGeometryUpdates);
- status_t updateResult =
- mConsumer->updateTexImage(&r, mFlinger->mPrimaryDispSync,
- &mAutoRefresh, &queuedBuffer,
- mLastFrameNumberReceived);
- if (updateResult == BufferQueue::PRESENT_LATER) {
- // Producer doesn't want buffer to be displayed yet. Signal a
- // layer update so we check again at the next opportunity.
- mFlinger->signalLayerUpdate();
- return outDirtyRegion;
- } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
- // If the buffer has been rejected, remove it from the shadow queue
- // and return early
- if (queuedBuffer) {
- Mutex::Autolock lock(mQueueItemLock);
- mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
- mQueueItems.removeAt(0);
- android_atomic_dec(&mQueuedFrames);
- }
- return outDirtyRegion;
- } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
- // This can occur if something goes wrong when trying to create the
- // EGLImage for this buffer. If this happens, the buffer has already
- // been released, so we need to clean up the queue and bug out
- // early.
- if (queuedBuffer) {
- Mutex::Autolock lock(mQueueItemLock);
- mQueueItems.clear();
- android_atomic_and(0, &mQueuedFrames);
- mTimeStats.clearLayerRecord(getName().c_str());
- }
-
- // Once we have hit this state, the shadow queue may no longer
- // correctly reflect the incoming BufferQueue's contents, so even if
- // updateTexImage starts working, the only safe course of action is
- // to continue to ignore updates.
- mUpdateTexImageFailed = true;
-
- return outDirtyRegion;
+ status_t err = updateTexImage(recomputeVisibleRegions, latchTime);
+ if (err != NO_ERROR) {
+ return false;
}
- if (queuedBuffer) {
- // Autolock scope
- auto currentFrameNumber = mConsumer->getFrameNumber();
-
- Mutex::Autolock lock(mQueueItemLock);
-
- // Remove any stale buffers that have been dropped during
- // updateTexImage
- while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
- mQueueItems.removeAt(0);
- android_atomic_dec(&mQueuedFrames);
- }
-
- const std::string layerName(getName().c_str());
- mTimeStats.setAcquireFence(layerName, currentFrameNumber, mQueueItems[0].mFenceTime);
- mTimeStats.setLatchTime(layerName, currentFrameNumber, latchTime);
-
- mQueueItems.removeAt(0);
- }
-
- // Decrement the queued-frames count. Signal another event if we
- // have more frames pending.
- if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1) ||
- mAutoRefresh) {
- mFlinger->signalLayerUpdate();
- }
-
- // update the active buffer
- getBE().compositionInfo.mBuffer =
- mConsumer->getCurrentBuffer(&getBE().compositionInfo.mBufferSlot);
- // replicated in LayerBE until FE/BE is ready to be synchronized
- mActiveBuffer = getBE().compositionInfo.mBuffer;
- if (getBE().compositionInfo.mBuffer == nullptr) {
- // this can only happen if the very first buffer was rejected.
- return outDirtyRegion;
+ err = updateActiveBuffer();
+ if (err != NO_ERROR) {
+ return false;
}
mBufferLatched = true;
- mPreviousFrameNumber = mCurrentFrameNumber;
- mCurrentFrameNumber = mConsumer->getFrameNumber();
- {
- Mutex::Autolock lock(mFrameEventHistoryMutex);
- mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+ err = updateFrameNumber(latchTime);
+ if (err != NO_ERROR) {
+ return false;
}
mRefreshPending = true;
@@ -528,55 +452,54 @@
recomputeVisibleRegions = true;
}
- ui::Dataspace dataSpace = mConsumer->getCurrentDataSpace();
- // treat modern dataspaces as legacy dataspaces whenever possible, until
- // we can trust the buffer producers
+ ui::Dataspace dataSpace = getDrawingDataSpace();
+ // translate legacy dataspaces to modern dataspaces
switch (dataSpace) {
- case ui::Dataspace::V0_SRGB:
- dataSpace = ui::Dataspace::SRGB;
+ case ui::Dataspace::SRGB:
+ dataSpace = ui::Dataspace::V0_SRGB;
break;
- case ui::Dataspace::V0_SRGB_LINEAR:
- dataSpace = ui::Dataspace::SRGB_LINEAR;
+ case ui::Dataspace::SRGB_LINEAR:
+ dataSpace = ui::Dataspace::V0_SRGB_LINEAR;
break;
- case ui::Dataspace::V0_JFIF:
- dataSpace = ui::Dataspace::JFIF;
+ case ui::Dataspace::JFIF:
+ dataSpace = ui::Dataspace::V0_JFIF;
break;
- case ui::Dataspace::V0_BT601_625:
- dataSpace = ui::Dataspace::BT601_625;
+ case ui::Dataspace::BT601_625:
+ dataSpace = ui::Dataspace::V0_BT601_625;
break;
- case ui::Dataspace::V0_BT601_525:
- dataSpace = ui::Dataspace::BT601_525;
+ case ui::Dataspace::BT601_525:
+ dataSpace = ui::Dataspace::V0_BT601_525;
break;
- case ui::Dataspace::V0_BT709:
- dataSpace = ui::Dataspace::BT709;
+ case ui::Dataspace::BT709:
+ dataSpace = ui::Dataspace::V0_BT709;
break;
default:
break;
}
mCurrentDataSpace = dataSpace;
- Rect crop(mConsumer->getCurrentCrop());
- const uint32_t transform(mConsumer->getCurrentTransform());
- const uint32_t scalingMode(mConsumer->getCurrentScalingMode());
- if ((crop != mCurrentCrop) ||
- (transform != mCurrentTransform) ||
- (scalingMode != mCurrentScalingMode)) {
+ Rect crop(getDrawingCrop());
+ const uint32_t transform(getDrawingTransform());
+ const uint32_t scalingMode(getDrawingScalingMode());
+ const bool transformToDisplayInverse(getTransformToDisplayInverse());
+ if ((crop != mCurrentCrop) || (transform != mCurrentTransform) ||
+ (scalingMode != mCurrentScalingMode) ||
+ (transformToDisplayInverse != mTransformToDisplayInverse)) {
mCurrentCrop = crop;
mCurrentTransform = transform;
mCurrentScalingMode = scalingMode;
+ mTransformToDisplayInverse = transformToDisplayInverse;
recomputeVisibleRegions = true;
}
if (oldBuffer != nullptr) {
- uint32_t bufWidth = getBE().compositionInfo.mBuffer->getWidth();
- uint32_t bufHeight = getBE().compositionInfo.mBuffer->getHeight();
- if (bufWidth != uint32_t(oldBuffer->width) ||
- bufHeight != uint32_t(oldBuffer->height)) {
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+ if (bufWidth != uint32_t(oldBuffer->width) || bufHeight != uint32_t(oldBuffer->height)) {
recomputeVisibleRegions = true;
}
}
- mCurrentOpacity = getOpacityForFormat(getBE().compositionInfo.mBuffer->format);
if (oldOpacity != isOpaque(s)) {
recomputeVisibleRegions = true;
}
@@ -602,307 +525,36 @@
}
}
- // FIXME: postedRegion should be dirty & bounds
- Region dirtyRegion(Rect(s.active.w, s.active.h));
-
- // transform the dirty region to window-manager space
- outDirtyRegion = (getTransform().transform(dirtyRegion));
-
- return outDirtyRegion;
-}
-
-void BufferLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
- mConsumer->setDefaultBufferSize(w, h);
-}
-
-void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
- // Apply this display's projection's viewport to the visible region
- // before giving it to the HWC HAL.
- const Transform& tr = displayDevice->getTransform();
- const auto& viewport = displayDevice->getViewport();
- Region visible = tr.transform(visibleRegion.intersect(viewport));
- auto hwcId = displayDevice->getHwcDisplayId();
- if (!hasHwcLayer(hwcId)) {
- return;
- }
- auto& hwcInfo = getBE().mHwcLayers[hwcId];
- auto& hwcLayer = hwcInfo.layer;
- auto error = hwcLayer->setVisibleRegion(visible);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
- to_string(error).c_str(), static_cast<int32_t>(error));
- visible.dump(LOG_TAG);
- }
-
- error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
- to_string(error).c_str(), static_cast<int32_t>(error));
- surfaceDamageRegion.dump(LOG_TAG);
- }
-
- // Sideband layers
- if (getBE().compositionInfo.hwc.sidebandStream.get()) {
- setCompositionType(hwcId, HWC2::Composition::Sideband);
- ALOGV("[%s] Requesting Sideband composition", mName.string());
- error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
- getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- }
- return;
- }
-
- // Device or Cursor layers
- if (mPotentialCursor) {
- ALOGV("[%s] Requesting Cursor composition", mName.string());
- setCompositionType(hwcId, HWC2::Composition::Cursor);
- } else {
- ALOGV("[%s] Requesting Device composition", mName.string());
- setCompositionType(hwcId, HWC2::Composition::Device);
- }
-
- ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
- error = hwcLayer->setDataspace(mCurrentDataSpace);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
-
- const HdrMetadata& metadata = mConsumer->getCurrentHdrMetadata();
- error = hwcLayer->setPerFrameMetadata(displayDevice->getSupportedPerFrameMetadata(), metadata);
- if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
- ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
-
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- hwcInfo.bufferCache.getHwcBuffer(getBE().compositionInfo.mBufferSlot,
- getBE().compositionInfo.mBuffer, &hwcSlot, &hwcBuffer);
-
- auto acquireFence = mConsumer->getCurrentFence();
- error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
- getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(),
- static_cast<int32_t>(error));
- }
-}
-
-bool BufferLayer::isOpaque(const Layer::State& s) const {
- // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
- // layer's opaque flag.
- if ((getBE().compositionInfo.hwc.sidebandStream == nullptr) && (getBE().compositionInfo.mBuffer == nullptr)) {
- return false;
- }
-
- // if the layer has the opaque flag, then we're always opaque,
- // otherwise we use the current buffer's format.
- return ((s.flags & layer_state_t::eLayerOpaque) != 0) || mCurrentOpacity;
-}
-
-void BufferLayer::onFirstRef() {
- Layer::onFirstRef();
-
- // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer, true);
- mProducer = new MonitoredProducer(producer, mFlinger, this);
- {
- // Grab the SF state lock during this since it's the only safe way to access RenderEngine
- Mutex::Autolock lock(mFlinger->mStateLock);
- mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName,
- this);
- }
- mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
- mConsumer->setContentsChangedListener(this);
- mConsumer->setName(mName);
-
- if (mFlinger->isLayerTripleBufferingDisabled()) {
- mProducer->setMaxDequeuedBufferCount(2);
- }
-
- const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
- updateTransformHint(hw);
-}
-
-// ---------------------------------------------------------------------------
-// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
-// ---------------------------------------------------------------------------
-
-void BufferLayer::onFrameAvailable(const BufferItem& item) {
- // Add this buffer from our internal queue tracker
- { // Autolock scope
- Mutex::Autolock lock(mQueueItemLock);
- mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
- item.mGraphicBuffer->getHeight(),
- item.mFrameNumber);
- // Reset the frame number tracker when we receive the first buffer after
- // a frame number reset
- if (item.mFrameNumber == 1) {
- mLastFrameNumberReceived = 0;
- }
-
- // Ensure that callbacks are handled in order
- while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
- status_t result = mQueueItemCondition.waitRelative(mQueueItemLock,
- ms2ns(500));
- if (result != NO_ERROR) {
- ALOGE("[%s] Timed out waiting on callback", mName.string());
- }
- }
-
- mQueueItems.push_back(item);
- android_atomic_inc(&mQueuedFrames);
-
- // Wake up any pending callbacks
- mLastFrameNumberReceived = item.mFrameNumber;
- mQueueItemCondition.broadcast();
- }
-
- mFlinger->signalLayerUpdate();
-}
-
-void BufferLayer::onFrameReplaced(const BufferItem& item) {
- { // Autolock scope
- Mutex::Autolock lock(mQueueItemLock);
-
- // Ensure that callbacks are handled in order
- while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
- status_t result = mQueueItemCondition.waitRelative(mQueueItemLock,
- ms2ns(500));
- if (result != NO_ERROR) {
- ALOGE("[%s] Timed out waiting on callback", mName.string());
- }
- }
-
- if (mQueueItems.empty()) {
- ALOGE("Can't replace a frame on an empty queue");
- return;
- }
- mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
-
- // Wake up any pending callbacks
- mLastFrameNumberReceived = item.mFrameNumber;
- mQueueItemCondition.broadcast();
- }
-}
-
-void BufferLayer::onSidebandStreamChanged() {
- if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
- // mSidebandStreamChanged was false
- mFlinger->signalLayerUpdate();
- }
-}
-
-bool BufferLayer::needsFiltering(const RenderArea& renderArea) const {
- return mNeedsFiltering || renderArea.needsFiltering();
-}
-
-// As documented in libhardware header, formats in the range
-// 0x100 - 0x1FF are specific to the HAL implementation, and
-// are known to have no alpha channel
-// TODO: move definition for device-specific range into
-// hardware.h, instead of using hard-coded values here.
-#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-
-bool BufferLayer::getOpacityForFormat(uint32_t format) {
- if (HARDWARE_IS_DEVICE_FORMAT(format)) {
- return true;
- }
- switch (format) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- case HAL_PIXEL_FORMAT_RGBA_FP16:
- case HAL_PIXEL_FORMAT_RGBA_1010102:
- return false;
- }
- // in all other case, we have no blending (also for unknown formats)
return true;
}
-bool BufferLayer::isHdrY410() const {
- // pixel format is HDR Y410 masquerading as RGBA_1010102
- return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ &&
- mConsumer->getCurrentApi() == NATIVE_WINDOW_API_MEDIA &&
- getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
-}
-
-void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
- ATRACE_CALL();
- const State& s(getDrawingState());
-
- computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
-
- /*
- * NOTE: the way we compute the texture coordinates here produces
- * different results than when we take the HWC path -- in the later case
- * the "source crop" is rounded to texel boundaries.
- * This can produce significantly different results when the texture
- * is scaled by a large amount.
- *
- * The GL code below is more logical (imho), and the difference with
- * HWC is due to a limitation of the HWC API to integers -- a question
- * is suspend is whether we should ignore this problem or revert to
- * GL composition when a buffer scaling is applied (maybe with some
- * minimal value)? Or, we could make GL behave like HWC -- but this feel
- * like more of a hack.
- */
- const Rect bounds{computeBounds()}; // Rounds from FloatRect
-
- Transform t = getTransform();
- Rect win = bounds;
- if (!s.finalCrop.isEmpty()) {
- win = t.transform(win);
- if (!win.intersect(s.finalCrop, &win)) {
- win.clear();
- }
- win = t.inverse().transform(win);
- if (!win.intersect(bounds, &win)) {
- win.clear();
+// transaction
+void BufferLayer::notifyAvailableFrames() {
+ auto headFrameNumber = getHeadFrameNumber();
+ bool headFenceSignaled = fenceHasSignaled();
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+ for (auto& point : mLocalSyncPoints) {
+ if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) {
+ point->setFrameAvailable();
}
}
-
- float left = float(win.left) / float(s.active.w);
- float top = float(win.top) / float(s.active.h);
- float right = float(win.right) / float(s.active.w);
- float bottom = float(win.bottom) / float(s.active.h);
-
- // TODO: we probably want to generate the texture coords with the mesh
- // here we assume that we only have 4 vertices
- Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(left, 1.0f - top);
- texCoords[1] = vec2(left, 1.0f - bottom);
- texCoords[2] = vec2(right, 1.0f - bottom);
- texCoords[3] = vec2(right, 1.0f - top);
-
- auto& engine(mFlinger->getRenderEngine());
- engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
- getColor());
- engine.setSourceDataSpace(mCurrentDataSpace);
-
- if (isHdrY410()) {
- engine.setSourceY410BT2020(true);
- }
-
- engine.drawMesh(getBE().mMesh);
- engine.disableBlending();
-
- engine.setSourceY410BT2020(false);
}
-uint32_t BufferLayer::getProducerStickyTransform() const {
- int producerStickyTransform = 0;
- int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform);
- if (ret != OK) {
- ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__,
- strerror(-ret), ret);
- return 0;
+bool BufferLayer::hasReadyFrame() const {
+ return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
+}
+
+uint32_t BufferLayer::getEffectiveScalingMode() const {
+ if (mOverrideScalingMode >= 0) {
+ return mOverrideScalingMode;
}
- return static_cast<uint32_t>(producerStickyTransform);
+
+ return mCurrentScalingMode;
+}
+
+bool BufferLayer::isProtected() const {
+ const sp<GraphicBuffer>& buffer(mActiveBuffer);
+ return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
}
bool BufferLayer::latchUnsignaledBuffers() {
@@ -919,65 +571,7 @@
return latch;
}
-uint64_t BufferLayer::getHeadFrameNumber() const {
- Mutex::Autolock lock(mQueueItemLock);
- if (!mQueueItems.empty()) {
- return mQueueItems[0].mFrameNumber;
- } else {
- return mCurrentFrameNumber;
- }
-}
-
-bool BufferLayer::headFenceHasSignaled() const {
- if (latchUnsignaledBuffers()) {
- return true;
- }
-
- Mutex::Autolock lock(mQueueItemLock);
- if (mQueueItems.empty()) {
- return true;
- }
- if (mQueueItems[0].mIsDroppable) {
- // Even though this buffer's fence may not have signaled yet, it could
- // be replaced by another buffer before it has a chance to, which means
- // that it's possible to get into a situation where a buffer is never
- // able to be latched. To avoid this, grab this buffer anyway.
- return true;
- }
- return mQueueItems[0].mFenceTime->getSignalTime() !=
- Fence::SIGNAL_TIME_PENDING;
-}
-
-uint32_t BufferLayer::getEffectiveScalingMode() const {
- if (mOverrideScalingMode >= 0) {
- return mOverrideScalingMode;
- }
- return mCurrentScalingMode;
-}
-
-// ----------------------------------------------------------------------------
-// transaction
-// ----------------------------------------------------------------------------
-
-void BufferLayer::notifyAvailableFrames() {
- auto headFrameNumber = getHeadFrameNumber();
- bool headFenceSignaled = headFenceHasSignaled();
- Mutex::Autolock lock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) {
- point->setFrameAvailable();
- }
- }
-}
-
-sp<IGraphicBufferProducer> BufferLayer::getProducer() const {
- return mProducer;
-}
-
-// ---------------------------------------------------------------------------
// h/w composer set-up
-// ---------------------------------------------------------------------------
-
bool BufferLayer::allTransactionsSignaled() {
auto headFrameNumber = getHeadFrameNumber();
bool matchingFramesFound = false;
@@ -1004,6 +598,121 @@
return !matchingFramesFound || allTransactionsApplied;
}
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool BufferLayer::getOpacityForFormat(uint32_t format) {
+ if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+ return true;
+ }
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
+ return false;
+ }
+ // in all other case, we have no blending (also for unknown formats)
+ 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, we
+ // only return mNeedsFiltering
+ if (displayDevice == nullptr) {
+ return mNeedsFiltering;
+ }
+
+ const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ if (outputLayer == nullptr) {
+ return mNeedsFiltering;
+ }
+
+ const auto& compositionState = outputLayer->getState();
+ const auto displayFrame = compositionState.displayFrame;
+ const auto sourceCrop = compositionState.sourceCrop;
+ return mNeedsFiltering || sourceCrop.getHeight() != displayFrame.getHeight() ||
+ sourceCrop.getWidth() != displayFrame.getWidth();
+}
+
+uint64_t BufferLayer::getHeadFrameNumber() const {
+ if (hasFrameUpdate()) {
+ return getFrameNumber();
+ } else {
+ return mCurrentFrameNumber;
+ }
+}
+
+Rect BufferLayer::getBufferSize(const State& s) const {
+ // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+ // we cannot determine the buffer size.
+ if ((s.sidebandStream != nullptr) ||
+ (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+ return Rect(getActiveWidth(s), getActiveHeight(s));
+ }
+
+ if (mActiveBuffer == nullptr) {
+ return Rect::INVALID_RECT;
+ }
+
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mCurrentTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return Rect(bufWidth, bufHeight);
+}
+
+std::shared_ptr<compositionengine::Layer> BufferLayer::getCompositionLayer() const {
+ return mCompositionLayer;
+}
+
+FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+ const State& s(getDrawingState());
+
+ // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+ // we cannot determine the buffer size.
+ if ((s.sidebandStream != nullptr) ||
+ (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+ return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ }
+
+ if (mActiveBuffer == nullptr) {
+ return parentBounds;
+ }
+
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mCurrentTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return FloatRect(0, 0, bufWidth, bufHeight);
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index bf0ca69..dc103cb 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -16,183 +16,180 @@
#pragma once
-#include "BufferLayerConsumer.h"
-#include "Client.h"
-#include "Layer.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWComposerBufferCache.h"
-#include "FrameTracker.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "RenderEngine/Mesh.h"
-#include "RenderEngine/Texture.h"
-#include "SurfaceFlinger.h"
-#include "Transform.h"
+#include <sys/types.h>
+#include <cstdint>
+#include <list>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
-
+#include <renderengine/Image.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
+#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
-
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Timers.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <list>
+#include "BufferLayerConsumer.h"
+#include "Client.h"
+#include "DisplayHardware/HWComposer.h"
+#include "FrameTracker.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "MonitoredProducer.h"
+#include "SurfaceFlinger.h"
namespace android {
-/*
- * A new BufferQueue and a new BufferLayerConsumer are created when the
- * BufferLayer is first referenced.
- *
- * This also implements onFrameAvailable(), which notifies SurfaceFlinger
- * that new data has arrived.
- */
-class BufferLayer : public Layer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferLayer : public Layer {
public:
- BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
- uint32_t h, uint32_t flags);
-
+ explicit BufferLayer(const LayerCreationArgs& args);
~BufferLayer() override;
- // If we have received a new buffer this frame, we will pass its surface
- // damage down to hardware composer. Otherwise, we must send a region with
- // one empty rect.
- void useSurfaceDamage();
- void useEmptyDamage();
-
// -----------------------------------------------------------------------
// Overriden from Layer
// -----------------------------------------------------------------------
-
- /*
- * getTypeId - Provide unique string for each class type in the Layer
- * hierarchy
- */
- const char* getTypeId() const override { return "BufferLayer"; }
-
- /*
- * isProtected - true if the layer may contain protected content in the
- * GRALLOC_USAGE_PROTECTED sense.
- */
- bool isProtected() const;
-
- /*
- * isVisible - true if this layer is visible, false otherwise
- */
- bool isVisible() const override;
-
- /*
- * isFixedSize - true if content has a fixed size
- */
- bool isFixedSize() const override;
-
- // the this layer's size and format
- status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
-
- /*
- * onDraw - draws the surface.
- */
- void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) const override;
-
- void onLayerDisplayed(const sp<Fence>& releaseFence) override;
-
- void abandon() override;
- bool shouldPresentNow(const DispSync& dispSync) const override;
- void setTransformHint(uint32_t orientation) const override;
- bool onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence,
- const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming& compositorTiming) override;
- std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
- bool getTransformToDisplayInverse() const override;
-
public:
- bool onPreComposition(nsecs_t refreshStartTime) override;
+ std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
- // If a buffer was replaced this frame, release the former buffer
- void releasePendingBuffer(nsecs_t dequeueReadyTime);
+ // If we have received a new buffer this frame, we will pass its surface
+ // damage down to hardware composer. Otherwise, we must send a region with
+ // one empty rect.
+ void useSurfaceDamage() override;
+ void useEmptyDamage() override;
- /*
- * latchBuffer - called each time the screen is redrawn and returns whether
- * the visible regions need to be recomputed (this is a fairly heavy
- * operation, so this should be set only if needed). Typically this is used
- * to figure out if the content or size of a surface has changed.
- */
- Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
- bool isBufferLatched() const override { return mRefreshPending; }
- void setDefaultBufferSize(uint32_t w, uint32_t h) override;
-
- bool isHdrY410() const override;
-
- void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
+ // getTypeId - Provide unique string for each class type in the Layer
+ // hierarchy
+ const char* getTypeId() const override { return "BufferLayer"; }
bool isOpaque(const Layer::State& s) const override;
-private:
- void onFirstRef() override;
+ // isVisible - true if this layer is visible, false otherwise
+ bool isVisible() const override;
- // Interface implementation for
- // BufferLayerConsumer::ContentsChangedListener
- void onFrameAvailable(const BufferItem& item) override;
- void onFrameReplaced(const BufferItem& item) override;
- void onSidebandStreamChanged() override;
+ // isProtected - true if the layer may contain protected content in the
+ // GRALLOC_USAGE_PROTECTED sense.
+ bool isProtected() const override;
- // needsLinearFiltering - true if this surface's state requires filtering
- bool needsFiltering(const RenderArea& renderArea) const;
+ // isFixedSize - true if content has a fixed size
+ bool isFixedSize() const override;
- static bool getOpacityForFormat(uint32_t format);
+ bool usesSourceCrop() const override;
- // drawing
- void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const;
+ bool isHdrY410() const override;
- // Temporary - Used only for LEGACY camera mode.
- uint32_t getProducerStickyTransform() const;
+ void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
+ const Rect& viewport, int32_t supportedPerFrameMetadata,
+ const ui::Dataspace targetDataspace) override;
- // Loads the corresponding system property once per process
- static bool latchUnsignaledBuffers();
+ bool onPreComposition(nsecs_t refreshStartTime) override;
+ bool onPostComposition(const std::optional<DisplayId>& displayId,
+ const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const CompositorTiming& compositorTiming) override;
- uint64_t getHeadFrameNumber() const;
- bool headFenceHasSignaled() const;
+ // latchBuffer - called each time the screen is redrawn and returns whether
+ // the visible regions need to be recomputed (this is a fairly heavy
+ // operation, so this should be set only if needed). Typically this is used
+ // to figure out if the content or size of a surface has changed.
+ bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+
+ bool isBufferLatched() const override { return mRefreshPending; }
+
+ void notifyAvailableFrames() override;
+
+ bool hasReadyFrame() const override;
// Returns the current scaling mode, unless mOverrideScalingMode
// is set, in which case, it returns mOverrideScalingMode
uint32_t getEffectiveScalingMode() const override;
+ // -----------------------------------------------------------------------
-public:
- void notifyAvailableFrames() override;
-
- PixelFormat getPixelFormat() const override { return mFormat; }
- sp<IGraphicBufferProducer> getProducer() const;
-
+ // -----------------------------------------------------------------------
+ // Functions that must be implemented by derived classes
+ // -----------------------------------------------------------------------
private:
- sp<BufferLayerConsumer> mConsumer;
+ virtual bool fenceHasSignaled() const = 0;
+
+ virtual nsecs_t getDesiredPresentTime() = 0;
+ virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0;
+
+ virtual void getDrawingTransformMatrix(float *matrix) = 0;
+ virtual uint32_t getDrawingTransform() const = 0;
+ virtual ui::Dataspace getDrawingDataSpace() const = 0;
+ virtual Rect getDrawingCrop() const = 0;
+ virtual uint32_t getDrawingScalingMode() const = 0;
+ virtual Region getDrawingSurfaceDamage() const = 0;
+ virtual const HdrMetadata& getDrawingHdrMetadata() const = 0;
+ virtual int getDrawingApi() const = 0;
+ virtual PixelFormat getPixelFormat() const = 0;
+
+ virtual uint64_t getFrameNumber() const = 0;
+
+ virtual bool getAutoRefresh() const = 0;
+ virtual bool getSidebandStreamChanged() const = 0;
+
+ // Latch sideband stream and returns true if the dirty region should be updated.
+ virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
+
+ virtual bool hasFrameUpdate() const = 0;
+
+ virtual void setFilteringEnabled(bool enabled) = 0;
+
+ virtual status_t bindTextureImage() = 0;
+ virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) = 0;
+
+ virtual status_t updateActiveBuffer() = 0;
+ virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+
+ virtual void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) = 0;
+
+protected:
+ // 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
bool allTransactionsSignaled();
- sp<IGraphicBufferProducer> mProducer;
- // constants
- uint32_t mTextureName; // from GLES
- PixelFormat mFormat;
+ static bool getOpacityForFormat(uint32_t format);
- // main thread
- uint32_t mCurrentScalingMode;
- bool mBufferLatched = false; // TODO: Use mActiveBuffer?
- uint64_t mPreviousFrameNumber; // Only accessed on the main thread.
- // The texture used to draw the layer in GLES composition mode
- mutable Texture mTexture;
+ // from GLES
+ const uint32_t mTextureName;
- bool mUpdateTexImageFailed; // This is only accessed on the main thread.
- bool mRefreshPending;
+ bool mRefreshPending{false};
+
+ // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
+ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer) override;
+
+private:
+ // Returns true if this layer requires filtering
+ bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const;
+
+ uint64_t getHeadFrameNumber() const;
+
+ uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+
+ bool mTransformToDisplayInverse{false};
+
+ // main thread.
+ bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
+
+ // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
+ // and its parent layer is not bounded
+ Rect getBufferSize(const State& s) const override;
+
+ std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+
+ FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 87333d0..cc78ece 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -20,11 +20,8 @@
//#define LOG_NDEBUG 0
#include "BufferLayerConsumer.h"
-
-#include "DispSync.h"
#include "Layer.h"
-#include "RenderEngine/Image.h"
-#include "RenderEngine/RenderEngine.h"
+#include "Scheduler/DispSync.h"
#include <inttypes.h>
@@ -38,10 +35,9 @@
#include <gui/GLConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
-
#include <private/gui/ComposerService.h>
-#include <private/gui/SyncFeatures.h>
-
+#include <renderengine/Image.h>
+#include <renderengine/RenderEngine.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
@@ -58,7 +54,8 @@
static const mat4 mtxIdentity;
BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
- RE::RenderEngine& engine, uint32_t tex, Layer* layer)
+ renderengine::RenderEngine& engine, uint32_t tex,
+ Layer* layer)
: ConsumerBase(bq, false),
mCurrentCrop(Rect::EMPTY_RECT),
mCurrentTransform(0),
@@ -101,54 +98,7 @@
mContentsChangedListener = listener;
}
-// We need to determine the time when a buffer acquired now will be
-// displayed. This can be calculated:
-// time when previous buffer's actual-present fence was signaled
-// + current display refresh rate * HWC latency
-// + a little extra padding
-//
-// Buffer producers are expected to set their desired presentation time
-// based on choreographer time stamps, which (coming from vsync events)
-// will be slightly later then the actual-present timing. If we get a
-// desired-present time that is unintentionally a hair after the next
-// vsync, we'll hold the frame when we really want to display it. We
-// need to take the offset between actual-present and reported-vsync
-// into account.
-//
-// If the system is configured without a DispSync phase offset for the app,
-// we also want to throw in a bit of padding to avoid edge cases where we
-// just barely miss. We want to do it here, not in every app. A major
-// source of trouble is the app's use of the display's ideal refresh time
-// (via Display.getRefreshRate()), which could be off of the actual refresh
-// by a few percent, with the error multiplied by the number of frames
-// between now and when the buffer should be displayed.
-//
-// If the refresh reported to the app has a phase offset, we shouldn't need
-// to tweak anything here.
-nsecs_t BufferLayerConsumer::computeExpectedPresent(const DispSync& dispSync) {
- // The HWC doesn't currently have a way to report additional latency.
- // Assume that whatever we submit now will appear right after the flip.
- // For a smart panel this might be 1. This is expressed in frames,
- // rather than time, because we expect to have a constant frame delay
- // regardless of the refresh rate.
- const uint32_t hwcLatency = 0;
-
- // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
- const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
-
- // The DispSync time is already adjusted for the difference between
- // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
- // we don't need to factor that in here. Pad a little to avoid
- // weird effects if apps might be requesting times right on the edge.
- nsecs_t extraPadding = 0;
- if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
- extraPadding = 1000000; // 1ms (6% of 60Hz)
- }
-
- return nextRefresh + extraPadding;
-}
-
-status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
+status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
bool* autoRefresh, bool* queuedBuffer,
uint64_t maxFrameNumber) {
ATRACE_CALL();
@@ -160,18 +110,12 @@
return NO_INIT;
}
- // Make sure RenderEngine is current
- if (!mRE.isCurrent()) {
- BLC_LOGE("updateTexImage: RenderEngine is not current");
- return INVALID_OPERATION;
- }
-
BufferItem item;
// Acquire the next buffer.
// In asynchronous mode the list is guaranteed to be one buffer
// deep, while in synchronous mode we use the oldest buffer.
- status_t err = acquireBufferLocked(&item, computeExpectedPresent(dispSync), maxFrameNumber);
+ status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
if (err != NO_ERROR) {
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
err = NO_ERROR;
@@ -206,7 +150,7 @@
return err;
}
- if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+ if (!mRE.useNativeFenceSync()) {
// Bind the new buffer to the GL texture.
//
// Older devices require the "implicit" synchronization provided
@@ -235,7 +179,7 @@
}
auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
- : mCurrentTextureImage->graphicBuffer();
+ : mCurrentTextureBuffer->graphicBuffer();
auto err = addReleaseFence(slot, buffer, fence);
if (err != OK) {
BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -271,56 +215,36 @@
}
// If item->mGraphicBuffer is not null, this buffer has not been acquired
- // before, so any prior EglImage created is using a stale buffer. This
- // replaces any old EglImage with a new one (using the new buffer).
+ // before, so we need to clean up old references.
if (item->mGraphicBuffer != nullptr) {
- mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE);
+ mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
}
return NO_ERROR;
}
-bool BufferLayerConsumer::canUseImageCrop(const Rect& crop) const {
- // If the crop rect is not at the origin, we can't set the crop on the
- // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
- // extension. In the future we can add a layered extension that
- // removes this restriction if there is hardware that can support it.
- return mRE.supportsImageCrop() && crop.left == 0 && crop.top == 0;
-}
-
status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
PendingRelease* pendingRelease) {
status_t err = NO_ERROR;
int slot = item.mSlot;
- // Do whatever sync ops we need to do before releasing the old slot.
- if (slot != mCurrentTexture) {
- err = syncForReleaseLocked();
- if (err != NO_ERROR) {
- // Release the buffer we just acquired. It's not safe to
- // release the old buffer, so instead we just drop the new frame.
- // As we are still under lock since acquireBuffer, it is safe to
- // release by slot.
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
- return err;
- }
- }
-
BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
- mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : 0,
+ (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr)
+ ? mCurrentTextureBuffer->graphicBuffer()->handle
+ : 0,
slot, mSlots[slot].mGraphicBuffer->handle);
// Hang onto the pointer so that it isn't freed in the call to
// releaseBufferLocked() if we're in shared buffer mode and both buffers are
// the same.
- sp<Image> nextTextureImage = mImages[slot];
+ std::shared_ptr<Image> nextTextureBuffer = mImages[slot];
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
if (pendingRelease == nullptr) {
status_t status =
- releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer());
+ releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer());
if (status < NO_ERROR) {
BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
status);
@@ -329,14 +253,14 @@
}
} else {
pendingRelease->currentTexture = mCurrentTexture;
- pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+ pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer();
pendingRelease->isPending = true;
}
}
// Update the BufferLayerConsumer state.
mCurrentTexture = slot;
- mCurrentTextureImage = nextTextureImage;
+ mCurrentTextureBuffer = nextTextureBuffer;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
@@ -357,51 +281,13 @@
status_t BufferLayerConsumer::bindTextureImageLocked() {
ATRACE_CALL();
- mRE.checkErrors();
- if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
- BLC_LOGE("bindTextureImage: no currently-bound texture");
- mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
- return NO_INIT;
+ if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) {
+ return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(),
+ mCurrentFence);
}
- const Rect& imageCrop = canUseImageCrop(mCurrentCrop) ? mCurrentCrop : Rect::EMPTY_RECT;
- status_t err = mCurrentTextureImage->createIfNeeded(imageCrop);
- if (err != NO_ERROR) {
- BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
- mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
- return UNKNOWN_ERROR;
- }
-
- mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image());
-
- // Wait for the new buffer to be ready.
- return doFenceWaitLocked();
-}
-
-status_t BufferLayerConsumer::syncForReleaseLocked() {
- BLC_LOGV("syncForReleaseLocked");
-
- if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- if (SyncFeatures::getInstance().useNativeFenceSync()) {
- base::unique_fd fenceFd = mRE.flush();
- if (fenceFd == -1) {
- BLC_LOGE("syncForReleaseLocked: failed to flush RenderEngine");
- return UNKNOWN_ERROR;
- }
- sp<Fence> fence(new Fence(std::move(fenceFd)));
- status_t err = addReleaseFenceLocked(mCurrentTexture,
- mCurrentTextureImage->graphicBuffer(), fence);
- if (err != OK) {
- BLC_LOGE("syncForReleaseLocked: error adding release fence: "
- "%s (%d)",
- strerror(-err), err);
- return err;
- }
- }
- }
-
- return OK;
+ return NO_INIT;
}
void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
@@ -418,26 +304,26 @@
bool needsRecompute = mFilteringEnabled != enabled;
mFilteringEnabled = enabled;
- if (needsRecompute && mCurrentTextureImage == nullptr) {
- BLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == nullptr");
+ if (needsRecompute && mCurrentTextureBuffer == nullptr) {
+ BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
}
- if (needsRecompute && mCurrentTextureImage != nullptr) {
+ if (needsRecompute && mCurrentTextureBuffer != nullptr) {
computeCurrentTransformMatrixLocked();
}
}
void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
BLC_LOGV("computeCurrentTransformMatrixLocked");
- sp<GraphicBuffer> buf =
- (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
- if (buf == nullptr) {
+ if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) {
BLC_LOGD("computeCurrentTransformMatrixLocked: "
- "mCurrentTextureImage is nullptr");
+ "mCurrentTextureBuffer is nullptr");
}
- const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop;
- GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform,
- mFilteringEnabled);
+ GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
+ mCurrentTextureBuffer == nullptr
+ ? nullptr
+ : mCurrentTextureBuffer->graphicBuffer(),
+ mCurrentCrop, mCurrentTransform, mFilteringEnabled);
}
nsecs_t BufferLayerConsumer::getTimestamp() {
@@ -478,14 +364,18 @@
return mCurrentApi;
}
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
+sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
Mutex::Autolock lock(mMutex);
if (outSlot != nullptr) {
*outSlot = mCurrentTexture;
}
- return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer();
+ if (outFence != nullptr) {
+ *outFence = mCurrentFence;
+ }
+
+ return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer();
}
Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -516,13 +406,8 @@
}
status_t BufferLayerConsumer::doFenceWaitLocked() const {
- if (!mRE.isCurrent()) {
- BLC_LOGE("doFenceWait: RenderEngine is not current");
- return INVALID_OPERATION;
- }
-
if (mCurrentFence->isValid()) {
- if (SyncFeatures::getInstance().useWaitSync()) {
+ if (mRE.useWaitSync()) {
base::unique_fd fenceFd(mCurrentFence->dup());
if (fenceFd == -1) {
BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno);
@@ -549,7 +434,7 @@
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
- mImages[slotIndex].clear();
+ mImages[slotIndex] = nullptr;
ConsumerBase::freeBufferLocked(slotIndex);
}
@@ -588,7 +473,10 @@
void BufferLayerConsumer::abandonLocked() {
BLC_LOGV("abandonLocked");
- mCurrentTextureImage.clear();
+ mCurrentTextureBuffer = nullptr;
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ mImages[i] = nullptr;
+ }
ConsumerBase::abandonLocked();
}
@@ -606,39 +494,11 @@
ConsumerBase::dumpLocked(result, prefix);
}
-BufferLayerConsumer::Image::Image(sp<GraphicBuffer> graphicBuffer, RE::RenderEngine& engine)
- : mGraphicBuffer(graphicBuffer),
- mImage{engine.createImage()},
- mCreated(false),
- mCropWidth(0),
- mCropHeight(0) {}
-
-BufferLayerConsumer::Image::~Image() = default;
-
-status_t BufferLayerConsumer::Image::createIfNeeded(const Rect& imageCrop) {
- const int32_t cropWidth = imageCrop.width();
- const int32_t cropHeight = imageCrop.height();
- if (mCreated && mCropWidth == cropWidth && mCropHeight == cropHeight) {
- return OK;
+BufferLayerConsumer::Image::~Image() {
+ if (mGraphicBuffer != nullptr) {
+ ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
+ mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
}
-
- mCreated = mImage->setNativeWindowBuffer(mGraphicBuffer->getNativeBuffer(),
- mGraphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED,
- cropWidth, cropHeight);
- if (mCreated) {
- mCropWidth = cropWidth;
- mCropHeight = cropHeight;
- } else {
- mCropWidth = 0;
- mCropHeight = 0;
-
- const sp<GraphicBuffer>& buffer = mGraphicBuffer;
- ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
- buffer->getPixelFormat());
- }
-
- return mCreated ? OK : UNKNOWN_ERROR;
}
}; // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index f81cdb1..32ccfbb 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -37,10 +37,10 @@
class Layer;
class String8;
-namespace RE {
+namespace renderengine {
class RenderEngine;
class Image;
-} // namespace RE
+} // namespace renderengine
/*
* BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
@@ -73,15 +73,13 @@
// BufferLayerConsumer constructs a new BufferLayerConsumer object. The
// tex parameter indicates the name of the RenderEngine texture to which
// images are to be streamed.
- BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, RE::RenderEngine& engine,
+ BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
uint32_t tex, Layer* layer);
// Sets the contents changed listener. This should be used instead of
// ConsumerBase::setFrameAvailableListener().
void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
- nsecs_t computeExpectedPresent(const DispSync& dispSync);
-
// updateTexImage acquires the most recently queued buffer, and sets the
// image contents of the target texture to it.
//
@@ -93,8 +91,8 @@
// Unlike the GLConsumer version, this version takes a functor that may be
// used to reject the newly acquired buffer. It also does not bind the
// RenderEngine texture until bindTextureImage is called.
- status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, bool* autoRefresh,
- bool* queuedBuffer, uint64_t maxFrameNumber);
+ status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
+ bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
// See BufferLayerConsumer::bindTextureImageLocked().
status_t bindTextureImage();
@@ -153,8 +151,9 @@
// getCurrentBuffer returns the buffer associated with the current image.
// When outSlot is not nullptr, the current buffer slot index is also
- // returned.
- sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
+ // returned. Simiarly, when outFence is not nullptr, the current output
+ // fence is returned.
+ sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
// getCurrentCrop returns the cropping rectangle of the current buffer.
Rect getCurrentCrop() const;
@@ -186,8 +185,7 @@
// specific info in addition to the ConsumerBase behavior.
virtual void dumpLocked(String8& result, const char* prefix) const;
- // acquireBufferLocked overrides the ConsumerBase method to update the
- // mImages array in addition to the ConsumerBase behavior.
+ // See ConsumerBase::acquireBufferLocked
virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) override;
@@ -210,54 +208,30 @@
status_t updateAndReleaseLocked(const BufferItem& item,
PendingRelease* pendingRelease = nullptr);
- // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target. Uses
- // mCurrentTexture if it's set, mCurrentTextureImage if not. If the
- // bind succeeds, this calls doFenceWait.
+ // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
+ // If the bind succeeds, this calls doFenceWait.
status_t bindTextureImageLocked();
private:
- // Image is a utility class for tracking and creating RE::Images. There
- // is primarily just one image per slot, but there is also special cases:
- // - After freeBuffer, we must still keep the current image/buffer
- // Reference counting RE::Images lets us handle all these cases easily while
- // also only creating new RE::Images from buffers when required.
- class Image : public LightRefBase<Image> {
+ // Utility class for managing GraphicBuffer references into renderengine
+ class Image {
public:
- Image(sp<GraphicBuffer> graphicBuffer, RE::RenderEngine& engine);
-
- Image(const Image& rhs) = delete;
- Image& operator=(const Image& rhs) = delete;
-
- // createIfNeeded creates an RE::Image if required (we haven't created
- // one yet, or the crop-rect has changed).
- status_t createIfNeeded(const Rect& imageCrop);
-
+ Image(sp<GraphicBuffer> graphicBuffer, renderengine::RenderEngine& engine)
+ : mGraphicBuffer(graphicBuffer), mRE(engine) {}
+ virtual ~Image();
const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
- const native_handle* graphicBufferHandle() {
- return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
- }
-
- const RE::Image& image() const { return *mImage; }
private:
- // Only allow instantiation using ref counting.
- friend class LightRefBase<Image>;
- virtual ~Image();
-
// mGraphicBuffer is the buffer that was used to create this image.
sp<GraphicBuffer> mGraphicBuffer;
-
- // mImage is the image created from mGraphicBuffer.
- std::unique_ptr<RE::Image> mImage;
- bool mCreated;
- int32_t mCropWidth;
- int32_t mCropHeight;
+ // Back-reference into renderengine to initiate cleanup.
+ renderengine::RenderEngine& mRE;
+ DISALLOW_COPY_AND_ASSIGN(Image);
};
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in
- // that slot and destroy the RE::Image in that slot. Otherwise it has no
- // effect.
+ // that slot. Otherwise it has no effect.
//
// This method must be called with mMutex locked.
virtual void freeBufferLocked(int slotIndex);
@@ -279,22 +253,16 @@
// access the current texture buffer.
status_t doFenceWaitLocked() const;
- // syncForReleaseLocked performs the synchronization needed to release the
- // current slot from RenderEngine. If needed it will set the current
- // slot's fence to guard against a producer accessing the buffer before
- // the outstanding accesses have completed.
- status_t syncForReleaseLocked();
-
// The default consumer usage flags that BufferLayerConsumer always sets on its
// BufferQueue instance; these will be OR:d with any additional flags passed
// from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
// consume buffers as hardware textures.
static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
- // mCurrentTextureImage is the Image/buffer of the current texture. It's
+ // mCurrentTextureBuffer is the buffer containing the current texture. It's
// possible that this buffer is not associated with any buffer slot, so we
// must track it separately in order to support the getCurrentBuffer method.
- sp<Image> mCurrentTextureImage;
+ std::shared_ptr<Image> mCurrentTextureBuffer;
// mCurrentCrop is the crop rectangle that applies to the current texture.
// It gets set each time updateTexImage is called.
@@ -352,7 +320,7 @@
// setFilteringEnabled().
bool mFilteringEnabled;
- RE::RenderEngine& mRE;
+ renderengine::RenderEngine& mRE;
// mTexName is the name of the RenderEngine texture to which streamed
// images will be bound when bindTexImage is called. It is set at
@@ -364,15 +332,6 @@
wp<ContentsChangedListener> mContentsChangedListener;
- // mImages stores the buffers that have been allocated by the BufferQueue
- // for each buffer slot. It is initialized to null pointers, and gets
- // filled in with the result of BufferQueue::acquire when the
- // client dequeues a buffer from a
- // slot that has not yet been used. The buffer allocated to a slot will also
- // be replaced if the requested buffer usage or geometry differs from that
- // of the buffer allocated to a slot.
- sp<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
// indicating that no buffer slot is currently bound to the texture. Note,
@@ -381,6 +340,9 @@
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;
+ // Shadow buffer cache for cleaning up renderengine references.
+ std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
// A release that is pending on the receipt of a new release fence from
// presentDisplay
PendingRelease mPendingRelease;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
new file mode 100644
index 0000000..e4179ee
--- /dev/null
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2018 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 <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <system/window.h>
+
+#include "BufferQueueLayer.h"
+#include "LayerRejecter.h"
+
+#include "TimeStats/TimeStats.h"
+
+namespace android {
+
+BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
+
+BufferQueueLayer::~BufferQueueLayer() {
+ mConsumer->abandon();
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for Layer
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+ mConsumer->setReleaseFence(releaseFence);
+}
+
+void BufferQueueLayer::setTransformHint(uint32_t orientation) const {
+ mConsumer->setTransformHint(orientation);
+}
+
+std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
+ std::vector<OccupancyTracker::Segment> history;
+ status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
+ if (result != NO_ERROR) {
+ ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(), result);
+ return {};
+ }
+ return history;
+}
+
+bool BufferQueueLayer::getTransformToDisplayInverse() const {
+ return mConsumer->getTransformToDisplayInverse();
+}
+
+void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+ if (!mConsumer->releasePendingBuffer()) {
+ return;
+ }
+
+ auto releaseFenceTime = std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence());
+ mReleaseTimeline.updateSignalTimes();
+ mReleaseTimeline.push(releaseFenceTime);
+
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ if (mPreviousFrameNumber != 0) {
+ mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
+ std::move(releaseFenceTime));
+ }
+}
+
+void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ mConsumer->setDefaultBufferSize(w, h);
+}
+
+int32_t BufferQueueLayer::getQueuedFrameCount() const {
+ return mQueuedFrames;
+}
+
+bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
+ if (getSidebandStreamChanged() || getAutoRefresh()) {
+ return true;
+ }
+
+ if (!hasFrameUpdate()) {
+ return false;
+ }
+
+ Mutex::Autolock lock(mQueueItemLock);
+
+ const int64_t addedTime = mQueueItems[0].mTimestamp;
+
+ // Ignore timestamps more than a second in the future
+ const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
+ ALOGW_IF(!isPlausible,
+ "[%s] Timestamp %" PRId64 " seems implausible "
+ "relative to expectedPresent %" PRId64,
+ mName.string(), addedTime, expectedPresentTime);
+
+ const bool isDue = addedTime < expectedPresentTime;
+ return isDue || !isPlausible;
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayer
+// -----------------------------------------------------------------------
+
+bool BufferQueueLayer::fenceHasSignaled() const {
+ if (latchUnsignaledBuffers()) {
+ return true;
+ }
+
+ if (!hasFrameUpdate()) {
+ return true;
+ }
+
+ Mutex::Autolock lock(mQueueItemLock);
+ if (mQueueItems[0].mIsDroppable) {
+ // Even though this buffer's fence may not have signaled yet, it could
+ // be replaced by another buffer before it has a chance to, which means
+ // that it's possible to get into a situation where a buffer is never
+ // able to be latched. To avoid this, grab this buffer anyway.
+ return true;
+ }
+ return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+}
+
+nsecs_t BufferQueueLayer::getDesiredPresentTime() {
+ return mConsumer->getTimestamp();
+}
+
+std::shared_ptr<FenceTime> BufferQueueLayer::getCurrentFenceTime() const {
+ return mConsumer->getCurrentFenceTime();
+}
+
+void BufferQueueLayer::getDrawingTransformMatrix(float *matrix) {
+ return mConsumer->getTransformMatrix(matrix);
+}
+
+// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's
+// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state
+// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's
+// current buffer so the consumer functions start with "getCurrent".
+//
+// This results in the rather confusing functions below.
+uint32_t BufferQueueLayer::getDrawingTransform() const {
+ return mConsumer->getCurrentTransform();
+}
+
+ui::Dataspace BufferQueueLayer::getDrawingDataSpace() const {
+ return mConsumer->getCurrentDataSpace();
+}
+
+Rect BufferQueueLayer::getDrawingCrop() const {
+ return mConsumer->getCurrentCrop();
+}
+
+uint32_t BufferQueueLayer::getDrawingScalingMode() const {
+ return mConsumer->getCurrentScalingMode();
+}
+
+Region BufferQueueLayer::getDrawingSurfaceDamage() const {
+ return mConsumer->getSurfaceDamage();
+}
+
+const HdrMetadata& BufferQueueLayer::getDrawingHdrMetadata() const {
+ return mConsumer->getCurrentHdrMetadata();
+}
+
+int BufferQueueLayer::getDrawingApi() const {
+ return mConsumer->getCurrentApi();
+}
+
+PixelFormat BufferQueueLayer::getPixelFormat() const {
+ return mFormat;
+}
+
+uint64_t BufferQueueLayer::getFrameNumber() const {
+ Mutex::Autolock lock(mQueueItemLock);
+ return mQueueItems[0].mFrameNumber;
+}
+
+bool BufferQueueLayer::getAutoRefresh() const {
+ return mAutoRefresh;
+}
+
+bool BufferQueueLayer::getSidebandStreamChanged() const {
+ return mSidebandStreamChanged;
+}
+
+bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+ bool sidebandStreamChanged = true;
+ if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
+ // mSidebandStreamChanged was changed to false
+ auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+ layerCompositionState.sidebandStream = mConsumer->getSidebandStream();
+ if (layerCompositionState.sidebandStream != nullptr) {
+ setTransactionFlags(eTransactionNeeded);
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ }
+ recomputeVisibleRegions = true;
+
+ return true;
+ }
+ return false;
+}
+
+bool BufferQueueLayer::hasFrameUpdate() const {
+ return mQueuedFrames > 0;
+}
+
+void BufferQueueLayer::setFilteringEnabled(bool enabled) {
+ return mConsumer->setFilteringEnabled(enabled);
+}
+
+status_t BufferQueueLayer::bindTextureImage() {
+ return mConsumer->bindTextureImage();
+}
+
+status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+ // This boolean is used to make sure that SurfaceFlinger's shadow copy
+ // of the buffer queue isn't modified when the buffer queue is returning
+ // BufferItem's that weren't actually queued. This can happen in shared
+ // buffer mode.
+ bool queuedBuffer = false;
+ const int32_t layerID = getSequence();
+ LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+ getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
+ getTransformToDisplayInverse(), mFreezeGeometryUpdates);
+
+ nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime();
+
+ if (isRemovedFromCurrentState()) {
+ expectedPresentTime = 0;
+ }
+
+ // updateTexImage() below might drop the some buffers at the head of the queue if there is a
+ // buffer behind them which is timely to be presented. However this buffer may not be signaled
+ // yet. The code below makes sure that this wouldn't happen by setting maxFrameNumber to the
+ // last buffer that was signaled.
+ uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived;
+ {
+ Mutex::Autolock lock(mQueueItemLock);
+ for (int i = 0; i < mQueueItems.size(); i++) {
+ bool fenceSignaled =
+ mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ if (!fenceSignaled) {
+ break;
+ }
+ lastSignaledFrameNumber = mQueueItems[i].mFrameNumber;
+ }
+ }
+ const uint64_t maxFrameNumberToAcquire =
+ std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber);
+
+ status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh,
+ &queuedBuffer, maxFrameNumberToAcquire);
+ if (updateResult == BufferQueue::PRESENT_LATER) {
+ // Producer doesn't want buffer to be displayed yet. Signal a
+ // layer update so we check again at the next opportunity.
+ mFlinger->signalLayerUpdate();
+ return BAD_VALUE;
+ } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
+ // If the buffer has been rejected, remove it from the shadow queue
+ // and return early
+ if (queuedBuffer) {
+ Mutex::Autolock lock(mQueueItemLock);
+ mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mQueueItems.removeAt(0);
+ mQueuedFrames--;
+ }
+ return BAD_VALUE;
+ } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
+ // This can occur if something goes wrong when trying to create the
+ // EGLImage for this buffer. If this happens, the buffer has already
+ // been released, so we need to clean up the queue and bug out
+ // early.
+ if (queuedBuffer) {
+ Mutex::Autolock lock(mQueueItemLock);
+ mQueueItems.clear();
+ mQueuedFrames = 0;
+ mFlinger->mTimeStats->onDestroy(layerID);
+ }
+
+ // Once we have hit this state, the shadow queue may no longer
+ // correctly reflect the incoming BufferQueue's contents, so even if
+ // updateTexImage starts working, the only safe course of action is
+ // to continue to ignore updates.
+ mUpdateTexImageFailed = true;
+
+ return BAD_VALUE;
+ }
+
+ if (queuedBuffer) {
+ // Autolock scope
+ auto currentFrameNumber = mConsumer->getFrameNumber();
+
+ Mutex::Autolock lock(mQueueItemLock);
+
+ // Remove any stale buffers that have been dropped during
+ // updateTexImage
+ while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+ mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mQueueItems.removeAt(0);
+ mQueuedFrames--;
+ }
+
+ mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
+ mQueueItems[0].mFenceTime);
+ mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
+
+ mQueueItems.removeAt(0);
+ }
+
+ // Decrement the queued-frames count. Signal another event if we
+ // have more frames pending.
+ if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) {
+ mFlinger->signalLayerUpdate();
+ }
+
+ return NO_ERROR;
+}
+
+status_t BufferQueueLayer::updateActiveBuffer() {
+ // update the active buffer
+ mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
+ auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+ layerCompositionState.buffer = mActiveBuffer;
+ layerCompositionState.bufferSlot = mActiveBufferSlot;
+
+ if (mActiveBuffer == nullptr) {
+ // this can only happen if the very first buffer was rejected.
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
+ mPreviousFrameNumber = mCurrentFrameNumber;
+ mCurrentFrameNumber = mConsumer->getFrameNumber();
+
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+ }
+ return NO_ERROR;
+}
+
+void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer);
+ LOG_FATAL_IF(!outputLayer->getState.hwc);
+ auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
+
+ uint32_t hwcSlot = 0;
+ sp<GraphicBuffer> hwcBuffer;
+ (*outputLayer->editState().hwc)
+ .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);
+
+ auto acquireFence = mConsumer->getCurrentFence();
+ auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), mActiveBuffer->handle,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+ layerCompositionState.bufferSlot = mActiveBufferSlot;
+ layerCompositionState.buffer = mActiveBuffer;
+ layerCompositionState.acquireFence = acquireFence;
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::fakeVsync() {
+ mRefreshPending = false;
+ bool ignored = false;
+ latchBuffer(ignored, systemTime());
+ usleep(16000);
+ releasePendingBuffer(systemTime());
+}
+
+void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
+ // Add this buffer from our internal queue tracker
+ { // Autolock scope
+ if (mFlinger->mUseSmart90ForVideo) {
+ // Report mApi ID for each layer.
+ mFlinger->mScheduler->addNativeWindowApi(item.mApi);
+ }
+
+ Mutex::Autolock lock(mQueueItemLock);
+ // Reset the frame number tracker when we receive the first buffer after
+ // a frame number reset
+ if (item.mFrameNumber == 1) {
+ mLastFrameNumberReceived = 0;
+ }
+
+ // Ensure that callbacks are handled in order
+ while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
+ status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
+ if (result != NO_ERROR) {
+ ALOGE("[%s] Timed out waiting on callback", mName.string());
+ }
+ }
+
+ mQueueItems.push_back(item);
+ mQueuedFrames++;
+
+ // Wake up any pending callbacks
+ mLastFrameNumberReceived = item.mFrameNumber;
+ mQueueItemCondition.broadcast();
+ }
+
+ mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+ item.mGraphicBuffer->getHeight(), item.mFrameNumber);
+
+ // If this layer is orphaned, then we run a fake vsync pulse so that
+ // dequeueBuffer doesn't block indefinitely.
+ if (isRemovedFromCurrentState()) {
+ fakeVsync();
+ } else {
+ mFlinger->signalLayerUpdate();
+ }
+}
+
+void BufferQueueLayer::onFrameReplaced(const BufferItem& item) {
+ { // Autolock scope
+ Mutex::Autolock lock(mQueueItemLock);
+
+ // Ensure that callbacks are handled in order
+ while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
+ status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
+ if (result != NO_ERROR) {
+ ALOGE("[%s] Timed out waiting on callback", mName.string());
+ }
+ }
+
+ if (!hasFrameUpdate()) {
+ ALOGE("Can't replace a frame on an empty queue");
+ return;
+ }
+ mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
+
+ // Wake up any pending callbacks
+ mLastFrameNumberReceived = item.mFrameNumber;
+ mQueueItemCondition.broadcast();
+ }
+}
+
+void BufferQueueLayer::onSidebandStreamChanged() {
+ bool sidebandStreamChanged = false;
+ if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) {
+ // mSidebandStreamChanged was changed to true
+ mFlinger->signalLayerUpdate();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::onFirstRef() {
+ BufferLayer::onFirstRef();
+
+ // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer, true);
+ mProducer = new MonitoredProducer(producer, mFlinger, this);
+ {
+ // Grab the SF state lock during this since it's the only safe way to access RenderEngine
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ mConsumer =
+ new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);
+ }
+ mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+ mConsumer->setContentsChangedListener(this);
+ mConsumer->setName(mName);
+
+ // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
+ if (!mFlinger->isLayerTripleBufferingDisabled()) {
+ mProducer->setMaxDequeuedBufferCount(2);
+ }
+
+ if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+ updateTransformHint(display);
+ }
+}
+
+status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
+ uint32_t const maxSurfaceDims =
+ std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims());
+
+ // never allow a surface larger than what our underlying GL implementation
+ // can handle.
+ if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) {
+ ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
+ return BAD_VALUE;
+ }
+
+ mFormat = format;
+
+ setDefaultBufferSize(w, h);
+ mConsumer->setDefaultBufferFormat(format);
+ mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+
+ return NO_ERROR;
+}
+
+sp<IGraphicBufferProducer> BufferQueueLayer::getProducer() const {
+ return mProducer;
+}
+
+uint32_t BufferQueueLayer::getProducerStickyTransform() const {
+ int producerStickyTransform = 0;
+ int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform);
+ if (ret != OK) {
+ ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__,
+ strerror(-ret), ret);
+ return 0;
+ }
+ return static_cast<uint32_t>(producerStickyTransform);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
new file mode 100644
index 0000000..a2aad17
--- /dev/null
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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 "BufferLayer.h"
+
+#include <utils/String8.h>
+
+namespace android {
+
+/*
+ * A new BufferQueue and a new BufferLayerConsumer are created when the
+ * BufferLayer is first referenced.
+ *
+ * This also implements onFrameAvailable(), which notifies SurfaceFlinger
+ * that new data has arrived.
+ */
+class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+public:
+ explicit BufferQueueLayer(const LayerCreationArgs&);
+ ~BufferQueueLayer() override;
+
+ // -----------------------------------------------------------------------
+ // Interface implementation for Layer
+ // -----------------------------------------------------------------------
+public:
+ void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+
+ void setTransformHint(uint32_t orientation) const override;
+
+ std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
+
+ bool getTransformToDisplayInverse() const override;
+
+ // If a buffer was replaced this frame, release the former buffer
+ void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
+
+ void setDefaultBufferSize(uint32_t w, uint32_t h) override;
+
+ int32_t getQueuedFrameCount() const override;
+
+ bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+ // -----------------------------------------------------------------------
+
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayer
+ // -----------------------------------------------------------------------
+public:
+ bool fenceHasSignaled() const override;
+
+private:
+ nsecs_t getDesiredPresentTime() override;
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
+
+ void getDrawingTransformMatrix(float *matrix) override;
+ uint32_t getDrawingTransform() const override;
+ ui::Dataspace getDrawingDataSpace() const override;
+ Rect getDrawingCrop() const override;
+ uint32_t getDrawingScalingMode() const override;
+ Region getDrawingSurfaceDamage() const override;
+ const HdrMetadata& getDrawingHdrMetadata() const override;
+ int getDrawingApi() const override;
+ PixelFormat getPixelFormat() const override;
+
+ uint64_t getFrameNumber() const override;
+
+ bool getAutoRefresh() const override;
+ bool getSidebandStreamChanged() const override;
+
+ bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+
+ bool hasFrameUpdate() const override;
+
+ void setFilteringEnabled(bool enabled) override;
+
+ status_t bindTextureImage() override;
+ status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+
+ status_t updateActiveBuffer() override;
+ status_t updateFrameNumber(nsecs_t latchTime) override;
+
+ void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) override;
+
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayerConsumer::ContentsChangedListener
+ // -----------------------------------------------------------------------
+protected:
+ void onFrameAvailable(const BufferItem& item) override;
+ void onFrameReplaced(const BufferItem& item) override;
+ void onSidebandStreamChanged() override;
+ // -----------------------------------------------------------------------
+
+public:
+ status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
+
+ sp<IGraphicBufferProducer> getProducer() const;
+
+private:
+ // Temporary - Used only for LEGACY camera mode.
+ uint32_t getProducerStickyTransform() const;
+
+ void onFirstRef() override;
+
+ sp<BufferLayerConsumer> mConsumer;
+ sp<IGraphicBufferProducer> mProducer;
+
+ PixelFormat mFormat{PIXEL_FORMAT_NONE};
+
+ // Only accessed on the main thread.
+ uint64_t mPreviousFrameNumber{0};
+ bool mUpdateTexImageFailed{false};
+
+ // Local copy of the queued contents of the incoming BufferQueue
+ mutable Mutex mQueueItemLock;
+ Condition mQueueItemCondition;
+ Vector<BufferItem> mQueueItems;
+ std::atomic<uint64_t> mLastFrameNumberReceived{0};
+
+ bool mAutoRefresh{false};
+ int mActiveBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+ // thread-safe
+ std::atomic<int32_t> mQueuedFrames{0};
+ std::atomic<bool> mSidebandStreamChanged{false};
+
+ void fakeVsync();
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
new file mode 100644
index 0000000..1ca0b02
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <limits>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <private/gui/SyncFeatures.h>
+#include <renderengine/Image.h>
+
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+#include "TimeStats/TimeStats.h"
+
+namespace android {
+
+// clang-format off
+const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+};
+// clang-format on
+
+BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {
+ mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
+}
+BufferStateLayer::~BufferStateLayer() {
+ if (mActiveBuffer != nullptr) {
+ auto& engine(mFlinger->getRenderEngine());
+ engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
+ }
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for Layer
+// -----------------------------------------------------------------------
+void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+ // The previous release fence notifies the client that SurfaceFlinger is done with the previous
+ // buffer that was presented on this layer. The first transaction that came in this frame that
+ // replaced the previous buffer on this layer needs this release fence, because the fence will
+ // let the client know when that previous buffer is removed from the screen.
+ //
+ // Every other transaction on this layer does not need a release fence because no other
+ // Transactions that were set on this layer this frame are going to have their preceeding buffer
+ // removed from the display this frame.
+ //
+ // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
+ // buffer so it doesn't need a previous release fence because the layer still needs the previous
+ // buffer. The second transaction contains a buffer so it needs a previous release fence because
+ // the previous buffer will be released this frame. The third transaction also contains a
+ // buffer. It replaces the buffer in the second transaction. The buffer in the second
+ // transaction will now no longer be presented so it is released immediately and the third
+ // transaction doesn't need a previous release fence.
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->releasePreviousBuffer) {
+ handle->previousReleaseFence = releaseFence;
+ break;
+ }
+ }
+}
+
+void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
+ // TODO(marissaw): send the transform hint to buffer owner
+ return;
+}
+
+void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
+ mFlinger->getTransactionCompletedThread().addPresentedCallbackHandles(
+ mDrawingState.callbackHandles);
+
+ mDrawingState.callbackHandles = {};
+}
+
+bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
+ if (getSidebandStreamChanged() || getAutoRefresh()) {
+ return true;
+ }
+
+ return hasFrameUpdate();
+}
+
+bool BufferStateLayer::willPresentCurrentTransaction() const {
+ // Returns true if the most recent Transaction applied to CurrentState will be presented.
+ return getSidebandStreamChanged() || getAutoRefresh() ||
+ (mCurrentState.modified &&
+ (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr));
+}
+
+bool BufferStateLayer::getTransformToDisplayInverse() const {
+ return mCurrentState.transformToDisplayInverse;
+}
+
+void BufferStateLayer::pushPendingState() {
+ if (!mCurrentState.modified) {
+ return;
+ }
+ mPendingStates.push_back(mCurrentState);
+ ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+}
+
+bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
+ const bool stateUpdateAvailable = !mPendingStates.empty();
+ while (!mPendingStates.empty()) {
+ popPendingState(stateToCommit);
+ }
+ mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
+ mCurrentState.modified = false;
+ return stateUpdateAvailable;
+}
+
+// Crop that applies to the window
+Rect BufferStateLayer::getCrop(const Layer::State& /*s*/) const {
+ return Rect::INVALID_RECT;
+}
+
+bool BufferStateLayer::setTransform(uint32_t transform) {
+ if (mCurrentState.transform == transform) return false;
+ mCurrentState.sequence++;
+ mCurrentState.transform = transform;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
+ if (mCurrentState.transformToDisplayInverse == transformToDisplayInverse) return false;
+ mCurrentState.sequence++;
+ mCurrentState.transformToDisplayInverse = transformToDisplayInverse;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setCrop(const Rect& crop) {
+ Rect c = crop;
+ if (c.left < 0) {
+ c.left = 0;
+ }
+ if (c.top < 0) {
+ c.top = 0;
+ }
+ // If the width and/or height are < 0, make it [0, 0, -1, -1] so the equality comparision below
+ // treats all invalid rectangles the same.
+ if (!c.isValid()) {
+ c.makeInvalid();
+ }
+
+ if (mCurrentState.crop == c) return false;
+ mCurrentState.sequence++;
+ mCurrentState.crop = c;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setFrame(const Rect& frame) {
+ int x = frame.left;
+ int y = frame.top;
+ int w = frame.getWidth();
+ int h = frame.getHeight();
+
+ if (x < 0) {
+ x = 0;
+ w = frame.right;
+ }
+
+ if (y < 0) {
+ y = 0;
+ h = frame.bottom;
+ }
+
+ if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
+ mCurrentState.active.w == w && mCurrentState.active.h == h) {
+ return false;
+ }
+
+ if (!frame.isValid()) {
+ x = y = w = h = 0;
+ }
+ mCurrentState.active.transform.set(x, y);
+ mCurrentState.active.w = w;
+ mCurrentState.active.h = h;
+
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) {
+ if (mCurrentState.buffer) {
+ mReleasePreviousBuffer = true;
+ }
+
+ mCurrentState.buffer = buffer;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
+ // The acquire fences of BufferStateLayers have already signaled before they are set
+ mCallbackHandleAcquireTime = fence->getSignalTime();
+
+ mCurrentState.acquireFence = fence;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) {
+ if (mCurrentState.dataspace == dataspace) return false;
+ mCurrentState.dataspace = dataspace;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
+ if (mCurrentState.hdrMetadata == hdrMetadata) return false;
+ mCurrentState.sequence++;
+ mCurrentState.hdrMetadata = hdrMetadata;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+ mCurrentState.sequence++;
+ mCurrentState.surfaceDamageRegion = surfaceDamage;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setApi(int32_t api) {
+ if (mCurrentState.api == api) return false;
+ mCurrentState.sequence++;
+ mCurrentState.api = api;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+ if (mCurrentState.sidebandStream == sidebandStream) return false;
+ mCurrentState.sequence++;
+ mCurrentState.sidebandStream = sidebandStream;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ if (!mSidebandStreamChanged.exchange(true)) {
+ // mSidebandStreamChanged was false
+ mFlinger->signalLayerUpdate();
+ }
+ return true;
+}
+
+bool BufferStateLayer::setTransactionCompletedListeners(
+ const std::vector<sp<CallbackHandle>>& handles) {
+ // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+ if (handles.empty()) {
+ mReleasePreviousBuffer = false;
+ return false;
+ }
+
+ const bool willPresent = willPresentCurrentTransaction();
+
+ for (const auto& handle : handles) {
+ // If this transaction set a buffer on this layer, release its previous buffer
+ handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+ // If this layer will be presented in this frame
+ if (willPresent) {
+ // If this transaction set an acquire fence on this layer, set its acquire time
+ handle->acquireTime = mCallbackHandleAcquireTime;
+
+ // Notify the transaction completed thread that there is a pending latched callback
+ // handle
+ mFlinger->getTransactionCompletedThread().registerPendingCallbackHandle(handle);
+
+ // Store so latched time and release fence can be set
+ mCurrentState.callbackHandles.push_back(handle);
+
+ } else { // If this layer will NOT need to be relatched and presented this frame
+ // Notify the transaction completed thread this handle is done
+ mFlinger->getTransactionCompletedThread().addUnpresentedCallbackHandle(handle);
+ }
+ }
+
+ mReleasePreviousBuffer = false;
+ mCallbackHandleAcquireTime = -1;
+
+ return willPresent;
+}
+
+bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
+ mCurrentState.transparentRegionHint = transparent;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+Rect BufferStateLayer::getBufferSize(const State& s) const {
+ // for buffer state layers we use the display frame size as the buffer size.
+ if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
+ return Rect(getActiveWidth(s), getActiveHeight(s));
+ }
+
+ // if the display frame is not defined, use the parent bounds as the buffer size.
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ Rect parentBounds = Rect(p->getBounds(Region()));
+ if (!parentBounds.isEmpty()) {
+ return parentBounds;
+ }
+ }
+
+ return Rect::INVALID_RECT;
+}
+
+FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+ const State& s(getDrawingState());
+ // for buffer state layers we use the display frame size as the buffer size.
+ if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
+ return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ }
+
+ // if the display frame is not defined, use the parent bounds as the buffer size.
+ return parentBounds;
+}
+
+void BufferStateLayer::setPostTime(nsecs_t postTime) {
+ mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
+}
+
+void BufferStateLayer::setDesiredPresentTime(nsecs_t desiredPresentTime) {
+ mDesiredPresentTime = desiredPresentTime;
+}
+
+// -----------------------------------------------------------------------
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayer
+// -----------------------------------------------------------------------
+bool BufferStateLayer::fenceHasSignaled() const {
+ if (latchUnsignaledBuffers()) {
+ return true;
+ }
+
+ return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+}
+
+nsecs_t BufferStateLayer::getDesiredPresentTime() {
+ return mDesiredPresentTime;
+}
+
+std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const {
+ return std::make_shared<FenceTime>(getDrawingState().acquireFence);
+}
+
+void BufferStateLayer::getDrawingTransformMatrix(float *matrix) {
+ std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix), matrix);
+}
+
+uint32_t BufferStateLayer::getDrawingTransform() const {
+ return getDrawingState().transform;
+}
+
+ui::Dataspace BufferStateLayer::getDrawingDataSpace() const {
+ return getDrawingState().dataspace;
+}
+
+// Crop that applies to the buffer
+Rect BufferStateLayer::getDrawingCrop() const {
+ const State& s(getDrawingState());
+
+ if (s.crop.isEmpty() && s.buffer) {
+ return s.buffer->getBounds();
+ } else if (s.buffer) {
+ Rect crop = s.crop;
+ crop.left = std::max(crop.left, 0);
+ crop.top = std::max(crop.top, 0);
+ uint32_t bufferWidth = s.buffer->getWidth();
+ uint32_t bufferHeight = s.buffer->getHeight();
+ if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
+ bufferWidth <= std::numeric_limits<int32_t>::max()) {
+ crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
+ crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
+ }
+ if (!crop.isValid()) {
+ // Crop rect is out of bounds, return whole buffer
+ return s.buffer->getBounds();
+ }
+ return crop;
+ }
+ return s.crop;
+}
+
+uint32_t BufferStateLayer::getDrawingScalingMode() const {
+ return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
+
+Region BufferStateLayer::getDrawingSurfaceDamage() const {
+ return getDrawingState().surfaceDamageRegion;
+}
+
+const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const {
+ return getDrawingState().hdrMetadata;
+}
+
+int BufferStateLayer::getDrawingApi() const {
+ return getDrawingState().api;
+}
+
+PixelFormat BufferStateLayer::getPixelFormat() const {
+ if (!mActiveBuffer) {
+ return PIXEL_FORMAT_NONE;
+ }
+ return mActiveBuffer->format;
+}
+
+uint64_t BufferStateLayer::getFrameNumber() const {
+ return mFrameNumber;
+}
+
+bool BufferStateLayer::getAutoRefresh() const {
+ // TODO(marissaw): support shared buffer mode
+ return false;
+}
+
+bool BufferStateLayer::getSidebandStreamChanged() const {
+ return mSidebandStreamChanged.load();
+}
+
+bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
+ if (mSidebandStreamChanged.exchange(false)) {
+ const State& s(getDrawingState());
+ // mSidebandStreamChanged was true
+ LOG_ALWAYS_FATAL_IF(!getCompositionLayer());
+ mSidebandStream = s.sidebandStream;
+ getCompositionLayer()->editState().frontEnd.sidebandStream = mSidebandStream;
+ if (mSidebandStream != nullptr) {
+ setTransactionFlags(eTransactionNeeded);
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ }
+ recomputeVisibleRegions = true;
+
+ return true;
+ }
+ return false;
+}
+
+bool BufferStateLayer::hasFrameUpdate() const {
+ const State& c(getCurrentState());
+ return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
+}
+
+void BufferStateLayer::setFilteringEnabled(bool enabled) {
+ GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop,
+ mCurrentTransform, enabled);
+}
+
+status_t BufferStateLayer::bindTextureImage() {
+ const State& s(getDrawingState());
+ auto& engine(mFlinger->getRenderEngine());
+
+ return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
+}
+
+status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) {
+ const State& s(getDrawingState());
+
+ if (!s.buffer) {
+ if (s.bgColorLayer) {
+ for (auto& handle : mDrawingState.callbackHandles) {
+ handle->latchTime = latchTime;
+ }
+ }
+ return NO_ERROR;
+ }
+
+ const int32_t layerID = getSequence();
+
+ // Reject if the layer is invalid
+ uint32_t bufferWidth = s.buffer->width;
+ uint32_t bufferHeight = s.buffer->height;
+
+ if (s.transform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+
+ if (s.transformToDisplayInverse) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ if (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
+ (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
+ ALOGE("[%s] rejecting buffer: "
+ "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
+ mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
+ mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
+ return BAD_VALUE;
+ }
+
+ for (auto& handle : mDrawingState.callbackHandles) {
+ handle->latchTime = latchTime;
+ }
+
+ if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Bind the new buffer to the GL texture.
+ //
+ // Older devices require the "implicit" synchronization provided
+ // by glEGLImageTargetTexture2DOES, which this method calls. Newer
+ // devices will either call this in Layer::onDraw, or (if it's not
+ // a GL-composited layer) not at all.
+ status_t err = bindTextureImage();
+ if (err != NO_ERROR) {
+ mFlinger->mTimeStats->onDestroy(layerID);
+ return BAD_VALUE;
+ }
+ }
+
+ mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+ mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
+
+ return NO_ERROR;
+}
+
+status_t BufferStateLayer::updateActiveBuffer() {
+ const State& s(getDrawingState());
+
+ if (s.buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ if (mActiveBuffer != nullptr) {
+ // todo: get this to work with BufferStateLayerCache
+ auto& engine(mFlinger->getRenderEngine());
+ engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
+ }
+ mActiveBuffer = s.buffer;
+ mActiveBufferFence = s.acquireFence;
+ auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+ layerCompositionState.buffer = mActiveBuffer;
+ layerCompositionState.bufferSlot = 0;
+
+ return NO_ERROR;
+}
+
+status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
+ // TODO(marissaw): support frame history events
+ mCurrentFrameNumber = mFrameNumber;
+ return NO_ERROR;
+}
+
+void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
+ auto& hwcInfo = *outputLayer->editState().hwc;
+ auto& hwcLayer = hwcInfo.hwcLayer;
+
+ const State& s(getDrawingState());
+
+ // obtain slot
+ uint32_t hwcSlot = 0;
+ sp<GraphicBuffer> buffer;
+ hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);
+
+ auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
+ s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ mCurrentStateModified = false;
+ mFrameNumber++;
+}
+
+void BufferStateLayer::onFirstRef() {
+ BufferLayer::onFirstRef();
+
+ if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+ updateTransformHint(display);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
new file mode 100644
index 0000000..668830a
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 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 "BufferLayer.h"
+#include "Layer.h"
+
+#include <gui/GLConsumer.h>
+#include <renderengine/Image.h>
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class BufferStateLayer : public BufferLayer {
+public:
+ explicit BufferStateLayer(const LayerCreationArgs&);
+ ~BufferStateLayer() override;
+
+ // -----------------------------------------------------------------------
+ // Interface implementation for Layer
+ // -----------------------------------------------------------------------
+ void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ void setTransformHint(uint32_t orientation) const override;
+ void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
+
+ bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+
+ bool getTransformToDisplayInverse() const override;
+
+ uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
+ return flags;
+ }
+ void pushPendingState() override;
+ bool applyPendingStates(Layer::State* stateToCommit) override;
+
+ uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
+ uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
+ ui::Transform getActiveTransform(const Layer::State& s) const override {
+ return s.active.transform;
+ }
+ Region getActiveTransparentRegion(const Layer::State& s) const override {
+ return s.transparentRegionHint;
+ }
+ Rect getCrop(const Layer::State& s) const;
+
+ bool setTransform(uint32_t transform) override;
+ bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
+ bool setCrop(const Rect& crop) override;
+ bool setFrame(const Rect& frame) override;
+ bool setBuffer(const sp<GraphicBuffer>& buffer) override;
+ bool setAcquireFence(const sp<Fence>& fence) override;
+ bool setDataspace(ui::Dataspace dataspace) override;
+ bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
+ bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
+ bool setApi(int32_t api) override;
+ bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
+ bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+
+ // Override to ignore legacy layer state properties that are not used by BufferStateLayer
+ bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
+ bool setPosition(float /*x*/, float /*y*/, bool /*immediate*/) override { return false; }
+ bool setTransparentRegionHint(const Region& transparent) override;
+ bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
+ bool /*allowNonRectPreservingTransforms*/) override {
+ return false;
+ }
+ bool setCrop_legacy(const Rect& /*crop*/, bool /*immediate*/) override { return false; }
+ bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
+ void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
+ uint64_t /*frameNumber*/) override {}
+ void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
+ uint64_t /*frameNumber*/) override {}
+
+ Rect getBufferSize(const State& s) const override;
+ FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+
+ void setPostTime(nsecs_t postTime) override;
+ void setDesiredPresentTime(nsecs_t desiredPresentTime) override;
+ // -----------------------------------------------------------------------
+
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayer
+ // -----------------------------------------------------------------------
+ bool fenceHasSignaled() const override;
+
+private:
+ nsecs_t getDesiredPresentTime() override;
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
+
+ void getDrawingTransformMatrix(float *matrix) override;
+ uint32_t getDrawingTransform() const override;
+ ui::Dataspace getDrawingDataSpace() const override;
+ Rect getDrawingCrop() const override;
+ uint32_t getDrawingScalingMode() const override;
+ Region getDrawingSurfaceDamage() const override;
+ const HdrMetadata& getDrawingHdrMetadata() const override;
+ int getDrawingApi() const override;
+ PixelFormat getPixelFormat() const override;
+
+ uint64_t getFrameNumber() const override;
+
+ bool getAutoRefresh() const override;
+ bool getSidebandStreamChanged() const override;
+
+ bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+
+ bool hasFrameUpdate() const override;
+
+ void setFilteringEnabled(bool enabled) override;
+
+ status_t bindTextureImage() override;
+ status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+
+ status_t updateActiveBuffer() override;
+ status_t updateFrameNumber(nsecs_t latchTime) override;
+
+ void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override;
+
+private:
+ void onFirstRef() override;
+ bool willPresentCurrentTransaction() const;
+
+ static const std::array<float, 16> IDENTITY_MATRIX;
+
+ std::unique_ptr<renderengine::Image> mTextureImage;
+
+ std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX};
+
+ std::atomic<bool> mSidebandStreamChanged{false};
+
+ uint32_t mFrameNumber{0};
+
+ sp<Fence> mPreviousReleaseFence;
+
+ bool mCurrentStateModified = false;
+ bool mReleasePreviousBuffer = false;
+ nsecs_t mCallbackHandleAcquireTime = -1;
+
+ nsecs_t mDesiredPresentTime = -1;
+
+ // TODO(marissaw): support sticky transform for LEGACY camera mode
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
new file mode 100644
index 0000000..cb02d16
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayerCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cinttypes>
+
+#include "BufferStateLayerCache.h"
+
+#define VALID_CACHE_ID(id) ((id) >= 0 || (id) < (BUFFER_CACHE_MAX_SIZE))
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BufferStateLayerCache);
+
+BufferStateLayerCache::BufferStateLayerCache() : mDeathRecipient(new CacheDeathRecipient) {}
+
+void BufferStateLayerCache::add(sp<IBinder> processToken, int32_t id,
+ const sp<GraphicBuffer>& buffer) {
+ if (!VALID_CACHE_ID(id)) {
+ ALOGE("failed to cache buffer: invalid buffer id");
+ return;
+ }
+
+ if (!processToken) {
+ ALOGE("failed to cache buffer: invalid process token");
+ return;
+ }
+
+ if (!buffer) {
+ ALOGE("failed to cache buffer: invalid buffer");
+ return;
+ }
+
+ std::lock_guard lock(mMutex);
+
+ // If this is a new process token, set a death recipient. If the client process dies, we will
+ // get a callback through binderDied.
+ if (mBuffers.find(processToken) == mBuffers.end()) {
+ status_t err = processToken->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("failed to cache buffer: could not link to death");
+ return;
+ }
+ }
+
+ auto& processBuffers = mBuffers[processToken];
+ processBuffers[id] = buffer;
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(sp<IBinder> processToken, int32_t id) {
+ if (!VALID_CACHE_ID(id)) {
+ ALOGE("failed to get buffer: invalid buffer id");
+ return nullptr;
+ }
+
+ if (!processToken) {
+ ALOGE("failed to cache buffer: invalid process token");
+ return nullptr;
+ }
+
+ std::lock_guard lock(mMutex);
+ auto itr = mBuffers.find(processToken);
+ if (itr == mBuffers.end()) {
+ ALOGE("failed to get buffer: process token not found");
+ return nullptr;
+ }
+
+ if (id >= itr->second.size()) {
+ ALOGE("failed to get buffer: id outside the bounds of the cache");
+ return nullptr;
+ }
+
+ return itr->second[id];
+}
+
+void BufferStateLayerCache::removeProcess(const wp<IBinder>& processToken) {
+ std::lock_guard lock(mMutex);
+ mBuffers.erase(processToken);
+}
+
+void BufferStateLayerCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
+ BufferStateLayerCache::getInstance().removeProcess(who);
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
new file mode 100644
index 0000000..ede3feb
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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 <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <array>
+#include <map>
+#include <mutex>
+
+#define BUFFER_CACHE_MAX_SIZE 64
+
+namespace android {
+
+class BufferStateLayerCache : public Singleton<BufferStateLayerCache> {
+public:
+ BufferStateLayerCache();
+
+ void add(sp<IBinder> processToken, int32_t id, const sp<GraphicBuffer>& buffer);
+ sp<GraphicBuffer> get(sp<IBinder> processToken, int32_t id);
+
+ void removeProcess(const wp<IBinder>& processToken);
+
+private:
+ std::mutex mMutex;
+ std::map<wp<IBinder> /*caching process*/, std::array<sp<GraphicBuffer>, BUFFER_CACHE_MAX_SIZE>>
+ mBuffers GUARDED_BY(mMutex);
+
+ class CacheDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ void binderDied(const wp<IBinder>& who) override;
+ };
+
+ sp<CacheDeathRecipient> mDeathRecipient;
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 0b59147..e54b460 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -17,7 +17,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <binder/PermissionCache.h>
#include <binder/IPCThreadState.h>
#include <private/android_filesystem_config.h>
@@ -35,55 +34,10 @@
// ---------------------------------------------------------------------------
Client::Client(const sp<SurfaceFlinger>& flinger)
- : Client(flinger, nullptr)
+ : mFlinger(flinger)
{
}
-Client::Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer)
- : mFlinger(flinger),
- mParentLayer(parentLayer)
-{
-}
-
-Client::~Client()
-{
- // We need to post a message to remove our remaining layers rather than
- // do so directly by acquiring the SurfaceFlinger lock. If we were to
- // attempt to directly call the lock it becomes effectively impossible
- // to use sp<Client> while holding the SF lock as descoping it could
- // then trigger a dead-lock.
-
- const size_t count = mLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- sp<Layer> l = mLayers.valueAt(i).promote();
- if (l == nullptr) {
- continue;
- }
- mFlinger->postMessageAsync(new LambdaMessage([flinger = mFlinger, l]() {
- flinger->removeLayer(l);
- }));
- }
-}
-
-void Client::updateParent(const sp<Layer>& parentLayer) {
- Mutex::Autolock _l(mLock);
-
- // If we didn't ever have a parent, then we must instead be
- // relying on permissions and we never need a parent.
- if (mParentLayer != nullptr) {
- mParentLayer = parentLayer;
- }
-}
-
-sp<Layer> Client::getParentLayer(bool* outParentDied) const {
- Mutex::Autolock _l(mLock);
- sp<Layer> parent = mParentLayer.promote();
- if (outParentDied != nullptr) {
- *outParentDied = (mParentLayer != nullptr && parent == nullptr);
- }
- return parent;
-}
-
status_t Client::initCheck() const {
return NO_ERROR;
}
@@ -118,40 +72,10 @@
return lbc;
}
-
-status_t Client::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // these must be checked
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- const int self_pid = getpid();
- // If we are called from another non root process without the GRAPHICS, SYSTEM, or ROOT
- // uid we require the sAccessSurfaceFlinger permission.
- // We grant an exception in the case that the Client has a "parent layer", as its
- // effects will be scoped to that layer.
- if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != 0)
- && (getParentLayer() == nullptr)) {
- // we're called from a different process, do the real check
- if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
- {
- ALOGE("Permission Denial: "
- "can't openGlobalTransaction pid=%d, uid<=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- }
- return BnSurfaceComposerClient::onTransact(code, data, reply, flags);
-}
-
-
-status_t Client::createSurface(
- const String8& name,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- const sp<IBinder>& parentHandle, int32_t windowType, int32_t ownerUid,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp)
-{
+status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parentHandle,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) {
sp<Layer> parent = nullptr;
if (parentHandle != nullptr) {
auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get());
@@ -160,22 +84,29 @@
return NAME_NOT_FOUND;
}
}
- if (parent == nullptr) {
- bool parentDied;
- parent = getParentLayer(&parentDied);
- // If we had a parent, but it died, we've lost all
- // our capabilities.
- if (parentDied) {
- return NAME_NOT_FOUND;
- }
- }
- return mFlinger->createLayer(name, this, w, h, format, flags, windowType,
- ownerUid, handle, gbp, &parent);
+ // We rely on createLayer to check permissions.
+ return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
+ &parent);
}
-status_t Client::destroySurface(const sp<IBinder>& handle) {
- return mFlinger->onLayerRemoved(this, handle);
+status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags,
+ const sp<IGraphicBufferProducer>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) {
+ if (mFlinger->authenticateSurfaceTexture(parent) == false) {
+ return BAD_VALUE;
+ }
+
+ const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer();
+ if (layer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ sp<IBinder> parentHandle = layer->getHandle();
+
+ return createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), handle, gbp);
}
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 49437ed..74e4818 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -39,46 +39,38 @@
{
public:
explicit Client(const sp<SurfaceFlinger>& flinger);
- Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer);
- ~Client();
+ ~Client() = default;
status_t initCheck() const;
// protected by SurfaceFlinger::mStateLock
void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
-
void detachLayer(const Layer* layer);
sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
- void updateParent(const sp<Layer>& parentLayer);
-
private:
// ISurfaceComposerClient interface
- virtual status_t createSurface(
- const String8& name,
- uint32_t w, uint32_t h,PixelFormat format, uint32_t flags,
- const sp<IBinder>& parent, int32_t windowType, int32_t ownerUid,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp);
+ virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp);
- virtual status_t destroySurface(const sp<IBinder>& handle);
+ virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags,
+ const sp<IGraphicBufferProducer>& parent,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp);
virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
-
- sp<Layer> getParentLayer(bool* outParentDied = nullptr) const;
-
// constant
sp<SurfaceFlinger> mFlinger;
// protected by mLock
DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers;
- wp<Layer> mParentLayer;
// thread-safe
mutable Mutex mLock;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index ff957c0..fcc2d97 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -22,70 +22,110 @@
#include <stdlib.h>
#include <sys/types.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <ui/GraphicBuffer.h>
-
#include "ColorLayer.h"
#include "DisplayDevice.h"
-#include "RenderEngine/RenderEngine.h"
#include "SurfaceFlinger.h"
namespace android {
// ---------------------------------------------------------------------------
-ColorLayer::ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
- uint32_t w, uint32_t h, uint32_t flags)
- : Layer(flinger, client, name, w, h, flags) {
- // drawing state & current state are identical
- mDrawingState = mCurrentState;
-}
+ColorLayer::ColorLayer(const LayerCreationArgs& args)
+ : Layer(args),
+ mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
+ compositionengine::LayerCreationArgs{this})} {}
-void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
- bool useIdentityTransform) const {
- half4 color = getColor();
- if (color.a > 0) {
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2);
- computeGeometry(renderArea, mesh, useIdentityTransform);
- auto& engine(mFlinger->getRenderEngine());
- engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
- true /* disableTexture */, color);
- engine.drawMesh(mesh);
- engine.disableBlending();
- }
+ColorLayer::~ColorLayer() = default;
+
+bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer) {
+ Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion,
+ supportProtectedContent, layer);
+ half4 color(getColor());
+ half3 solidColor(color.r, color.g, color.b);
+ layer.source.solidColor = solidColor;
+ return true;
}
bool ColorLayer::isVisible() const {
- const Layer::State& s(getDrawingState());
- return !isHiddenByPolicy() && s.color.a;
+ return !isHiddenByPolicy() && getAlpha() > 0.0f;
}
-void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
- const Transform& tr = displayDevice->getTransform();
- const auto& viewport = displayDevice->getViewport();
- Region visible = tr.transform(visibleRegion.intersect(viewport));
- auto hwcId = displayDevice->getHwcDisplayId();
- if (!hasHwcLayer(hwcId)) {
- return;
+bool ColorLayer::setColor(const half3& color) {
+ if (mCurrentState.color.r == color.r && mCurrentState.color.g == color.g &&
+ mCurrentState.color.b == color.b) {
+ return false;
}
- auto& hwcInfo = getBE().mHwcLayers[hwcId];
- auto& hwcLayer = hwcInfo.layer;
+
+ mCurrentState.sequence++;
+ mCurrentState.color.r = color.r;
+ mCurrentState.color.g = color.g;
+ mCurrentState.color.b = color.b;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool ColorLayer::setDataspace(ui::Dataspace dataspace) {
+ if (mCurrentState.dataspace == dataspace) {
+ return false;
+ }
+
+ mCurrentState.sequence++;
+ mCurrentState.dataspace = dataspace;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display,
+ const ui::Transform& transform, const Rect& viewport,
+ int32_t /* supportedPerFrameMetadata */,
+ const ui::Dataspace targetDataspace) {
+ RETURN_IF_NO_HWC_LAYER(display);
+
+ Region visible = transform.transform(visibleRegion.intersect(viewport));
+
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
+
+ auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
+
auto error = hwcLayer->setVisibleRegion(visible);
if (error != HWC2::Error::None) {
ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
to_string(error).c_str(), static_cast<int32_t>(error));
visible.dump(LOG_TAG);
}
+ outputLayer->editState().visibleRegion = visible;
- setCompositionType(hwcId, HWC2::Composition::SolidColor);
+ setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR);
- error = hwcLayer->setDataspace(mCurrentDataSpace);
+ const ui::Dataspace dataspace =
+ isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace
+ : mCurrentDataSpace;
+ error = hwcLayer->setDataspace(dataspace);
if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
+ auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
+ layerCompositionState.dataspace = mCurrentDataSpace;
+
half4 color = getColor();
error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
static_cast<uint8_t>(std::round(255.0f * color.g)),
@@ -94,6 +134,9 @@
ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
+ layerCompositionState.color = {static_cast<uint8_t>(std::round(255.0f * color.r)),
+ static_cast<uint8_t>(std::round(255.0f * color.g)),
+ static_cast<uint8_t>(std::round(255.0f * color.b)), 255};
// Clear out the transform, because it doesn't make sense absent a source buffer
error = hwcLayer->setTransform(HWC2::Transform::None);
@@ -101,6 +144,31 @@
ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
+ outputLayer->editState().bufferTransform = static_cast<Hwc2::Transform>(0);
+
+ error = hwcLayer->setColorTransform(getColorTransform());
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+ layerCompositionState.colorTransform = getColorTransform();
+
+ error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ surfaceDamageRegion.dump(LOG_TAG);
+ }
+ layerCompositionState.surfaceDamage = surfaceDamageRegion;
+}
+
+void ColorLayer::commitTransaction(const State& stateToCommit) {
+ Layer::commitTransaction(stateToCommit);
+ mCurrentDataSpace = mDrawingState.dataspace;
+}
+
+std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const {
+ return mCompositionLayer;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index 0cde398..53d5b5b 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -25,16 +25,34 @@
class ColorLayer : public Layer {
public:
- ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
- uint32_t h, uint32_t flags);
- virtual ~ColorLayer() = default;
+ explicit ColorLayer(const LayerCreationArgs&);
+ ~ColorLayer() override;
+
+ std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
virtual const char* getTypeId() const { return "ColorLayer"; }
- virtual void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) const;
bool isVisible() const override;
- void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
+ bool setColor(const half3& color) override;
+
+ bool setDataspace(ui::Dataspace dataspace) override;
+
+ void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
+ const Rect& viewport, int32_t supportedPerFrameMetadata,
+ const ui::Dataspace targetDataspace) override;
+
+ void commitTransaction(const State& stateToCommit) override;
+
+ bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+
+protected:
+ virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer);
+
+private:
+ std::shared_ptr<compositionengine::Layer> mCompositionLayer;
};
} // namespace android
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index d56b1c8..b7d61ce 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_SURFACE_FLINGER_COLORIZER_H
#define ANDROID_SURFACE_FLINGER_COLORIZER_H
-#include <utils/String8.h>
+#include <android-base/stringprintf.h>
namespace android {
@@ -40,19 +40,19 @@
: mEnabled(enabled) {
}
- void colorize(String8& out, color c) {
+ void colorize(std::string& out, color c) {
if (mEnabled) {
- out.appendFormat("\e[%dm", c);
+ base::StringAppendF(&out, "\e[%dm", c);
}
}
- void bold(String8& out) {
+ void bold(std::string& out) {
if (mEnabled) {
out.append("\e[1m");
}
}
- void reset(String8& out) {
+ void reset(std::string& out) {
if (mEnabled) {
out.append("\e[0m");
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
new file mode 100644
index 0000000..e86d35d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -0,0 +1,122 @@
+cc_defaults {
+ name: "libcompositionengine_defaults",
+ defaults: ["surfaceflinger_defaults"],
+ cflags: [
+ "-DLOG_TAG=\"CompositionEngine\"",
+ ],
+ shared_libs: [
+ "android.frameworks.vr.composer@1.0",
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.3",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.3",
+ "libbase",
+ "libcutils",
+ "libgui",
+ "liblayers_proto",
+ "liblog",
+ "libnativewindow",
+ "libsync",
+ "libtimestats_proto",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "libmath",
+ "librenderengine",
+ "libtrace_proto",
+ ],
+ header_libs: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ "android.hardware.graphics.composer@2.2-command-buffer",
+ "android.hardware.graphics.composer@2.3-command-buffer",
+ "libsurfaceflinger_headers",
+ ],
+}
+
+cc_library {
+ name: "libcompositionengine",
+ defaults: ["libcompositionengine_defaults"],
+ srcs: [
+ "src/CompositionEngine.cpp",
+ "src/Display.cpp",
+ "src/DisplayColorProfile.cpp",
+ "src/DisplaySurface.cpp",
+ "src/DumpHelpers.cpp",
+ "src/HwcBufferCache.cpp",
+ "src/Layer.cpp",
+ "src/LayerCompositionState.cpp",
+ "src/Output.cpp",
+ "src/OutputCompositionState.cpp",
+ "src/OutputLayer.cpp",
+ "src/OutputLayerCompositionState.cpp",
+ "src/RenderSurface.cpp",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libcompositionengine_mocks",
+ defaults: ["libcompositionengine_defaults"],
+ srcs: [
+ "mock/CompositionEngine.cpp",
+ "mock/Display.cpp",
+ "mock/DisplayColorProfile.cpp",
+ "mock/DisplaySurface.cpp",
+ "mock/Layer.cpp",
+ "mock/LayerFE.cpp",
+ "mock/Output.cpp",
+ "mock/OutputLayer.cpp",
+ "mock/RenderSurface.cpp",
+ ],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ "libcompositionengine",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "libcompositionengine_test",
+ test_suites: ["device-tests"],
+ defaults: ["libcompositionengine_defaults"],
+ srcs: [
+ "tests/CompositionEngineTest.cpp",
+ "tests/DisplayColorProfileTest.cpp",
+ "tests/DisplayTest.cpp",
+ "tests/HwcBufferCacheTest.cpp",
+ "tests/LayerTest.cpp",
+ "tests/MockHWC2.cpp",
+ "tests/MockHWComposer.cpp",
+ "tests/OutputTest.cpp",
+ "tests/OutputLayerTest.cpp",
+ "tests/RenderSurfaceTest.cpp",
+ ],
+ static_libs: [
+ "libcompositionengine",
+ "libcompositionengine_mocks",
+ "librenderengine_mocks",
+ "libgmock",
+ "libgtest",
+ ],
+ sanitize: {
+ // By using the address sanitizer, we not only uncover any issues
+ // with the test, but also any issues with the code under test.
+ //
+ // Note: If you get an runtime link error like:
+ //
+ // CANNOT LINK EXECUTABLE "/data/local/tmp/libcompositionengine_test": library "libclang_rt.asan-aarch64-android.so" not found
+ //
+ // it is because the address sanitizer shared objects are not installed
+ // by default in the system image.
+ //
+ // You can either "make dist tests" before flashing, or set this
+ // option to false temporarily.
+ address: true,
+ },
+}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
new file mode 100644
index 0000000..896f8aa
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 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 <memory>
+
+namespace android {
+
+class HWComposer;
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine {
+
+class Display;
+class Layer;
+
+struct DisplayCreationArgs;
+struct LayerCreationArgs;
+
+/**
+ * Encapsulates all the interfaces and implementation details for performing
+ * display output composition.
+ */
+class CompositionEngine {
+public:
+ virtual ~CompositionEngine();
+
+ // Create a composition Display
+ virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
+ virtual std::shared_ptr<Layer> createLayer(LayerCreationArgs&&) = 0;
+
+ virtual HWComposer& getHwComposer() const = 0;
+ virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
+
+ virtual renderengine::RenderEngine& getRenderEngine() const = 0;
+ virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
new file mode 100644
index 0000000..dbcd3bd
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+#include <compositionengine/Output.h>
+
+namespace android::compositionengine {
+
+struct RenderSurfaceCreationArgs;
+struct DisplayColorProfileCreationArgs;
+
+/**
+ * A display is a composition target which may be backed by a hardware composer
+ * display device
+ */
+class Display : public virtual Output {
+public:
+ // Gets the HWC DisplayId for the display if there is one
+ virtual const std::optional<DisplayId>& getId() const = 0;
+
+ // True if the display is secure
+ virtual bool isSecure() const = 0;
+
+ // True if the display is virtual
+ virtual bool isVirtual() const = 0;
+
+ // Releases the use of the HWC display, if any
+ virtual void disconnect() = 0;
+
+ // Creates a render color mode for the display
+ virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+
+ // Creates a render surface for the display
+ virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+
+protected:
+ ~Display() = default;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
new file mode 100644
index 0000000..e2a0d42
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+class HdrCapabilities;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates all the state and functionality for how colors should be
+ * transformed for a display
+ */
+class DisplayColorProfile {
+public:
+ constexpr static float sDefaultMinLumiance = 0.0;
+ constexpr static float sDefaultMaxLumiance = 500.0;
+
+ virtual ~DisplayColorProfile();
+
+ // Returns true if the profile is valid. This is meant to be checked post-
+ // construction and prior to use, as not everything is set up by the
+ // constructor.
+ virtual bool isValid() const = 0;
+
+ // Returns true if the profile supports the indicated render intent
+ virtual bool hasRenderIntent(ui::RenderIntent) const = 0;
+
+ // Returns true if the profile supports the indicated dataspace
+ virtual bool hasLegacyHdrSupport(ui::Dataspace) const = 0;
+
+ // Obtains the best combination of color mode and render intent for the
+ // input values
+ virtual void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+ ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+ ui::RenderIntent* outIntent) const = 0;
+
+ // Returns true if the profile supports a wide color gamut
+ virtual bool hasWideColorGamut() const = 0;
+
+ // Returns the per-frame metadata value for this profile
+ virtual int32_t getSupportedPerFrameMetadata() const = 0;
+
+ // Returns true if HWC for this profile supports HDR10Plus
+ virtual bool hasHDR10PlusSupport() const = 0;
+
+ // Returns true if HWC for this profile supports HDR10
+ virtual bool hasHDR10Support() const = 0;
+
+ // Returns true if HWC for this profile supports HLG
+ virtual bool hasHLGSupport() const = 0;
+
+ // Returns true if HWC for this profile supports DolbyVision
+ virtual bool hasDolbyVisionSupport() const = 0;
+
+ // Gets the supported HDR capabilities for the profile
+ virtual const HdrCapabilities& getHdrCapabilities() const = 0;
+
+ // Debugging
+ virtual void dump(std::string&) const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
new file mode 100644
index 0000000..ef0f925
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 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 <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+/**
+ * A parameter object for creating DisplayColorProfile instances
+ */
+struct DisplayColorProfileCreationArgs {
+ using HwcColorModes = std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>;
+
+ // True if this display supports a wide color gamut
+ bool hasWideColorGamut;
+
+ // The HDR capabilities supported by the HWC
+ HdrCapabilities hdrCapabilities;
+
+ // The per-frame metadata supported by the HWC
+ int32_t supportedPerFrameMetadata;
+
+ // The mapping of color modes and render intents supported by the HWC
+ HwcColorModes hwcColorModes;
+};
+
+/**
+ * A helper for setting up a DisplayColorProfileCreationArgs value in-line.
+ *
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ * DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+ * HwcColorModes()}
+ *
+ * Prefer:
+ *
+ * DisplayColorProfileCreationArgsBuilder().setHasWideColorGamut(false)
+ * .setIsVirtual(false).setDisplayId(displayId).Build();
+ */
+class DisplayColorProfileCreationArgsBuilder {
+public:
+ DisplayColorProfileCreationArgs Build() { return std::move(mArgs); }
+
+ DisplayColorProfileCreationArgsBuilder& setHasWideColorGamut(bool hasWideColorGamut) {
+ mArgs.hasWideColorGamut = hasWideColorGamut;
+ return *this;
+ }
+ DisplayColorProfileCreationArgsBuilder& setHdrCapabilities(HdrCapabilities&& hdrCapabilities) {
+ mArgs.hdrCapabilities = std::move(hdrCapabilities);
+ return *this;
+ }
+ DisplayColorProfileCreationArgsBuilder& setSupportedPerFrameMetadata(
+ int32_t supportedPerFrameMetadata) {
+ mArgs.supportedPerFrameMetadata = supportedPerFrameMetadata;
+ return *this;
+ }
+ DisplayColorProfileCreationArgsBuilder& setHwcColorModes(
+ DisplayColorProfileCreationArgs::HwcColorModes&& hwcColorModes) {
+ mArgs.hwcColorModes = std::move(hwcColorModes);
+ return *this;
+ }
+
+private:
+ DisplayColorProfileCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
new file mode 100644
index 0000000..0b6b4e4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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 <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+/**
+ * A parameter object for creating Display instances
+ */
+struct DisplayCreationArgs {
+ // True if this display is secure
+ bool isSecure = false;
+
+ // True if this display is a virtual display
+ bool isVirtual = false;
+
+ // Identifies the display to the HWC, if composition is supported by it
+ std::optional<DisplayId> displayId;
+};
+
+/**
+ * A helper for setting up a DisplayCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ * DisplayCreationArgs{false, false, displayId}
+ *
+ * Prefer:
+ *
+ * DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
+ * .setDisplayId(displayId).build();
+ */
+class DisplayCreationArgsBuilder {
+public:
+ DisplayCreationArgs build() { return std::move(mArgs); }
+
+ DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
+ mArgs.isSecure = isSecure;
+ return *this;
+ }
+ DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
+ mArgs.isVirtual = isVirtual;
+ return *this;
+ }
+ DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
+ mArgs.displayId = displayId;
+ return *this;
+ }
+
+private:
+ DisplayCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
similarity index 80%
rename from services/surfaceflinger/DisplayHardware/DisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index f744f5c..0e67acf 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -14,23 +14,27 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_DISPLAY_SURFACE_H
-#define ANDROID_SF_DISPLAY_SURFACE_H
+#pragma once
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
class Fence;
class IGraphicBufferProducer;
class String8;
+namespace compositionengine {
+
+/**
+ * An abstraction for working with a display surface (buffer queue)
+ */
class DisplaySurface : public virtual RefBase {
public:
+ virtual ~DisplaySurface();
+
// beginFrame is called at the beginning of the composition loop, before
// the configuration is known. The DisplaySurface should do anything it
// needs to do to enable HWComposer to decide how to compose the frame.
@@ -44,9 +48,9 @@
// GLES and HWC for this frame.
enum CompositionType {
COMPOSITION_UNKNOWN = 0,
- COMPOSITION_GLES = 1,
- COMPOSITION_HWC = 2,
- COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
+ COMPOSITION_GLES = 1,
+ COMPOSITION_HWC = 2,
+ COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
};
virtual status_t prepareFrame(CompositionType compositionType) = 0;
@@ -70,15 +74,7 @@
virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
-
-protected:
- DisplaySurface() {}
- virtual ~DisplaySurface() {}
};
-// ---------------------------------------------------------------------------
+} // namespace compositionengine
} // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_DISPLAY_SURFACE_H
-
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
new file mode 100644
index 0000000..8cb9203
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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 <cstdint>
+#include <string>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+typedef int64_t nsecs_t;
+
+namespace compositionengine {
+
+class Display;
+class LayerFE;
+
+namespace impl {
+struct LayerCompositionState;
+} // namespace impl
+
+/**
+ * A layer contains the output-independent composition state for a front-end
+ * Layer
+ */
+class Layer {
+public:
+ virtual ~Layer();
+
+ // Gets the front-end interface for this layer. Can return nullptr if the
+ // front-end layer no longer exists.
+ virtual sp<LayerFE> getLayerFE() const = 0;
+
+ using CompositionState = impl::LayerCompositionState;
+
+ // Gets the raw composition state data for the layer
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual const CompositionState& getState() const = 0;
+
+ // Allows mutable access to the raw composition state data for the layer.
+ // This is meant to be used by the various functions that are part of the
+ // composition process.
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual CompositionState& editState() = 0;
+
+ // Debugging
+ virtual void dump(std::string& result) const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
new file mode 100644
index 0000000..db3312b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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 <utils/RefBase.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class LayerFE;
+
+/**
+ * A parameter object for creating Layer instances
+ */
+struct LayerCreationArgs {
+ // A weak pointer to the front-end layer instance that the new layer will
+ // represent.
+ wp<LayerFE> layerFE;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
new file mode 100644
index 0000000..9f635b9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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 <utils/RefBase.h>
+
+namespace android {
+
+class Fence;
+
+namespace compositionengine {
+
+struct LayerFECompositionState;
+
+// Defines the interface used by the CompositionEngine to make requests
+// of the front-end layer
+class LayerFE : public virtual RefBase {
+public:
+ // Latches the output-independent state. If includeGeometry is false, the
+ // geometry state can be skipped.
+ virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
+
+ // Called after the layer is displayed to update the presentation fence
+ virtual void onLayerDisplayed(const sp<Fence>&) = 0;
+
+ // Gets some kind of identifier for the layer for debug purposes.
+ virtual const char* getDebugName() const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
new file mode 100644
index 0000000..e6ee078
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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 <cstdint>
+
+#include <gui/BufferQueue.h>
+#include <gui/HdrMetadata.h>
+#include <math/mat4.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+#include "DisplayHardware/ComposerHal.h"
+
+namespace android::compositionengine {
+
+/*
+ * Used by LayerFE::getCompositionState
+ */
+struct LayerFECompositionState {
+ // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
+ // value recomputed / set every frame.
+ Region geomVisibleRegion;
+
+ /*
+ * Geometry state
+ */
+
+ bool isSecure{false};
+ bool geomUsesSourceCrop{false};
+ bool geomBufferUsesDisplayInverseTransform{false};
+ uint32_t geomBufferTransform{0};
+ ui::Transform geomLayerTransform;
+ ui::Transform geomInverseLayerTransform;
+ Rect geomBufferSize;
+ Rect geomContentCrop;
+ Rect geomCrop;
+ Region geomActiveTransparentRegion;
+ FloatRect geomLayerBounds;
+
+ /*
+ * Presentation
+ */
+
+ // The blend mode for this layer
+ Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID};
+
+ // The alpha value for this layer
+ float alpha{1.f};
+
+ /*
+ * Extra metadata
+ */
+
+ // The type for this layer
+ int type{0};
+
+ // The appId for this layer
+ int appId{0};
+
+ /*
+ * Per-frame content
+ */
+
+ // The type of composition for this layer
+ Hwc2::IComposerClient::Composition compositionType{Hwc2::IComposerClient::Composition::INVALID};
+
+ // The buffer and related state
+ sp<GraphicBuffer> buffer;
+ int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+ sp<Fence> acquireFence;
+ Region surfaceDamage;
+
+ // The handle to use for a sideband stream for this layer
+ sp<NativeHandle> sidebandStream;
+
+ // The color for this layer
+ Hwc2::IComposerClient::Color color;
+
+ /*
+ * Per-frame presentation state
+ */
+
+ // The dataspace for this layer
+ ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
+ // The metadata for this layer
+ HdrMetadata hdrMetadata;
+
+ // The color transform
+ mat4 colorTransform;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
new file mode 100644
index 0000000..54e6bd6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 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 <cstdint>
+#include <optional>
+#include <string>
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+#include <utils/StrongPointer.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class DisplayColorProfile;
+class Layer;
+class LayerFE;
+class RenderSurface;
+class OutputLayer;
+
+namespace impl {
+struct OutputCompositionState;
+} // namespace impl
+
+/**
+ * Encapsulates all the state involved with composing layers for an output
+ */
+class Output {
+public:
+ using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
+
+ virtual ~Output();
+
+ // Returns true if the output is valid. This is meant to be checked post-
+ // construction and prior to use, as not everything is set up by the
+ // constructor.
+ virtual bool isValid() const = 0;
+
+ // Enables (or disables) composition on this output
+ virtual void setCompositionEnabled(bool) = 0;
+
+ // Sets the projection state to use
+ virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+ const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+ // Sets the bounds to use
+ virtual void setBounds(const ui::Size&) = 0;
+
+ // Sets the layer stack filtering settings for this output. See
+ // belongsInOutput for full details.
+ virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
+
+ // Sets the color transform matrix to use
+ virtual void setColorTransform(const mat4&) = 0;
+
+ // Sets the output color mode
+ virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+
+ // Outputs a string with a state dump
+ virtual void dump(std::string&) const = 0;
+
+ // Gets the debug name for the output
+ virtual const std::string& getName() const = 0;
+
+ // Sets a debug name for the output
+ virtual void setName(const std::string&) = 0;
+
+ // Gets the current render color mode for the output
+ virtual DisplayColorProfile* getDisplayColorProfile() const = 0;
+
+ // Gets the current render surface for the output
+ virtual RenderSurface* getRenderSurface() const = 0;
+
+ using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+ // Gets the raw composition state data for the output
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual const OutputCompositionState& getState() const = 0;
+
+ // Allows mutable access to the raw composition state data for the output.
+ // This is meant to be used by the various functions that are part of the
+ // composition process.
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual OutputCompositionState& editState() = 0;
+
+ // Gets the dirty region in layer stack space.
+ // If repaintEverything is true, this will be the full display bounds.
+ virtual Region getDirtyRegion(bool repaintEverything) const = 0;
+
+ // Tests whether a given layerStackId belongs in this output.
+ // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
+ // unless the layer should display on the primary output only and this is not the primary output
+
+ // A layer belongs to the output if its layerStackId matches. Additionally
+ // if the layer should only show in the internal (primary) display only and
+ // this output allows that.
+ virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
+
+ // Returns a pointer to the output layer corresponding to the given layer on
+ // this output, or nullptr if the layer does not have one
+ virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
+
+ // Gets the OutputLayer corresponding to the input Layer instance from the
+ // current ordered set of output layers. If there is no such layer, a new
+ // one is created and returned.
+ virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::optional<DisplayId>,
+ std::shared_ptr<Layer>,
+ sp<LayerFE>) = 0;
+
+ // Sets the new ordered set of output layers for this output
+ virtual void setOutputLayersOrderedByZ(OutputLayers&&) = 0;
+
+ // Gets the ordered set of output layers for this output
+ virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0;
+
+protected:
+ virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
+ virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
new file mode 100644
index 0000000..cd63b57
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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 <optional>
+#include <string>
+
+#include <utils/StrongPointer.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android {
+
+namespace compositionengine {
+
+class CompositionEngine;
+class Output;
+class Layer;
+class LayerFE;
+
+namespace impl {
+struct OutputLayerCompositionState;
+} // namespace impl
+
+/**
+ * An output layer contains the output-dependent composition state for a layer
+ */
+class OutputLayer {
+public:
+ virtual ~OutputLayer();
+
+ // Gets the output which owns this output layer
+ virtual const Output& getOutput() const = 0;
+
+ // Gets the display-independent layer which this output layer represents
+ virtual Layer& getLayer() const = 0;
+
+ // Gets the front-end layer interface this output layer represents
+ virtual LayerFE& getLayerFE() const = 0;
+
+ using CompositionState = compositionengine::impl::OutputLayerCompositionState;
+
+ // Gets the raw composition state data for the layer
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual const CompositionState& getState() const = 0;
+
+ // Allows mutable access to the raw composition state data for the layer.
+ // This is meant to be used by the various functions that are part of the
+ // composition process.
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual CompositionState& editState() = 0;
+
+ // 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) = 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
+ // skipped.
+ virtual void writeStateToHWC(bool includeGeometry) const = 0;
+
+ // Debugging
+ virtual void dump(std::string& result) const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
new file mode 100644
index 0000000..e69b99f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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 <cstdint>
+#include <vector>
+
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Size.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates everything for composing to a render surface with RenderEngine
+ */
+class RenderSurface {
+public:
+ virtual ~RenderSurface();
+
+ // Returns true if the render surface is valid. This is meant to be checked
+ // post-construction and prior to use, as not everything is set up by the
+ // constructor.
+ virtual bool isValid() const = 0;
+
+ // Performs one-time initialization of the render surface. This is meant
+ // to be called after the validation check.
+ virtual void initialize() = 0;
+
+ // Returns the bounds of the surface
+ virtual const ui::Size& getSize() const = 0;
+
+ // Gets the latest fence to pass to the HWC to signal that the surface
+ // buffer is done rendering
+ virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+ // Sets the size of the surface
+ virtual void setDisplaySize(const ui::Size&) = 0;
+
+ // Sets the dataspace used for rendering the surface
+ virtual void setBufferDataspace(ui::Dataspace) = 0;
+
+ // Configures the protected rendering on the surface
+ virtual void setProtected(bool useProtected) = 0;
+
+ // Called to signal that rendering has started. 'mustRecompose' should be
+ // true if the entire frame must be recomposed.
+ virtual status_t beginFrame(bool mustRecompose) = 0;
+
+ // Prepares the frame for rendering
+ virtual status_t prepareFrame() = 0;
+
+ // Allocates a buffer as scratch space for GPU composition
+ virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 0;
+
+ // Queues the drawn buffer for consumption by HWC. readyFence is the fence
+ // which will fire when the buffer is ready for consumption.
+ virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
+
+ // Called after the HWC calls are made to present the display
+ virtual void onPresentDisplayCompleted() = 0;
+
+ // Called to set the viewport and projection state for rendering into this
+ // surface
+ virtual void setViewportAndProjection() = 0;
+
+ // Called after the surface has been rendering to signal the surface should
+ // be made ready for displaying
+ virtual void flip() = 0;
+
+ // Debugging - Dumps the state of the RenderSurface to a string
+ virtual void dump(std::string& result) const = 0;
+
+ // Debugging - gets the page flip count for the RenderSurface
+ virtual std::uint32_t getPageFlipCount() const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
new file mode 100644
index 0000000..a1230b3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 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 <cstdint>
+#include <memory>
+
+#include <compositionengine/DisplaySurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class Display;
+
+/**
+ * A parameter object for creating RenderSurface instances
+ */
+struct RenderSurfaceCreationArgs {
+ // The initial width of the surface
+ int32_t displayWidth;
+
+ // The initial height of the surface
+ int32_t displayHeight;
+
+ // The ANativeWindow for the buffer queue for this surface
+ sp<ANativeWindow> nativeWindow;
+
+ // The DisplaySurface for this surface
+ sp<DisplaySurface> displaySurface;
+};
+
+/**
+ * A helper for setting up a RenderSurfaceCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ * RenderSurfaceCreationArgs{1000, 1000, nativeWindow, displaySurface}
+ *
+ * Prefer:
+ *
+ * RenderSurfaceCreationArgsBuilder().setDisplayWidth(1000).setDisplayHeight(1000)
+ * .setNativeWindow(nativeWindow).setDisplaySurface(displaySurface).Build();
+ */
+class RenderSurfaceCreationArgsBuilder {
+public:
+ RenderSurfaceCreationArgs build() { return std::move(mArgs); }
+
+ RenderSurfaceCreationArgsBuilder& setDisplayWidth(int32_t displayWidth) {
+ mArgs.displayWidth = displayWidth;
+ return *this;
+ }
+ RenderSurfaceCreationArgsBuilder& setDisplayHeight(int32_t displayHeight) {
+ mArgs.displayHeight = displayHeight;
+ return *this;
+ }
+ RenderSurfaceCreationArgsBuilder& setNativeWindow(sp<ANativeWindow> nativeWindow) {
+ mArgs.nativeWindow = nativeWindow;
+ return *this;
+ }
+ RenderSurfaceCreationArgsBuilder& setDisplaySurface(sp<DisplaySurface> displaySurface) {
+ mArgs.displaySurface = displaySurface;
+ return *this;
+ }
+
+private:
+ RenderSurfaceCreationArgs mArgs;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
new file mode 100644
index 0000000..b01eb64
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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 <compositionengine/CompositionEngine.h>
+
+namespace android::compositionengine::impl {
+
+class CompositionEngine : public compositionengine::CompositionEngine {
+public:
+ CompositionEngine();
+ ~CompositionEngine() override;
+
+ std::shared_ptr<compositionengine::Display> createDisplay(
+ compositionengine::DisplayCreationArgs&&) override;
+ std::shared_ptr<compositionengine::Layer> createLayer(
+ compositionengine::LayerCreationArgs&&) override;
+
+ HWComposer& getHwComposer() const override;
+ void setHwComposer(std::unique_ptr<HWComposer>) override;
+
+ renderengine::RenderEngine& getRenderEngine() const override;
+ void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+
+private:
+ std::unique_ptr<HWComposer> mHwComposer;
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+
+std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine();
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
new file mode 100644
index 0000000..0e20c43
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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 <memory>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/impl/Output.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+struct DisplayCreationArgs;
+
+namespace impl {
+
+class Display : public compositionengine::impl::Output, public compositionengine::Display {
+public:
+ Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+ virtual ~Display();
+
+ // compositionengine::Output overrides
+ void dump(std::string&) const override;
+ void setColorTransform(const mat4&) override;
+ void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+ // compositionengine::Display overrides
+ const std::optional<DisplayId>& getId() const override;
+ bool isSecure() const override;
+ bool isVirtual() const override;
+ void disconnect() override;
+ void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override;
+ void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override;
+
+private:
+ const bool mIsVirtual;
+ std::optional<DisplayId> mId;
+};
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+ const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
new file mode 100644
index 0000000..49c2d2c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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 <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class Display;
+
+namespace impl {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+ DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+ ~DisplayColorProfile() override;
+
+ bool isValid() const override;
+
+ bool hasRenderIntent(ui::RenderIntent intent) const override;
+ bool hasLegacyHdrSupport(ui::Dataspace dataspace) const override;
+ void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+ ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+ ui::RenderIntent* outIntent) const override;
+
+ bool hasWideColorGamut() const override;
+ int32_t getSupportedPerFrameMetadata() const override;
+
+ // Whether h/w composer has native support for specific HDR type.
+ bool hasHDR10PlusSupport() const override;
+ bool hasHDR10Support() const override;
+ bool hasHLGSupport() const override;
+ bool hasDolbyVisionSupport() const override;
+
+ const HdrCapabilities& getHdrCapabilities() const override;
+
+ void dump(std::string&) const override;
+
+private:
+ void populateColorModes(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes);
+ void addColorMode(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes,
+ const ui::ColorMode mode, const ui::RenderIntent intent);
+
+ // Mappings from desired Dataspace/RenderIntent to the supported
+ // Dataspace/ColorMode/RenderIntent.
+ using ColorModeKey = uint64_t;
+ struct ColorModeValue {
+ ui::Dataspace dataspace;
+ ui::ColorMode colorMode;
+ ui::RenderIntent renderIntent;
+ };
+
+ static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
+ return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
+ }
+
+ // Need to know if display is wide-color capable or not.
+ // Initialized by SurfaceFlinger when the DisplayDevice is created.
+ // Fed to RenderEngine during composition.
+ bool mHasWideColorGamut{false};
+ int32_t mSupportedPerFrameMetadata{0};
+ bool mHasHdr10Plus{false};
+ bool mHasHdr10{false};
+ bool mHasHLG{false};
+ bool mHasDolbyVision{false};
+ HdrCapabilities mHdrCapabilities;
+ std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
+};
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+ DisplayColorProfileCreationArgs&&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
new file mode 100644
index 0000000..782c8d7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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 <cstdint>
+#include <string>
+#include <type_traits>
+
+#include <math/mat4.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine::impl {
+
+void dumpVal(std::string& out, const char* name, bool);
+void dumpVal(std::string& out, const char* name, const void*);
+void dumpVal(std::string& out, const char* name, int);
+void dumpVal(std::string& out, const char* name, float);
+void dumpVal(std::string& out, const char* name, uint32_t);
+void dumpHex(std::string& out, const char* name, uint64_t);
+void dumpVal(std::string& out, const char* name, const char* value);
+void dumpVal(std::string& out, const char* name, const std::string& value);
+
+// For enums with named values
+void dumpVal(std::string& out, const char* name, const char*, int);
+void dumpVal(std::string& out, const char* name, const std::string&, int);
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const char* valueName, EnumType value) {
+ dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const std::string& valueName, EnumType value) {
+ dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect);
+void dumpVal(std::string& out, const char* name, const Rect& rect);
+void dumpVal(std::string& out, const char* name, const Region& region);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const ui::Size&);
+
+void dumpVal(std::string& out, const char* name, const mat4& tr);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
new file mode 100644
index 0000000..02d7890
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include <gui/BufferQueue.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+
+namespace compositionengine::impl {
+
+// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
+// HWC display and layer. When updating a display target or a layer buffer,
+// we have the option to send the buffer handle over or to request the HAL to
+// retrieve it from its cache. The latter is cheaper since it eliminates the
+// overhead to transfer the handle over the trasport layer, and the overhead
+// for the HAL to clone and retain the handle.
+//
+// To be able to find out whether a buffer is already in the HAL's cache, we
+// use HWComposerBufferCache to mirror the cache in SF.
+class HwcBufferCache {
+public:
+ HwcBufferCache();
+ // Given a buffer, return the HWC cache slot and
+ // buffer to be sent to HWC.
+ //
+ // outBuffer is set to buffer when buffer is not in the HWC cache;
+ // otherwise, outBuffer is set to nullptr.
+ void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer);
+
+protected:
+ bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot);
+ uint32_t getLeastRecentlyUsedSlot();
+ uint64_t getCounter();
+
+private:
+ // an array where the index corresponds to a slot and the value corresponds to a (counter,
+ // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
+ // or used and allows us to keep track of the least-recently used buffer.
+ std::pair<uint64_t, wp<GraphicBuffer>> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+
+ // The cache increments this counter value when a slot is updated or used.
+ // Used to track the least recently-used buffer
+ uint64_t mCounter = 1;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
new file mode 100644
index 0000000..3e56b21
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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 <memory>
+
+#include <compositionengine/Layer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class LayerFE;
+
+struct LayerCreationArgs;
+
+namespace impl {
+
+class Display;
+
+class Layer : public compositionengine::Layer {
+public:
+ Layer(const CompositionEngine&, compositionengine::LayerCreationArgs&&);
+ ~Layer() override;
+
+ sp<LayerFE> getLayerFE() const override;
+
+ const LayerCompositionState& getState() const override;
+ LayerCompositionState& editState() override;
+
+ void dump(std::string& result) const override;
+
+private:
+ const compositionengine::CompositionEngine& mCompositionEngine;
+ const wp<LayerFE> mLayerFE;
+
+ LayerCompositionState mState;
+};
+
+std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&,
+ compositionengine::LayerCreationArgs&&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
new file mode 100644
index 0000000..67bea4b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 <cstdint>
+#include <string>
+
+#include <compositionengine/LayerFECompositionState.h>
+#include <renderengine/Mesh.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+struct LayerCompositionState {
+ /*
+ * State intended to be set by LayerFE::getCompositionState
+ */
+
+ LayerFECompositionState frontEnd;
+
+ /*
+ * RE state
+ */
+
+ renderengine::Mesh reMesh{renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2};
+
+ // Debugging
+ void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
new file mode 100644
index 0000000..b1d1f42
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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 <memory>
+#include <utility>
+#include <vector>
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class Layer;
+class OutputLayer;
+
+namespace impl {
+
+class Output : public virtual compositionengine::Output {
+public:
+ Output(const CompositionEngine&);
+ ~Output() override;
+
+ bool isValid() const override;
+
+ void setCompositionEnabled(bool) override;
+ void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+ const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+ void setBounds(const ui::Size&) override;
+ void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+
+ void setColorTransform(const mat4&) override;
+ void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+ void dump(std::string&) const override;
+
+ const std::string& getName() const override;
+ void setName(const std::string&) override;
+
+ compositionengine::DisplayColorProfile* getDisplayColorProfile() const override;
+ void setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile>) override;
+
+ compositionengine::RenderSurface* getRenderSurface() const override;
+ void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
+
+ const OutputCompositionState& getState() const override;
+ OutputCompositionState& editState() override;
+
+ Region getDirtyRegion(bool repaintEverything) const override;
+ bool belongsInOutput(uint32_t, bool) const override;
+
+ compositionengine::OutputLayer* getOutputLayerForLayer(
+ compositionengine::Layer*) const override;
+ std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer(
+ std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
+ sp<LayerFE>) override;
+ void setOutputLayersOrderedByZ(OutputLayers&&) override;
+ const OutputLayers& getOutputLayersOrderedByZ() const override;
+
+ // Testing
+ void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
+ void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+
+protected:
+ const CompositionEngine& getCompositionEngine() const;
+ void dumpBase(std::string&) const;
+
+private:
+ void dirtyEntireOutput();
+
+ const CompositionEngine& mCompositionEngine;
+
+ std::string mName;
+
+ OutputCompositionState mState;
+
+ std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
+ std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
+
+ OutputLayers mOutputLayersOrderedByZ;
+};
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
new file mode 100644
index 0000000..024ed45
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+struct OutputCompositionState {
+ // If false, composition will not per performed for this display
+ bool isEnabled{false};
+
+ // If false, this output is not considered secure
+ bool isSecure{false};
+
+ // If true, this output displays layers that are internal-only
+ bool layerStackInternal{false};
+
+ // The layer stack to display on this display
+ uint32_t layerStackId{~0u};
+
+ // The physical space screen bounds
+ Rect bounds;
+
+ // The logical to physical transformation to use
+ ui::Transform transform;
+
+ // The physical orientation of the display, expressed as ui::Transform
+ // orientation flags.
+ uint32_t orientation{0};
+
+ // The logical space user visible bounds
+ Rect frame;
+
+ // The logical space user viewport rectangle
+ Rect viewport;
+
+ // The physical space scissor rectangle
+ Rect scissor;
+
+ // If true, RenderEngine filtering should be enabled
+ bool needsFiltering{false};
+
+ // The logical coordinates for the dirty region for the display.
+ // dirtyRegion is semi-persistent state. Dirty rectangles are added to it
+ // by the FE until composition happens, at which point it is cleared.
+ Region dirtyRegion;
+
+ // The logical coordinates for the undefined region for the display.
+ // The undefined region is internal to the composition engine. It is
+ // updated every time the geometry changes.
+ Region undefinedRegion;
+
+ // True if the last composition frame had visible layers
+ bool lastCompositionHadVisibleLayers{false};
+
+ // The color transform to apply
+ android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
+
+ // Current active color mode
+ ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+ // Current active render intent
+ ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+
+ // Current active dstaspace
+ ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
+ // Debugging
+ void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
new file mode 100644
index 0000000..6a4818f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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 <memory>
+#include <string>
+
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine::impl {
+
+class OutputLayer : public compositionengine::OutputLayer {
+public:
+ OutputLayer(const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>,
+ sp<compositionengine::LayerFE>);
+ ~OutputLayer() override;
+
+ void initialize(const CompositionEngine&, std::optional<DisplayId>);
+
+ const compositionengine::Output& getOutput() const override;
+ compositionengine::Layer& getLayer() const override;
+ compositionengine::LayerFE& getLayerFE() const override;
+
+ const OutputLayerCompositionState& getState() const override;
+ OutputLayerCompositionState& editState() override;
+
+ void updateCompositionState(bool) override;
+ void writeStateToHWC(bool) const override;
+
+ void dump(std::string& result) const override;
+
+ virtual FloatRect calculateOutputSourceCrop() const;
+ virtual Rect calculateOutputDisplayFrame() const;
+ virtual uint32_t calculateOutputRelativeBufferTransform() const;
+
+private:
+ Rect calculateInitialCrop() const;
+
+ const compositionengine::Output& mOutput;
+ std::shared_ptr<compositionengine::Layer> mLayer;
+ sp<compositionengine::LayerFE> mLayerFE;
+
+ OutputLayerCompositionState mState;
+};
+
+std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+ const CompositionEngine&, std::optional<DisplayId>, const compositionengine::Output&,
+ std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
new file mode 100644
index 0000000..b78e9e0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 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 <cstdint>
+#include <optional>
+#include <string>
+
+#include <compositionengine/impl/HwcBufferCache.h>
+#include <renderengine/Mesh.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include "DisplayHardware/ComposerHal.h"
+
+namespace HWC2 {
+class Layer;
+} // namespace HWC2
+
+namespace android {
+
+class HWComposer;
+
+namespace compositionengine::impl {
+
+struct OutputLayerCompositionState {
+ // The region of this layer which is visible on this output
+ Region visibleRegion;
+
+ // If true, client composition will be used on this output
+ bool forceClientComposition{false};
+
+ // If true, when doing client composition, the target may need to be cleared
+ bool clearClientTarget{false};
+
+ // The display frame for this layer on this output
+ Rect displayFrame;
+
+ // The source crop for this layer on this output
+ FloatRect sourceCrop;
+
+ // The buffer transform to use for this layer o on this output.
+ Hwc2::Transform bufferTransform{static_cast<Hwc2::Transform>(0)};
+
+ // The Z order index of this layer on this output
+ uint32_t z;
+
+ /*
+ * HWC state
+ */
+
+ struct Hwc {
+ explicit Hwc(std::shared_ptr<HWC2::Layer> hwcLayer) : hwcLayer(hwcLayer) {}
+
+ // The HWC Layer backing this layer
+ std::shared_ptr<HWC2::Layer> hwcLayer;
+
+ // The HWC composition type for this layer
+ Hwc2::IComposerClient::Composition hwcCompositionType{
+ Hwc2::IComposerClient::Composition::INVALID};
+
+ // The buffer cache for this layer. This is used to lower the
+ // cost of sending reused buffers to the HWC.
+ HwcBufferCache hwcBufferCache;
+ };
+
+ // The HWC state is optional, and is only set up if there is any potential
+ // HWC acceleration possible.
+ std::optional<Hwc> hwc;
+
+ // Debugging
+ void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
new file mode 100644
index 0000000..3c79084
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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 <memory>
+
+#include <android-base/unique_fd.h>
+#include <compositionengine/RenderSurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class CompositionEngine;
+class Display;
+class DisplaySurface;
+
+struct RenderSurfaceCreationArgs;
+
+namespace impl {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+ RenderSurface(const CompositionEngine&, compositionengine::Display&,
+ compositionengine::RenderSurfaceCreationArgs&&);
+ ~RenderSurface() override;
+
+ bool isValid() const override;
+ void initialize() override;
+ const ui::Size& getSize() const override;
+
+ const sp<Fence>& getClientTargetAcquireFence() const override;
+ void setBufferDataspace(ui::Dataspace) override;
+ void setDisplaySize(const ui::Size&) override;
+ void setProtected(bool useProtected) override;
+ status_t beginFrame(bool mustRecompose) override;
+ status_t prepareFrame() override;
+ sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
+ void queueBuffer(base::unique_fd&& readyFence) override;
+ void onPresentDisplayCompleted() override;
+ void setViewportAndProjection() override;
+ void flip() override;
+
+ // Debugging
+ void dump(std::string& result) const override;
+ std::uint32_t getPageFlipCount() const override;
+
+ // Testing
+ void setPageFlipCountForTest(std::uint32_t);
+ void setSizeForTest(const ui::Size&);
+ sp<GraphicBuffer>& mutableGraphicBufferForTest();
+ base::unique_fd& mutableBufferReadyForTest();
+
+private:
+ const compositionengine::CompositionEngine& mCompositionEngine;
+ const compositionengine::Display& mDisplay;
+
+ // ANativeWindow being rendered into
+ const sp<ANativeWindow> mNativeWindow;
+ // Current buffer being rendered into
+ sp<GraphicBuffer> mGraphicBuffer;
+ const sp<DisplaySurface> mDisplaySurface;
+ ui::Size mSize;
+ std::uint32_t mPageFlipCount{0};
+};
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+ const compositionengine::CompositionEngine&, compositionengine::Display&,
+ compositionengine::RenderSurfaceCreationArgs&&);
+
+} // namespace impl
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
new file mode 100644
index 0000000..0f57685
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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 <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/LayerCreationArgs.h>
+#include <gmock/gmock.h>
+#include <renderengine/RenderEngine.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::mock {
+
+class CompositionEngine : public compositionengine::CompositionEngine {
+public:
+ CompositionEngine();
+ ~CompositionEngine() override;
+
+ MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
+ MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(LayerCreationArgs&&));
+
+ MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
+ MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
+
+ MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
+ MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
new file mode 100644
index 0000000..d763aa6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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 <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/mock/Output.h>
+#include <gmock/gmock.h>
+#include <system/window.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine::mock {
+
+class Display : public compositionengine::mock::Output, public compositionengine::Display {
+public:
+ Display();
+ virtual ~Display();
+
+ MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+ MOCK_CONST_METHOD0(isSecure, bool());
+ MOCK_CONST_METHOD0(isVirtual, bool());
+
+ MOCK_METHOD0(disconnect, void());
+
+ MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
+ MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
new file mode 100644
index 0000000..8056c9d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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 <compositionengine/DisplayColorProfile.h>
+#include <gmock/gmock.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine::mock {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+ DisplayColorProfile();
+ ~DisplayColorProfile() override;
+
+ MOCK_CONST_METHOD0(isValid, bool());
+
+ MOCK_CONST_METHOD1(hasRenderIntent, bool(ui::RenderIntent));
+ MOCK_CONST_METHOD1(hasLegacyHdrSupport, bool(ui::Dataspace));
+ MOCK_CONST_METHOD5(getBestColorMode,
+ void(ui::Dataspace, ui::RenderIntent, ui::Dataspace*, ui::ColorMode*,
+ ui::RenderIntent*));
+ MOCK_CONST_METHOD0(hasWideColorGamut, bool());
+ MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+ MOCK_CONST_METHOD0(hasHDR10PlusSupport, bool());
+ MOCK_CONST_METHOD0(hasHDR10Support, bool());
+ MOCK_CONST_METHOD0(hasHLGSupport, bool());
+ MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
+
+ MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
similarity index 81%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
index d6c9aa4..31b5f95 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -16,16 +16,13 @@
#pragma once
+#include <compositionengine/DisplaySurface.h>
#include <gmock/gmock.h>
-
#include <utils/String8.h>
-#include "DisplayHardware/DisplaySurface.h"
+namespace android::compositionengine::mock {
-namespace android {
-namespace mock {
-
-class DisplaySurface : public android::DisplaySurface {
+class DisplaySurface : public compositionengine::DisplaySurface {
public:
DisplaySurface();
~DisplaySurface() override;
@@ -39,5 +36,4 @@
MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
};
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
new file mode 100644
index 0000000..cce3b97
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Layer : public compositionengine::Layer {
+public:
+ Layer();
+ virtual ~Layer();
+
+ MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>());
+
+ MOCK_CONST_METHOD0(getState, const CompositionState&());
+ MOCK_METHOD0(editState, CompositionState&());
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
new file mode 100644
index 0000000..aab18db
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <gmock/gmock.h>
+#include <ui/Fence.h>
+
+namespace android::compositionengine::mock {
+
+// Defines the interface used by the CompositionEngine to make requests
+// of the front-end layer.
+class LayerFE : public compositionengine::LayerFE {
+public:
+ LayerFE();
+ virtual ~LayerFE();
+
+ MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
+ MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
+
+ MOCK_CONST_METHOD0(getDebugName, const char*());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
new file mode 100644
index 0000000..d0e7b19
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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 <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Output : public virtual compositionengine::Output {
+public:
+ Output();
+ virtual ~Output();
+
+ MOCK_CONST_METHOD0(isValid, bool());
+
+ MOCK_METHOD1(setCompositionEnabled, void(bool));
+ MOCK_METHOD6(setProjection,
+ void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+ MOCK_METHOD1(setBounds, void(const ui::Size&));
+ MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
+
+ MOCK_METHOD1(setColorTransform, void(const mat4&));
+ MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD0(getName, const std::string&());
+ MOCK_METHOD1(setName, void(const std::string&));
+
+ MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*());
+ MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>));
+
+ MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
+ MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+
+ MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
+ MOCK_METHOD0(editState, OutputCompositionState&());
+
+ MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
+
+ MOCK_CONST_METHOD1(getOutputLayerForLayer,
+ compositionengine::OutputLayer*(compositionengine::Layer*));
+ MOCK_METHOD3(getOrCreateOutputLayer,
+ std::unique_ptr<compositionengine::OutputLayer>(
+ std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
+ sp<compositionengine::LayerFE>));
+ MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&));
+ MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
new file mode 100644
index 0000000..29cd08a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 <compositionengine/CompositionEngine.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class OutputLayer : public compositionengine::OutputLayer {
+public:
+ OutputLayer();
+ virtual ~OutputLayer();
+
+ MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
+ MOCK_CONST_METHOD0(getLayer, compositionengine::Layer&());
+ MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
+
+ MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
+ MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
+
+ MOCK_METHOD1(updateCompositionState, void(bool));
+ MOCK_CONST_METHOD1(writeStateToHWC, void(bool));
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
new file mode 100644
index 0000000..1562f58
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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 <compositionengine/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <ui/GraphicBuffer.h>
+
+
+namespace android::compositionengine::mock {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+ RenderSurface();
+ ~RenderSurface() override;
+
+ MOCK_CONST_METHOD0(isValid, bool());
+ MOCK_METHOD0(initialize, void());
+ MOCK_CONST_METHOD0(getSize, const ui::Size&());
+ MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
+ MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
+ MOCK_METHOD1(setProtected, void(bool));
+ MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+ MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
+ MOCK_METHOD0(prepareFrame, status_t());
+ MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
+ MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
+ MOCK_METHOD0(onPresentDisplayCompleted, void());
+ MOCK_METHOD0(setViewportAndProjection, void());
+ MOCK_METHOD0(flip, void());
+ MOCK_CONST_METHOD1(dump, void(std::string& result));
+ MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/mock/CompositionEngine.cpp
new file mode 100644
index 0000000..778a09e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/CompositionEngine.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 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 <compositionengine/mock/CompositionEngine.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+CompositionEngine::CompositionEngine() = default;
+CompositionEngine::~CompositionEngine() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Display.cpp
index e6ac6bf..01cf112 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Display.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Display::Display() = default;
+Display::~Display() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
index e6ac6bf..581d1f7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 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
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/DisplayColorProfile.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+DisplayColorProfile::DisplayColorProfile() = default;
+DisplayColorProfile::~DisplayColorProfile() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
index e6ac6bf..bbbd7c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "compositionengine/mock/DisplaySurface.h"
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
-// Explicit default instantiation is recommended.
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
DisplaySurface::DisplaySurface() = default;
DisplaySurface::~DisplaySurface() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
similarity index 61%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Layer.cpp
index e6ac6bf..08483cb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Layer.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Layer::Layer() = default;
+Layer::~Layer() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
index e6ac6bf..607eaad 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/LayerFE.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+LayerFE::LayerFE() = default;
+LayerFE::~LayerFE() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Output.cpp
index e6ac6bf..44df4c3 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Output.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Output::Output() = default;
+Output::~Output() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
new file mode 100644
index 0000000..4da9377
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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 <compositionengine/mock/OutputLayer.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+OutputLayer::OutputLayer() = default;
+OutputLayer::~OutputLayer() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
index e6ac6bf..fe718d6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 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
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/RenderSurface.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+RenderSurface::RenderSurface() = default;
+RenderSurface::~RenderSurface() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
new file mode 100644
index 0000000..cb08b81
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 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 <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/Layer.h>
+#include <renderengine/RenderEngine.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+CompositionEngine::~CompositionEngine() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() {
+ return std::make_unique<CompositionEngine>();
+}
+
+CompositionEngine::CompositionEngine() = default;
+CompositionEngine::~CompositionEngine() = default;
+
+std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
+ DisplayCreationArgs&& args) {
+ return compositionengine::impl::createDisplay(*this, std::move(args));
+}
+
+std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(LayerCreationArgs&& args) {
+ return compositionengine::impl::createLayer(*this, std::move(args));
+}
+
+HWComposer& CompositionEngine::getHwComposer() const {
+ return *mHwComposer.get();
+}
+
+void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) {
+ mHwComposer = std::move(hwComposer);
+}
+
+renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
+ return *mRenderEngine.get();
+}
+
+void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
+ mRenderEngine = std::move(renderEngine);
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
new file mode 100644
index 0000000..f9d70e3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::DisplayCreationArgs&& args) {
+ return std::make_shared<Display>(compositionEngine, std::move(args));
+}
+
+Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
+ : compositionengine::impl::Output(compositionEngine),
+ mIsVirtual(args.isVirtual),
+ mId(args.displayId) {
+ editState().isSecure = args.isSecure;
+}
+
+Display::~Display() = default;
+
+const std::optional<DisplayId>& Display::getId() const {
+ return mId;
+}
+
+bool Display::isSecure() const {
+ return getState().isSecure;
+}
+
+bool Display::isVirtual() const {
+ return mIsVirtual;
+}
+
+void Display::disconnect() {
+ if (!mId) {
+ return;
+ }
+
+ auto& hwc = getCompositionEngine().getHwComposer();
+ hwc.disconnectDisplay(*mId);
+ mId.reset();
+}
+
+void Display::setColorTransform(const mat4& transform) {
+ Output::setColorTransform(transform);
+
+ auto& hwc = getCompositionEngine().getHwComposer();
+ status_t result = hwc.setColorTransform(*mId, transform);
+ ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
+ mId ? to_string(*mId).c_str() : "", result);
+}
+
+void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+ ui::RenderIntent renderIntent) {
+ if (mode == getState().colorMode && dataspace == getState().dataspace &&
+ renderIntent == getState().renderIntent) {
+ return;
+ }
+
+ if (mIsVirtual) {
+ ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+ return;
+ }
+
+ Output::setColorMode(mode, dataspace, renderIntent);
+
+ auto& hwc = getCompositionEngine().getHwComposer();
+ hwc.setActiveColorMode(*mId, mode, renderIntent);
+}
+
+void Display::dump(std::string& out) const {
+ using android::base::StringAppendF;
+
+ StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str());
+
+ out.append("\n ");
+
+ dumpVal(out, "isVirtual", mIsVirtual);
+ if (mId) {
+ dumpVal(out, "hwcId", to_string(*mId));
+ } else {
+ StringAppendF(&out, "no hwcId, ");
+ }
+
+ out.append("\n");
+
+ Output::dumpBase(out);
+}
+
+void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
+ setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+}
+
+void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
+ setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
+ std::move(args)));
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
new file mode 100644
index 0000000..130ab1d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 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 <array>
+#include <unordered_set>
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <log/log.h>
+#include <ui/DebugUtils.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+namespace impl {
+namespace {
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::RenderIntent;
+
+// ordered list of known SDR color modes
+const std::array<ColorMode, 3> sSdrColorModes = {
+ ColorMode::DISPLAY_BT2020,
+ ColorMode::DISPLAY_P3,
+ ColorMode::SRGB,
+};
+
+// ordered list of known HDR color modes
+const std::array<ColorMode, 2> sHdrColorModes = {
+ ColorMode::BT2100_PQ,
+ ColorMode::BT2100_HLG,
+};
+
+// ordered list of known SDR render intents
+const std::array<RenderIntent, 2> sSdrRenderIntents = {
+ RenderIntent::ENHANCE,
+ RenderIntent::COLORIMETRIC,
+};
+
+// ordered list of known HDR render intents
+const std::array<RenderIntent, 2> sHdrRenderIntents = {
+ RenderIntent::TONE_MAP_ENHANCE,
+ RenderIntent::TONE_MAP_COLORIMETRIC,
+};
+
+// map known color mode to dataspace
+Dataspace colorModeToDataspace(ColorMode mode) {
+ switch (mode) {
+ case ColorMode::SRGB:
+ return Dataspace::V0_SRGB;
+ case ColorMode::DISPLAY_P3:
+ return Dataspace::DISPLAY_P3;
+ case ColorMode::DISPLAY_BT2020:
+ return Dataspace::DISPLAY_BT2020;
+ case ColorMode::BT2100_HLG:
+ return Dataspace::BT2020_HLG;
+ case ColorMode::BT2100_PQ:
+ return Dataspace::BT2020_PQ;
+ default:
+ return Dataspace::UNKNOWN;
+ }
+}
+
+// Return a list of candidate color modes.
+std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
+ std::vector<ColorMode> candidates;
+
+ // add mode itself
+ candidates.push_back(mode);
+
+ // check if mode is HDR
+ bool isHdr = false;
+ for (auto hdrMode : sHdrColorModes) {
+ if (hdrMode == mode) {
+ isHdr = true;
+ break;
+ }
+ }
+
+ // add other HDR candidates when mode is HDR
+ if (isHdr) {
+ for (auto hdrMode : sHdrColorModes) {
+ if (hdrMode != mode) {
+ candidates.push_back(hdrMode);
+ }
+ }
+ }
+
+ // add other SDR candidates
+ for (auto sdrMode : sSdrColorModes) {
+ if (sdrMode != mode) {
+ candidates.push_back(sdrMode);
+ }
+ }
+
+ return candidates;
+}
+
+// Return a list of candidate render intents.
+std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
+ std::vector<RenderIntent> candidates;
+
+ // add intent itself
+ candidates.push_back(intent);
+
+ // check if intent is HDR
+ bool isHdr = false;
+ for (auto hdrIntent : sHdrRenderIntents) {
+ if (hdrIntent == intent) {
+ isHdr = true;
+ break;
+ }
+ }
+
+ if (isHdr) {
+ // add other HDR candidates when intent is HDR
+ for (auto hdrIntent : sHdrRenderIntents) {
+ if (hdrIntent != intent) {
+ candidates.push_back(hdrIntent);
+ }
+ }
+ } else {
+ // add other SDR candidates when intent is SDR
+ for (auto sdrIntent : sSdrRenderIntents) {
+ if (sdrIntent != intent) {
+ candidates.push_back(sdrIntent);
+ }
+ }
+ }
+
+ return candidates;
+}
+
+// Return the best color mode supported by HWC.
+ColorMode getHwcColorMode(
+ const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
+ ColorMode mode) {
+ std::vector<ColorMode> candidates = getColorModeCandidates(mode);
+ for (auto candidate : candidates) {
+ auto iter = hwcColorModes.find(candidate);
+ if (iter != hwcColorModes.end()) {
+ return candidate;
+ }
+ }
+
+ return ColorMode::NATIVE;
+}
+
+// Return the best render intent supported by HWC.
+RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
+ std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
+ for (auto candidate : candidates) {
+ for (auto hwcIntent : hwcIntents) {
+ if (candidate == hwcIntent) {
+ return candidate;
+ }
+ }
+ }
+
+ return RenderIntent::COLORIMETRIC;
+}
+
+} // anonymous namespace
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+ DisplayColorProfileCreationArgs&& args) {
+ return std::make_unique<DisplayColorProfile>(std::move(args));
+}
+
+DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+ : mHasWideColorGamut(args.hasWideColorGamut),
+ mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
+ populateColorModes(args.hwcColorModes);
+
+ std::vector<ui::Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
+ for (ui::Hdr hdrType : types) {
+ switch (hdrType) {
+ case ui::Hdr::HDR10_PLUS:
+ mHasHdr10Plus = true;
+ break;
+ case ui::Hdr::HDR10:
+ mHasHdr10 = true;
+ break;
+ case ui::Hdr::HLG:
+ mHasHLG = true;
+ break;
+ case ui::Hdr::DOLBY_VISION:
+ mHasDolbyVision = true;
+ break;
+ default:
+ ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
+ }
+ }
+
+ float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
+ float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
+ float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
+
+ minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
+ maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
+ maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
+ if (args.hasWideColorGamut) {
+ // insert HDR10/HLG as we will force client composition for HDR10/HLG
+ // layers
+ if (!hasHDR10Support()) {
+ types.push_back(ui::Hdr::HDR10);
+ }
+
+ if (!hasHLGSupport()) {
+ types.push_back(ui::Hdr::HLG);
+ }
+ }
+
+ mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
+}
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+bool DisplayColorProfile::isValid() const {
+ return true;
+}
+
+bool DisplayColorProfile::hasWideColorGamut() const {
+ return mHasWideColorGamut;
+}
+
+int32_t DisplayColorProfile::getSupportedPerFrameMetadata() const {
+ return mSupportedPerFrameMetadata;
+}
+
+bool DisplayColorProfile::hasHDR10PlusSupport() const {
+ return mHasHdr10Plus;
+}
+
+bool DisplayColorProfile::hasHDR10Support() const {
+ return mHasHdr10;
+}
+
+bool DisplayColorProfile::hasHLGSupport() const {
+ return mHasHLG;
+}
+
+bool DisplayColorProfile::hasDolbyVisionSupport() const {
+ return mHasDolbyVision;
+}
+
+const HdrCapabilities& DisplayColorProfile::getHdrCapabilities() const {
+ return mHdrCapabilities;
+}
+
+void DisplayColorProfile::populateColorModes(
+ const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
+ if (!hasWideColorGamut()) {
+ return;
+ }
+
+ // collect all known SDR render intents
+ std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
+ sSdrRenderIntents.end());
+ auto iter = hwcColorModes.find(ColorMode::SRGB);
+ if (iter != hwcColorModes.end()) {
+ for (auto intent : iter->second) {
+ sdrRenderIntents.insert(intent);
+ }
+ }
+
+ // add all known SDR combinations
+ for (auto intent : sdrRenderIntents) {
+ for (auto mode : sSdrColorModes) {
+ addColorMode(hwcColorModes, mode, intent);
+ }
+ }
+
+ // collect all known HDR render intents
+ std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
+ sHdrRenderIntents.end());
+ iter = hwcColorModes.find(ColorMode::BT2100_PQ);
+ if (iter != hwcColorModes.end()) {
+ for (auto intent : iter->second) {
+ hdrRenderIntents.insert(intent);
+ }
+ }
+
+ // add all known HDR combinations
+ for (auto intent : hdrRenderIntents) {
+ for (auto mode : sHdrColorModes) {
+ addColorMode(hwcColorModes, mode, intent);
+ }
+ }
+}
+
+// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
+// supported by HWC.
+void DisplayColorProfile::addColorMode(
+ const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes, const ColorMode mode,
+ const RenderIntent intent) {
+ // find the best color mode
+ const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+
+ // find the best render intent
+ auto iter = hwcColorModes.find(hwcColorMode);
+ const auto& hwcIntents =
+ iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
+ const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
+
+ const Dataspace dataspace = colorModeToDataspace(mode);
+ const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
+
+ ALOGV("DisplayColorProfile: map (%s, %s) to (%s, %s, %s)",
+ dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+ decodeRenderIntent(intent).c_str(),
+ dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
+ decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
+
+ mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+}
+
+bool DisplayColorProfile::hasRenderIntent(RenderIntent intent) const {
+ // assume a render intent is supported when SRGB supports it; we should
+ // get rid of that assumption.
+ auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent));
+ return iter != mColorModes.end() && iter->second.renderIntent == intent;
+}
+
+bool DisplayColorProfile::hasLegacyHdrSupport(Dataspace dataspace) const {
+ if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
+ (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
+ auto iter =
+ mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
+ return iter == mColorModes.end() || iter->second.dataspace != dataspace;
+ }
+
+ return false;
+}
+
+void DisplayColorProfile::getBestColorMode(Dataspace dataspace, RenderIntent intent,
+ Dataspace* outDataspace, ColorMode* outMode,
+ RenderIntent* outIntent) const {
+ auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
+ if (iter != mColorModes.end()) {
+ *outDataspace = iter->second.dataspace;
+ *outMode = iter->second.colorMode;
+ *outIntent = iter->second.renderIntent;
+ } else {
+ // this is unexpected on a WCG display
+ if (hasWideColorGamut()) {
+ ALOGE("map unknown (%s)/(%s) to default color mode",
+ dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+ decodeRenderIntent(intent).c_str());
+ }
+
+ *outDataspace = Dataspace::UNKNOWN;
+ *outMode = ColorMode::NATIVE;
+ *outIntent = RenderIntent::COLORIMETRIC;
+ }
+}
+
+void DisplayColorProfile::dump(std::string& out) const {
+ out.append(" Composition Display Color State:");
+
+ out.append("\n HWC Support: ");
+
+ dumpVal(out, "wideColorGamut", hasWideColorGamut());
+ dumpVal(out, "hdr10plus", hasHDR10PlusSupport());
+ dumpVal(out, "hdr10", hasHDR10Support());
+ dumpVal(out, "hlg", hasHLGSupport());
+ dumpVal(out, "dv", hasDolbyVisionSupport());
+ dumpVal(out, "metadata", getSupportedPerFrameMetadata());
+
+ out.append("\n");
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index e6ac6bf..db6d4f2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/DisplaySurface.h>
-namespace android {
-namespace mock {
+namespace android::compositionengine {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
DisplaySurface::~DisplaySurface() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
new file mode 100644
index 0000000..9598430
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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 <cinttypes>
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine::impl {
+
+using android::base::StringAppendF;
+
+void dumpVal(std::string& out, const char* name, bool value) {
+ StringAppendF(&out, "%s=%s ", name, value ? "true" : "false");
+}
+
+void dumpVal(std::string& out, const char* name, const void* value) {
+ StringAppendF(&out, "%s=%p ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, int value) {
+ StringAppendF(&out, "%s=%d ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, float value) {
+ StringAppendF(&out, "%s=%f ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, uint32_t value) {
+ StringAppendF(&out, "%s=%u ", name, value);
+}
+
+void dumpHex(std::string& out, const char* name, uint64_t value) {
+ StringAppendF(&out, "%s=0x08%" PRIx64 " ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const char* value) {
+ StringAppendF(&out, "%s=%s ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& value) {
+ dumpVal(out, name, value.c_str());
+}
+
+void dumpVal(std::string& out, const char* name, const char* valueName, int value) {
+ StringAppendF(&out, "%s=%s (%d) ", name, valueName, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) {
+ dumpVal(out, name, valueName.c_str(), value);
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
+ StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Rect& rect) {
+ StringAppendF(&out, "%s=[%d %d %d %d] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Region& region) {
+ region.dump(out, name, 0);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
+ transform.dump(out, name);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Size& size) {
+ StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
+void dumpVal(std::string& out, const char* name, const mat4& tr) {
+ StringAppendF(&out,
+ "%s=["
+ /* clang-format off */
+ "[%0.3f,%0.3f,%0.3f,%0.3f]"
+ "[%0.3f,%0.3f,%0.3f,%0.3f]"
+ "[%0.3f,%0.3f,%0.3f,%0.3f]"
+ "[%0.3f,%0.3f,%0.3f,%0.3f]]",
+ name,
+ tr[0][0], tr[1][0], tr[2][0], tr[3][0],
+ tr[0][1], tr[1][1], tr[2][1], tr[3][1],
+ tr[0][2], tr[1][2], tr[2][2], tr[3][2],
+ tr[0][3], tr[1][3], tr[2][3], tr[3][3]
+ ); /* clang-format on */
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
new file mode 100644
index 0000000..8613210
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::compositionengine::impl {
+
+HwcBufferCache::HwcBufferCache() {
+ std::fill(std::begin(mBuffers), std::end(mBuffers),
+ std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr));
+}
+bool HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
+ // search for cached buffer first
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ // Weak pointers in the cache may have had their object destroyed.
+ // Comparisons between weak pointers will accurately reflect this case,
+ // but comparisons between weak and strong may not. Thus, we create a weak
+ // pointer from strong pointer buffer
+ wp<GraphicBuffer> weakCopy(buffer);
+ if (mBuffers[i].second == weakCopy) {
+ *outSlot = i;
+ return true;
+ }
+ }
+
+ // use the least-recently used slot
+ *outSlot = getLeastRecentlyUsedSlot();
+ return false;
+}
+
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+ auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers));
+ return std::distance(std::begin(mBuffers), iter);
+}
+
+void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer) {
+ bool cached = getSlot(buffer, outSlot);
+
+ auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
+ if (cached) {
+ // already cached in HWC, skip sending the buffer
+ *outBuffer = nullptr;
+ currentCounter = getCounter();
+ } else {
+ *outBuffer = buffer;
+
+ // update cache
+ currentBuffer = buffer;
+ currentCounter = getCounter();
+ }
+}
+
+uint64_t HwcBufferCache::getCounter() {
+ return mCounter++;
+}
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
new file mode 100644
index 0000000..96e9731
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/impl/Layer.h>
+
+namespace android::compositionengine {
+
+Layer::~Layer() = default;
+
+namespace impl {
+
+std::shared_ptr<compositionengine::Layer> createLayer(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::LayerCreationArgs&& args) {
+ return std::make_shared<Layer>(compositionEngine, std::move(args));
+}
+
+Layer::Layer(const CompositionEngine& compositionEngine, LayerCreationArgs&& args)
+ : mCompositionEngine(compositionEngine), mLayerFE(args.layerFE) {
+ static_cast<void>(mCompositionEngine); // Temporary use to prevent an unused warning
+}
+
+Layer::~Layer() = default;
+
+sp<LayerFE> Layer::getLayerFE() const {
+ return mLayerFE.promote();
+}
+
+const LayerCompositionState& Layer::getState() const {
+ return mState;
+}
+
+LayerCompositionState& Layer::editState() {
+ return mState;
+}
+
+void Layer::dump(std::string& out) const {
+ auto layerFE = getLayerFE();
+ android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
+ layerFE ? layerFE->getDebugName() : "<unknown>");
+ mState.dump(out);
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
new file mode 100644
index 0000000..40c4da9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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 <android-base/stringprintf.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+
+namespace android::compositionengine::impl {
+
+namespace {
+
+using android::compositionengine::impl::dumpVal;
+
+void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color value) {
+ using android::base::StringAppendF;
+ StringAppendF(&out, "%s=[%d %d %d] ", name, value.r, value.g, value.b);
+}
+
+void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) {
+ out.append(" ");
+ dumpVal(out, "isSecure", state.isSecure);
+ dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop);
+ dumpVal(out, "geomBufferUsesDisplayInverseTransform",
+ state.geomBufferUsesDisplayInverseTransform);
+ dumpVal(out, "geomLayerTransform", state.geomLayerTransform);
+
+ out.append("\n ");
+ dumpVal(out, "geomBufferSize", state.geomBufferSize);
+ dumpVal(out, "geomContentCrop", state.geomContentCrop);
+ dumpVal(out, "geomCrop", state.geomCrop);
+ dumpVal(out, "geomBufferTransform", state.geomBufferTransform);
+
+ out.append("\n ");
+ dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion);
+
+ out.append(" ");
+ dumpVal(out, "geomLayerBounds", state.geomLayerBounds);
+
+ out.append("\n ");
+ dumpVal(out, "blend", toString(state.blendMode), state.blendMode);
+ dumpVal(out, "alpha", state.alpha);
+
+ out.append("\n ");
+ dumpVal(out, "type", state.type);
+ dumpVal(out, "appId", state.appId);
+
+ dumpVal(out, "composition type", toString(state.compositionType), state.compositionType);
+
+ out.append("\n buffer: ");
+ dumpVal(out, "buffer", state.buffer.get());
+ dumpVal(out, "slot", state.bufferSlot);
+
+ out.append("\n ");
+ dumpVal(out, "sideband stream", state.sidebandStream.get());
+
+ out.append("\n ");
+ dumpVal(out, "color", state.color);
+
+ out.append("\n ");
+ dumpVal(out, "dataspace", toString(state.dataspace), state.dataspace);
+ dumpVal(out, "hdr metadata types", state.hdrMetadata.validTypes);
+ dumpVal(out, "colorTransform", state.colorTransform);
+
+ out.append("\n");
+}
+
+} // namespace
+
+void LayerCompositionState::dump(std::string& out) const {
+ out.append(" frontend:\n");
+ dumpFrontEnd(out, frontEnd);
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
new file mode 100644
index 0000000..d22bdaf
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputLayer.h>
+#include <ui/DebugUtils.h>
+
+namespace android::compositionengine {
+
+Output::~Output() = default;
+
+namespace impl {
+
+Output::Output(const CompositionEngine& compositionEngine)
+ : mCompositionEngine(compositionEngine) {}
+
+Output::~Output() = default;
+
+const CompositionEngine& Output::getCompositionEngine() const {
+ return mCompositionEngine;
+}
+
+bool Output::isValid() const {
+ return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
+ mRenderSurface->isValid();
+}
+
+const std::string& Output::getName() const {
+ return mName;
+}
+
+void Output::setName(const std::string& name) {
+ mName = name;
+}
+
+void Output::setCompositionEnabled(bool enabled) {
+ if (mState.isEnabled == enabled) {
+ return;
+ }
+
+ mState.isEnabled = enabled;
+ dirtyEntireOutput();
+}
+
+void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
+ const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+ mState.transform = transform;
+ mState.orientation = orientation;
+ mState.scissor = scissor;
+ mState.frame = frame;
+ mState.viewport = viewport;
+ mState.needsFiltering = needsFiltering;
+
+ dirtyEntireOutput();
+}
+
+// TODO(lpique): Rename setSize() once more is moved.
+void Output::setBounds(const ui::Size& size) {
+ mRenderSurface->setDisplaySize(size);
+ // TODO(lpique): Rename mState.size once more is moved.
+ mState.bounds = Rect(mRenderSurface->getSize());
+
+ dirtyEntireOutput();
+}
+
+void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
+ mState.layerStackId = layerStackId;
+ mState.layerStackInternal = isInternal;
+
+ dirtyEntireOutput();
+}
+
+void Output::setColorTransform(const mat4& transform) {
+ const bool isIdentity = (transform == mat4());
+ const auto newColorTransform =
+ isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+
+ if (mState.colorTransform == newColorTransform) {
+ return;
+ }
+
+ mState.colorTransform = newColorTransform;
+
+ dirtyEntireOutput();
+}
+
+void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+ ui::RenderIntent renderIntent) {
+ if (mState.colorMode == mode && mState.dataspace == dataspace &&
+ mState.renderIntent == renderIntent) {
+ return;
+ }
+
+ mState.colorMode = mode;
+ mState.dataspace = dataspace;
+ mState.renderIntent = renderIntent;
+
+ mRenderSurface->setBufferDataspace(dataspace);
+
+ ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
+ decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
+ renderIntent);
+
+ dirtyEntireOutput();
+}
+
+void Output::dump(std::string& out) const {
+ using android::base::StringAppendF;
+
+ StringAppendF(&out, " Composition Output State: [\"%s\"]", mName.c_str());
+
+ out.append("\n ");
+
+ dumpBase(out);
+}
+
+void Output::dumpBase(std::string& out) const {
+ mState.dump(out);
+
+ if (mDisplayColorProfile) {
+ mDisplayColorProfile->dump(out);
+ } else {
+ out.append(" No display color profile!\n");
+ }
+
+ if (mRenderSurface) {
+ mRenderSurface->dump(out);
+ } else {
+ out.append(" No render surface!\n");
+ }
+
+ android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size());
+ for (const auto& outputLayer : mOutputLayersOrderedByZ) {
+ if (!outputLayer) {
+ continue;
+ }
+ outputLayer->dump(out);
+ }
+}
+
+compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
+ return mDisplayColorProfile.get();
+}
+
+void Output::setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+ mDisplayColorProfile = std::move(mode);
+}
+
+void Output::setDisplayColorProfileForTest(
+ std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+ mDisplayColorProfile = std::move(mode);
+}
+
+compositionengine::RenderSurface* Output::getRenderSurface() const {
+ return mRenderSurface.get();
+}
+
+void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
+ mRenderSurface = std::move(surface);
+ mState.bounds = Rect(mRenderSurface->getSize());
+
+ dirtyEntireOutput();
+}
+
+void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
+ mRenderSurface = std::move(surface);
+}
+
+const OutputCompositionState& Output::getState() const {
+ return mState;
+}
+
+OutputCompositionState& Output::editState() {
+ return mState;
+}
+
+Region Output::getDirtyRegion(bool repaintEverything) const {
+ Region dirty(mState.viewport);
+ if (!repaintEverything) {
+ dirty.andSelf(mState.dirtyRegion);
+ }
+ return dirty;
+}
+
+bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+ // The layerStackId's must match, and also the layer must not be internal
+ // only when not on an internal output.
+ return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
+}
+
+compositionengine::OutputLayer* Output::getOutputLayerForLayer(
+ compositionengine::Layer* layer) const {
+ for (const auto& outputLayer : mOutputLayersOrderedByZ) {
+ if (outputLayer && &outputLayer->getLayer() == layer) {
+ return outputLayer.get();
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
+ std::optional<DisplayId> displayId, std::shared_ptr<compositionengine::Layer> layer,
+ sp<compositionengine::LayerFE> layerFE) {
+ for (auto& outputLayer : mOutputLayersOrderedByZ) {
+ if (outputLayer && &outputLayer->getLayer() == layer.get()) {
+ return std::move(outputLayer);
+ }
+ }
+ return createOutputLayer(mCompositionEngine, displayId, *this, layer, layerFE);
+}
+
+void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) {
+ mOutputLayersOrderedByZ = std::move(layers);
+}
+
+const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const {
+ return mOutputLayersOrderedByZ;
+}
+
+void Output::dirtyEntireOutput() {
+ mState.dirtyRegion.set(mState.bounds);
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
new file mode 100644
index 0000000..9549054
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine::impl {
+
+void OutputCompositionState::dump(std::string& out) const {
+ out.append(" ");
+ dumpVal(out, "isEnabled", isEnabled);
+ dumpVal(out, "isSecure", isSecure);
+
+ dumpVal(out, "layerStack", layerStackId);
+ dumpVal(out, "layerStackInternal", layerStackInternal);
+
+ out.append("\n ");
+
+ dumpVal(out, "transform", transform);
+
+ out.append("\n ");
+
+ dumpVal(out, "frame", frame);
+ dumpVal(out, "viewport", viewport);
+ dumpVal(out, "scissor", scissor);
+ dumpVal(out, "needsFiltering", needsFiltering);
+
+ out.append("\n ");
+
+ dumpVal(out, "colorMode", toString(colorMode), colorMode);
+ dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
+ dumpVal(out, "dataspace", toString(dataspace), dataspace);
+ dumpVal(out, "colorTransform", colorTransform);
+
+ out.append("\n");
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
new file mode 100644
index 0000000..5ce72b0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+OutputLayer::~OutputLayer() = default;
+
+namespace impl {
+
+namespace {
+
+FloatRect reduce(const FloatRect& win, const Region& exclude) {
+ if (CC_LIKELY(exclude.isEmpty())) {
+ return win;
+ }
+ // Convert through Rect (by rounding) for lack of FloatRegion
+ return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
+}
+
+} // namespace
+
+std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
+ const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId,
+ const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer,
+ sp<compositionengine::LayerFE> layerFE) {
+ auto result = std::make_unique<OutputLayer>(output, layer, layerFE);
+ result->initialize(compositionEngine, displayId);
+ return result;
+}
+
+OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp<LayerFE> layerFE)
+ : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
+
+OutputLayer::~OutputLayer() = default;
+
+void OutputLayer::initialize(const CompositionEngine& compositionEngine,
+ std::optional<DisplayId> displayId) {
+ if (!displayId) {
+ return;
+ }
+
+ auto& hwc = compositionEngine.getHwComposer();
+
+ mState.hwc.emplace(std::shared_ptr<HWC2::Layer>(hwc.createLayer(*displayId),
+ [&hwc, displayId](HWC2::Layer* layer) {
+ hwc.destroyLayer(*displayId, layer);
+ }));
+}
+
+const compositionengine::Output& OutputLayer::getOutput() const {
+ return mOutput;
+}
+
+compositionengine::Layer& OutputLayer::getLayer() const {
+ return *mLayer;
+}
+
+compositionengine::LayerFE& OutputLayer::getLayerFE() const {
+ return *mLayerFE;
+}
+
+const OutputLayerCompositionState& OutputLayer::getState() const {
+ return mState;
+}
+
+OutputLayerCompositionState& OutputLayer::editState() {
+ return mState;
+}
+
+Rect OutputLayer::calculateInitialCrop() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+
+ // apply the projection's clipping to the window crop in
+ // layerstack space, and convert-back to layer space.
+ // if there are no window scaling involved, this operation will map to full
+ // pixels in the buffer.
+
+ FloatRect activeCropFloat =
+ reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion);
+
+ const Rect& viewport = mOutput.getState().viewport;
+ const ui::Transform& layerTransform = layerState.geomLayerTransform;
+ const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
+ // Transform to screen space.
+ activeCropFloat = layerTransform.transform(activeCropFloat);
+ activeCropFloat = activeCropFloat.intersect(viewport.toFloatRect());
+ // Back to layer space to work with the content crop.
+ activeCropFloat = inverseLayerTransform.transform(activeCropFloat);
+
+ // This needs to be here as transform.transform(Rect) computes the
+ // transformed rect and then takes the bounding box of the result before
+ // returning. This means
+ // transform.inverse().transform(transform.transform(Rect)) != Rect
+ // in which case we need to make sure the final rect is clipped to the
+ // display bounds.
+ Rect activeCrop{activeCropFloat};
+ if (!activeCrop.intersect(layerState.geomBufferSize, &activeCrop)) {
+ activeCrop.clear();
+ }
+ return activeCrop;
+}
+
+FloatRect OutputLayer::calculateOutputSourceCrop() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+ const auto& outputState = mOutput.getState();
+
+ if (!layerState.geomUsesSourceCrop) {
+ return {};
+ }
+
+ // the content crop is the area of the content that gets scaled to the
+ // layer's size. This is in buffer space.
+ FloatRect crop = layerState.geomContentCrop.toFloatRect();
+
+ // In addition there is a WM-specified crop we pull from our drawing state.
+ Rect activeCrop = calculateInitialCrop();
+ const Rect& bufferSize = layerState.geomBufferSize;
+
+ int winWidth = bufferSize.getWidth();
+ int winHeight = bufferSize.getHeight();
+
+ // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1])
+ // if display frame hasn't been set and the parent is an unbounded layer.
+ if (winWidth < 0 && winHeight < 0) {
+ return crop;
+ }
+
+ // Transform the window crop to match the buffer coordinate system,
+ // which means using the inverse of the current transform set on the
+ // SurfaceFlingerConsumer.
+ uint32_t invTransform = layerState.geomBufferTransform;
+ if (layerState.geomBufferUsesDisplayInverseTransform) {
+ /*
+ * the code below applies the primary display's inverse transform to the
+ * buffer
+ */
+ uint32_t invTransformOrient = outputState.orientation;
+ // calculate the inverse transform
+ if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
+ invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
+ }
+ // and apply to the current transform
+ invTransform =
+ (ui::Transform(invTransformOrient) * ui::Transform(invTransform)).getOrientation();
+ }
+
+ if (invTransform & HAL_TRANSFORM_ROT_90) {
+ // If the activeCrop has been rotate the ends are rotated but not
+ // the space itself so when transforming ends back we can't rely on
+ // 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) {
+ invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
+ }
+ std::swap(winWidth, winHeight);
+ }
+ const Rect winCrop =
+ 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);
+
+ float insetL = winCrop.left * xScale;
+ float insetT = winCrop.top * yScale;
+ float insetR = (winWidth - winCrop.right) * xScale;
+ float insetB = (winHeight - winCrop.bottom) * yScale;
+
+ crop.left += insetL;
+ crop.top += insetT;
+ crop.right -= insetR;
+ crop.bottom -= insetB;
+
+ return crop;
+}
+
+Rect OutputLayer::calculateOutputDisplayFrame() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+ const auto& outputState = mOutput.getState();
+
+ // apply the layer's transform, followed by the display's global transform
+ // here we're guaranteed that the layer's transform preserves rects
+ Region activeTransparentRegion = layerState.geomActiveTransparentRegion;
+ const ui::Transform& layerTransform = layerState.geomLayerTransform;
+ const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
+ const Rect& bufferSize = layerState.geomBufferSize;
+ Rect activeCrop = layerState.geomCrop;
+ if (!activeCrop.isEmpty() && bufferSize.isValid()) {
+ activeCrop = layerTransform.transform(activeCrop);
+ if (!activeCrop.intersect(outputState.viewport, &activeCrop)) {
+ activeCrop.clear();
+ }
+ activeCrop = inverseLayerTransform.transform(activeCrop, true);
+ // This needs to be here as transform.transform(Rect) computes the
+ // transformed rect and then takes the bounding box of the result before
+ // returning. This means
+ // transform.inverse().transform(transform.transform(Rect)) != Rect
+ // in which case we need to make sure the final rect is clipped to the
+ // display bounds.
+ if (!activeCrop.intersect(bufferSize, &activeCrop)) {
+ activeCrop.clear();
+ }
+ // mark regions outside the crop as transparent
+ activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
+ activeTransparentRegion.orSelf(
+ Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
+ activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
+ activeTransparentRegion.orSelf(
+ Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
+ }
+
+ // reduce uses a FloatRect to provide more accuracy during the
+ // transformation. We then round upon constructing 'frame'.
+ Rect frame{
+ layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
+ if (!frame.intersect(outputState.viewport, &frame)) {
+ frame.clear();
+ }
+ const ui::Transform displayTransform{outputState.transform};
+
+ return displayTransform.transform(frame);
+}
+
+uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
+ const auto& layerState = mLayer->getState().frontEnd;
+ const auto& outputState = mOutput.getState();
+
+ /*
+ * Transformations are applied in this order:
+ * 1) buffer orientation/flip/mirror
+ * 2) state transformation (window manager)
+ * 3) layer orientation (screen orientation)
+ * (NOTE: the matrices are multiplied in reverse order)
+ */
+ const ui::Transform& layerTransform = layerState.geomLayerTransform;
+ const ui::Transform displayTransform{outputState.orientation};
+ const ui::Transform bufferTransform{layerState.geomBufferTransform};
+ ui::Transform transform(displayTransform * layerTransform * bufferTransform);
+
+ if (layerState.geomBufferUsesDisplayInverseTransform) {
+ /*
+ * the code below applies the primary display's inverse transform to the
+ * buffer
+ */
+ uint32_t invTransform = outputState.orientation;
+ // calculate the inverse transform
+ if (invTransform & HAL_TRANSFORM_ROT_90) {
+ invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
+ }
+
+ /*
+ * Here we cancel out the orientation component of the WM transform.
+ * The scaling and translate components are already included in our bounds
+ * computation so it's enough to just omit it in the composition.
+ * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
+ */
+ transform = ui::Transform(invTransform) * displayTransform * bufferTransform;
+ }
+
+ // this gives us only the "orientation" component of the transform
+ return transform.getOrientation();
+} // namespace impl
+
+void OutputLayer::updateCompositionState(bool includeGeometry) {
+ if (includeGeometry) {
+ mState.displayFrame = calculateOutputDisplayFrame();
+ mState.sourceCrop = calculateOutputSourceCrop();
+ mState.bufferTransform =
+ static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
+
+ if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) ||
+ (mState.bufferTransform & ui::Transform::ROT_INVALID)) {
+ mState.forceClientComposition = true;
+ }
+ }
+}
+
+void OutputLayer::writeStateToHWC(bool includeGeometry) const {
+ // Skip doing this if there is no HWC interface
+ if (!mState.hwc) {
+ return;
+ }
+
+ auto& hwcLayer = (*mState.hwc).hwcLayer;
+ if (!hwcLayer) {
+ ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
+ mLayerFE->getDebugName(), mOutput.getName().c_str());
+ return;
+ }
+
+ if (includeGeometry) {
+ // Output dependent state
+
+ if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame);
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+ mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top,
+ mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+ "%s (%d)",
+ mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top,
+ mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ if (auto error =
+ hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform));
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
+ toString(mState.bufferTransform).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ // Output independent state
+
+ const auto& outputIndependentState = mLayer->getState().frontEnd;
+
+ if (auto error = hwcLayer->setBlendMode(
+ static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
+ toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
+ outputIndependentState.alpha, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+
+ if (auto error =
+ hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
+ error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+ }
+}
+
+void OutputLayer::dump(std::string& out) const {
+ using android::base::StringAppendF;
+
+ StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(),
+ mLayerFE->getDebugName());
+ mState.dump(out);
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
new file mode 100644
index 0000000..861ea57
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+
+#include "DisplayHardware/HWC2.h"
+
+namespace android::compositionengine::impl {
+
+namespace {
+
+void dumpHwc(const OutputLayerCompositionState::Hwc& hwc, std::string& out) {
+ out.append("\n hwc: ");
+
+ if (hwc.hwcLayer == nullptr) {
+ out.append("No layer ");
+ } else {
+ dumpHex(out, "layer", hwc.hwcLayer->getId());
+ }
+
+ dumpVal(out, "composition", toString(hwc.hwcCompositionType), hwc.hwcCompositionType);
+}
+
+} // namespace
+
+void OutputLayerCompositionState::dump(std::string& out) const {
+ out.append(" ");
+ dumpVal(out, "visibleRegion", visibleRegion);
+
+ out.append(" ");
+ dumpVal(out, "forceClientComposition", forceClientComposition);
+ dumpVal(out, "clearClientTarget", clearClientTarget);
+ dumpVal(out, "displayFrame", displayFrame);
+ dumpVal(out, "sourceCrop", sourceCrop);
+ dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
+ dumpVal(out, "z-index", z);
+
+ if (hwc) {
+ dumpHwc(*hwc, out);
+ }
+
+ out.append("\n");
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
new file mode 100644
index 0000000..b4dfba1
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <android-base/stringprintf.h>
+#include <android/native_window.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <log/log.h>
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/Trace.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+RenderSurface::~RenderSurface() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) {
+ return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args));
+}
+
+RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
+ RenderSurfaceCreationArgs&& args)
+ : mCompositionEngine(compositionEngine),
+ mDisplay(display),
+ mNativeWindow(args.nativeWindow),
+ mDisplaySurface(args.displaySurface),
+ mSize(args.displayWidth, args.displayHeight) {}
+
+RenderSurface::~RenderSurface() = default;
+
+bool RenderSurface::isValid() const {
+ return mSize.isValid();
+}
+
+void RenderSurface::initialize() {
+ ANativeWindow* const window = mNativeWindow.get();
+
+ int status = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
+ status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+ ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
+ status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+ ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
+}
+
+const ui::Size& RenderSurface::getSize() const {
+ return mSize;
+}
+
+const sp<Fence>& RenderSurface::getClientTargetAcquireFence() const {
+ return mDisplaySurface->getClientTargetAcquireFence();
+}
+
+void RenderSurface::setDisplaySize(const ui::Size& size) {
+ mDisplaySurface->resizeBuffers(size.width, size.height);
+ mSize = size;
+}
+
+void RenderSurface::setBufferDataspace(ui::Dataspace dataspace) {
+ native_window_set_buffers_data_space(mNativeWindow.get(),
+ static_cast<android_dataspace>(dataspace));
+}
+
+void RenderSurface::setProtected(bool useProtected) {
+ uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+ if (useProtected) {
+ usageFlags |= GRALLOC_USAGE_PROTECTED;
+ }
+ const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
+ ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
+}
+
+status_t RenderSurface::beginFrame(bool mustRecompose) {
+ return mDisplaySurface->beginFrame(mustRecompose);
+}
+
+status_t RenderSurface::prepareFrame() {
+ auto& hwc = mCompositionEngine.getHwComposer();
+ const auto id = mDisplay.getId();
+ if (id) {
+ status_t error = hwc.prepare(*id, mDisplay);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ }
+
+ DisplaySurface::CompositionType compositionType;
+ const bool hasClient = hwc.hasClientComposition(id);
+ const bool hasDevice = hwc.hasDeviceComposition(id);
+ if (hasClient && hasDevice) {
+ compositionType = DisplaySurface::COMPOSITION_MIXED;
+ } else if (hasClient) {
+ compositionType = DisplaySurface::COMPOSITION_GLES;
+ } else if (hasDevice) {
+ compositionType = DisplaySurface::COMPOSITION_HWC;
+ } else {
+ // Nothing to do -- when turning the screen off we get a frame like
+ // this. Call it a HWC frame since we won't be doing any GLES work but
+ // will do a prepare/set cycle.
+ compositionType = DisplaySurface::COMPOSITION_HWC;
+ }
+ return mDisplaySurface->prepareFrame(compositionType);
+}
+
+sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) {
+ ATRACE_CALL();
+ int fd = -1;
+ ANativeWindowBuffer* buffer = nullptr;
+
+ status_t result = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
+
+ if (result != NO_ERROR) {
+ ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
+ mDisplay.getName().c_str(), result);
+ // Return fast here as we can't do much more - any rendering we do
+ // now will just be wrong.
+ return mGraphicBuffer;
+ }
+
+ ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+ mGraphicBuffer->getNativeBuffer()->handle);
+ mGraphicBuffer = GraphicBuffer::from(buffer);
+
+ *bufferFence = base::unique_fd(fd);
+
+ return mGraphicBuffer;
+}
+
+void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
+ auto& hwc = mCompositionEngine.getHwComposer();
+ const auto id = mDisplay.getId();
+
+ if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) {
+ // hasFlipClientTargetRequest could return true even if we haven't
+ // dequeued a buffer before. Try dequeueing one if we don't have a
+ // buffer ready.
+ if (mGraphicBuffer == nullptr) {
+ ALOGI("Attempting to queue a client composited buffer without one "
+ "previously dequeued for display [%s]. Attempting to dequeue "
+ "a scratch buffer now",
+ mDisplay.getName().c_str());
+ // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+ // after a successful call to queueBuffer, or if dequeueBuffer has
+ // never been called.
+ base::unique_fd unused;
+ dequeueBuffer(&unused);
+ }
+
+ if (mGraphicBuffer == nullptr) {
+ ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
+ } else {
+ status_t result =
+ mNativeWindow->queueBuffer(mNativeWindow.get(),
+ mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+ if (result != NO_ERROR) {
+ ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
+ result);
+ // We risk blocking on dequeueBuffer if the primary display failed
+ // to queue up its buffer, so crash here.
+ if (!mDisplay.isVirtual()) {
+ LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
+ } else {
+ mNativeWindow->cancelBuffer(mNativeWindow.get(),
+ mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+ }
+ }
+
+ mGraphicBuffer = nullptr;
+ }
+ }
+
+ status_t result = mDisplaySurface->advanceFrame();
+ if (result != NO_ERROR) {
+ ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplay.getName().c_str(), result);
+ }
+}
+
+void RenderSurface::onPresentDisplayCompleted() {
+ mDisplaySurface->onFrameCommitted();
+}
+
+void RenderSurface::setViewportAndProjection() {
+ auto& renderEngine = mCompositionEngine.getRenderEngine();
+ Rect sourceCrop = Rect(mSize);
+ renderEngine.setViewportAndProjection(mSize.width, mSize.height, sourceCrop,
+ ui::Transform::ROT_0);
+}
+
+void RenderSurface::flip() {
+ mPageFlipCount++;
+}
+
+void RenderSurface::dump(std::string& out) const {
+ using android::base::StringAppendF;
+
+ out.append(" Composition RenderSurface State:");
+
+ out.append("\n ");
+
+ dumpVal(out, "size", mSize);
+ StringAppendF(&out, "ANativeWindow=%p (format %d) ", mNativeWindow.get(),
+ ANativeWindow_getFormat(mNativeWindow.get()));
+ dumpVal(out, "flips", mPageFlipCount);
+ out.append("\n");
+
+ String8 surfaceDump;
+ mDisplaySurface->dumpAsString(surfaceDump);
+ out.append(surfaceDump);
+}
+
+std::uint32_t RenderSurface::getPageFlipCount() const {
+ return mPageFlipCount;
+}
+
+void RenderSurface::setPageFlipCountForTest(std::uint32_t count) {
+ mPageFlipCount = count;
+}
+
+void RenderSurface::setSizeForTest(const ui::Size& size) {
+ mSize = size;
+}
+
+sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
+ return mGraphicBuffer;
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
new file mode 100644
index 0000000..3766f27
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 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 <compositionengine/impl/CompositionEngine.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+using ::testing::StrictMock;
+
+class CompositionEngineTest : public testing::Test {
+public:
+ ~CompositionEngineTest() override;
+ mock::HWComposer* mHwc = new StrictMock<mock::HWComposer>();
+ renderengine::mock::RenderEngine* mRenderEngine =
+ new StrictMock<renderengine::mock::RenderEngine>();
+ impl::CompositionEngine mEngine;
+};
+
+CompositionEngineTest::~CompositionEngineTest() = default;
+
+TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
+ auto engine = impl::createCompositionEngine();
+ EXPECT_TRUE(engine.get() != nullptr);
+}
+
+TEST_F(CompositionEngineTest, canSetHWComposer) {
+ mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(mHwc));
+
+ EXPECT_EQ(mHwc, &mEngine.getHwComposer());
+}
+
+TEST_F(CompositionEngineTest, canSetRenderEngine) {
+ mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+
+ EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
new file mode 100644
index 0000000..9215884
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -0,0 +1,642 @@
+/*
+ * Copyright 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 <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::graphics::common::V1_1 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
+ return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+ << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_1
+
+namespace android::hardware::graphics::common::V1_2 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
+ return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+ << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
+ return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+ << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_2
+
+namespace android::compositionengine {
+namespace {
+
+using testing::_;
+using testing::Contains;
+using testing::IsEmpty;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SizeIs;
+using testing::StrictMock;
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::Hdr;
+using ui::RenderIntent;
+
+// This allows us to simulate a vendor-specified intent being used.
+constexpr RenderIntent VendorRenderIntent = static_cast<RenderIntent>(0x100);
+
+class DisplayColorProfileTest : public testing::Test {
+public:
+ ~DisplayColorProfileTest() override = default;
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+};
+
+class ProfileFactory {
+public:
+ impl::DisplayColorProfile build() const {
+ return impl::DisplayColorProfile{DisplayColorProfileCreationArgs{
+ mHasWideColorGamut,
+ HdrCapabilities(mSupportedHdrTypes, mMaxLuminance, mMaxAverageLuminance,
+ mMinLuminance),
+ mSupportedPerFrameMetadata,
+ mSupportedColorModes,
+ }};
+ }
+
+ ProfileFactory& setHasWideColorGamut(bool value) {
+ mHasWideColorGamut = value;
+ return *this;
+ }
+
+ ProfileFactory& setPerFrameMetadata(int32_t value) {
+ mSupportedPerFrameMetadata = value;
+ return *this;
+ }
+
+ ProfileFactory& addHdrType(Hdr value) {
+ mSupportedHdrTypes.emplace_back(value);
+ return *this;
+ }
+
+ ProfileFactory& addHdrTypes(std::initializer_list<Hdr> values) {
+ for (auto value : values) {
+ mSupportedHdrTypes.emplace_back(value);
+ }
+ return *this;
+ }
+
+ ProfileFactory& setMaxLuminance(float value) {
+ mMaxLuminance = value;
+ return *this;
+ }
+
+ ProfileFactory& setMaxAverageLuminance(float value) {
+ mMaxAverageLuminance = value;
+ return *this;
+ }
+
+ ProfileFactory& setMinLuminance(float value) {
+ mMinLuminance = value;
+ return *this;
+ }
+
+ ProfileFactory& addColorModeRenderIntent(ColorMode colorMode, RenderIntent renderIntent) {
+ mSupportedColorModes[colorMode].emplace_back(renderIntent);
+ return *this;
+ }
+
+ ProfileFactory& addColorModeRenderIntents(ColorMode colorMode,
+ std::initializer_list<RenderIntent> renderIntents) {
+ auto& profileedRenderIntents = mSupportedColorModes[colorMode];
+ for (auto renderIntent : renderIntents) {
+ profileedRenderIntents.emplace_back(renderIntent);
+ }
+ return *this;
+ }
+
+ static impl::DisplayColorProfile createProfileWithNoColorModeSupport() {
+ return ProfileFactory().build();
+ }
+
+ static impl::DisplayColorProfile createProfileWithBT2020ColorModeSupport() {
+ return ProfileFactory()
+ .setHasWideColorGamut(true)
+ .addHdrType(Hdr::HDR10)
+ .addColorModeRenderIntent(ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC)
+ .addColorModeRenderIntent(ColorMode::DISPLAY_BT2020, RenderIntent::ENHANCE)
+ .addColorModeRenderIntent(ColorMode::DISPLAY_BT2020, VendorRenderIntent)
+ .build();
+ }
+
+ static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport() {
+ return ProfileFactory()
+ .setHasWideColorGamut(true)
+ .addHdrType(Hdr::HDR10)
+ .addColorModeRenderIntent(ColorMode::SRGB, RenderIntent::COLORIMETRIC)
+ .addColorModeRenderIntent(ColorMode::SRGB, RenderIntent::ENHANCE)
+ .addColorModeRenderIntent(ColorMode::SRGB, VendorRenderIntent)
+ .build();
+ }
+
+ static impl::DisplayColorProfile createProfileWithBT2100PQSupport() {
+ return ProfileFactory()
+ .setHasWideColorGamut(true)
+ .addHdrType(Hdr::HLG)
+ .addColorModeRenderIntent(ColorMode::BT2100_PQ, VendorRenderIntent)
+ .build();
+ }
+
+ static impl::DisplayColorProfile createProfileWithDisplayP3ColorModeSupport() {
+ return ProfileFactory()
+ .setHasWideColorGamut(true)
+ .addHdrType(Hdr::HLG)
+ .addColorModeRenderIntent(ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC)
+ .addColorModeRenderIntent(ColorMode::DISPLAY_P3, RenderIntent::ENHANCE)
+ .addColorModeRenderIntent(ColorMode::DISPLAY_P3, VendorRenderIntent)
+ .build();
+ }
+
+private:
+ bool mHasWideColorGamut = false;
+ std::vector<Hdr> mSupportedHdrTypes;
+ float mMaxLuminance = -1.f;
+ float mMaxAverageLuminance = -1.f;
+ float mMinLuminance = -1.f;
+ int32_t mSupportedPerFrameMetadata = 0;
+ std::unordered_map<ColorMode, std::vector<RenderIntent>> mSupportedColorModes;
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurface Construction
+ */
+
+TEST_F(DisplayColorProfileTest, ctorSetsHasWideColorGamutFromInputArgs) {
+ {
+ auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+ EXPECT_FALSE(profile.hasWideColorGamut());
+ }
+
+ {
+ auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+ EXPECT_TRUE(profile.hasWideColorGamut());
+ }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSetsSupportedPerFrameMetadataFromInputArgs) {
+ {
+ auto profile = ProfileFactory().setPerFrameMetadata(0).build();
+
+ EXPECT_EQ(0, profile.getSupportedPerFrameMetadata());
+ }
+
+ {
+ impl::DisplayColorProfile profile = ProfileFactory().setPerFrameMetadata(123).build();
+
+ EXPECT_EQ(123, profile.getSupportedPerFrameMetadata());
+ }
+}
+
+TEST_F(DisplayColorProfileTest, ctorDetectsSupportedHdrTypesFromInputArgs) {
+ {
+ // The constructor will set the internal state to not indicate any
+ // profile for HDR modes if none are profileed.
+ auto profile = ProfileFactory().build();
+
+ EXPECT_FALSE(profile.hasHDR10PlusSupport());
+ EXPECT_FALSE(profile.hasHDR10Support());
+ EXPECT_FALSE(profile.hasHLGSupport());
+ EXPECT_FALSE(profile.hasDolbyVisionSupport());
+ }
+
+ {
+ // The constructor will set the intenral state to indicate HDR10Plus
+ // profile if the input arguments indicate it is profileed.
+ auto profile = ProfileFactory().addHdrType(Hdr::HDR10_PLUS).build();
+
+ EXPECT_TRUE(profile.hasHDR10PlusSupport());
+ EXPECT_FALSE(profile.hasHDR10Support());
+ EXPECT_FALSE(profile.hasHLGSupport());
+ EXPECT_FALSE(profile.hasDolbyVisionSupport());
+ }
+
+ {
+ // The constructor will set the intenral state to indicate HDR10 profile
+ // if the input arguments indicate it is profileed.
+ auto profile = ProfileFactory().addHdrType(Hdr::HDR10).build();
+
+ EXPECT_FALSE(profile.hasHDR10PlusSupport());
+ EXPECT_TRUE(profile.hasHDR10Support());
+ EXPECT_FALSE(profile.hasHLGSupport());
+ EXPECT_FALSE(profile.hasDolbyVisionSupport());
+ }
+
+ {
+ // The constructor will set the intenral state to indicate HLG profile
+ // if the input arguments indicate it is profileed.
+ auto profile = ProfileFactory().addHdrType(Hdr::HLG).build();
+
+ EXPECT_FALSE(profile.hasHDR10PlusSupport());
+ EXPECT_FALSE(profile.hasHDR10Support());
+ EXPECT_TRUE(profile.hasHLGSupport());
+ EXPECT_FALSE(profile.hasDolbyVisionSupport());
+ }
+
+ {
+ // The constructor will set the intenral state to indicate Dolbyvision profile
+ // if the input arguments indicate it is profileed.
+ auto profile = ProfileFactory().addHdrType(Hdr::DOLBY_VISION).build();
+
+ EXPECT_FALSE(profile.hasHDR10Support());
+ EXPECT_FALSE(profile.hasHLGSupport());
+ EXPECT_TRUE(profile.hasDolbyVisionSupport());
+ }
+}
+
+TEST_F(DisplayColorProfileTest, ctorUsesOrDefaultsLuminanceValuesFromInputArgs) {
+ {
+ // The constructor will use a default value for each luminance setting
+ // that is negative.
+ auto profile = ProfileFactory()
+ .setMaxLuminance(-1.f)
+ .setMaxAverageLuminance(-1.f)
+ .setMinLuminance(-1.f)
+ .build();
+
+ EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+ profile.getHdrCapabilities().getDesiredMaxLuminance());
+ EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+ profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+ EXPECT_EQ(DisplayColorProfile::sDefaultMinLumiance,
+ profile.getHdrCapabilities().getDesiredMinLuminance());
+ }
+
+ {
+ // The constructor will otherwise take and use a positive value for each
+ // of the luminance settings.
+ auto profile = ProfileFactory()
+ .setMaxLuminance(1001.f)
+ .setMaxAverageLuminance(1002.f)
+ .setMinLuminance(1003.f)
+ .build();
+
+ EXPECT_EQ(1001.f, profile.getHdrCapabilities().getDesiredMaxLuminance());
+ EXPECT_EQ(1002.f, profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+ EXPECT_EQ(1003.f, profile.getHdrCapabilities().getDesiredMinLuminance());
+ }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSignalsHdrSupportForAnyWideColorGamutDevice) {
+ {
+ // If the output does not profile wide color gamut, then no HDR modes
+ // will be profileed in the generated HDR capabilities.
+ auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), IsEmpty());
+ }
+
+ {
+ // If the HWC does not show profile for certain HDR modes, then the
+ // generated HDR capabilities will indicate profile anyway.
+ auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+ }
+
+ {
+ // If the HWC profiles the HDR modes, then the generated capabilities
+ // still has one entry for each HDR type.
+ auto profile = ProfileFactory()
+ .setHasWideColorGamut(true)
+ .addHdrTypes({Hdr::HLG, Hdr::HDR10})
+ .build();
+
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+ EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasRenderIntent
+ */
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasNoSupport) {
+ auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBT2020upport) {
+ auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport) {
+ auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+ EXPECT_TRUE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBTG2100PQSupport) {
+ auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+ EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+ EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+ EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasLegacyHdrSupport
+ */
+
+TEST_F(DisplayColorProfileTest, hasLegacyHdrSupport) {
+ {
+ auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+ EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+ EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+ }
+
+ {
+ auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+ EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+ EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+ }
+
+ {
+ auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+ EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+ EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+ }
+
+ {
+ auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+ EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+ EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getBestColorMode()
+ */
+
+void checkGetBestColorMode(
+ DisplayColorProfile& profile,
+ const std::array<std::tuple<Dataspace, ColorMode, RenderIntent>, 15>& expected) {
+ using ArgsType = std::tuple<Dataspace, RenderIntent>;
+
+ // These are the combinations of dataspaces and render intents that could be
+ // passed to RenderSurface::getBestColorMode()
+ const std::array<std::tuple<Dataspace, RenderIntent>, 15> kArgs = {
+ /* clang-format off */
+
+ // Non-HDR combinations
+
+ /* 0 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 1 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::ENHANCE},
+ /* 2 */ ArgsType{Dataspace::DISPLAY_BT2020, VendorRenderIntent}, // Vendor explicit setting
+
+ /* 3 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 4 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::ENHANCE},
+ /* 5 */ ArgsType{Dataspace::DISPLAY_P3, VendorRenderIntent}, // Vendor explicit setting
+
+ /* 6 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::COLORIMETRIC},
+ /* 7 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::ENHANCE},
+ /* 8 */ ArgsType{Dataspace::V0_SRGB, VendorRenderIntent}, // Vendor explicit setting
+
+ // HDR combinations
+
+ /* 9 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_COLORIMETRIC},
+ /* 10 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_ENHANCE},
+ /* 11 */ ArgsType{Dataspace::BT2020_PQ, VendorRenderIntent}, // Vendor explicit setting
+
+ /* 12 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_COLORIMETRIC},
+ /* 13 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_ENHANCE},
+ /* 14 */ ArgsType{Dataspace::BT2020_HLG, VendorRenderIntent}, // Vendor explicit setting
+ /* clang-format on */
+ };
+
+ for (size_t i = 0; i < kArgs.size(); i++) {
+ std::tuple<Dataspace, ColorMode, RenderIntent> actual;
+ profile.getBestColorMode(std::get<0>(kArgs[i]), std::get<1>(kArgs[i]), &std::get<0>(actual),
+ &std::get<1>(actual), &std::get<2>(actual));
+
+ EXPECT_EQ(expected[i], actual) << " for index " << i;
+ }
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasNoSupport) {
+ auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+ // Note: This table of expected values goes with the table of arguments
+ // used in checkGetBestColorMode.
+ using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+ std::array<Result, 15> expectedResults = {
+ /* clang-format off */
+ /* 0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 10 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* clang-format on */
+ };
+
+ checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2020Support) {
+ auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+ // Note: This table of expected values goes with the table of arguments
+ // used in checkGetBestColorMode.
+ using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+ std::array<Result, 15> expectedResults = {
+ /* clang-format off */
+ /* 0 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 1 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::ENHANCE},
+ /* 2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 3 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 4 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::ENHANCE},
+ /* 5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 6 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 7 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::ENHANCE},
+ /* 8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 9 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 10 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 12 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 13 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+ /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* clang-format on */
+ };
+
+ checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport) {
+ auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+ // Note: This table of expected values goes with the table of arguments
+ // used in checkGetBestColorMode.
+ using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+ std::array<Result, 15> expectedResults = {
+ /* clang-format off */
+ /* 0 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 1 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+ /* 2 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+ /* 3 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 4 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+ /* 5 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+ /* 6 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 7 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+ /* 8 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+ /* 9 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 10 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 12 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 13 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+ /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* clang-format on */
+ };
+
+ checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasDisplayP3Support) {
+ auto profile = ProfileFactory::createProfileWithDisplayP3ColorModeSupport();
+
+ // Note: This table of expected values goes with the table of arguments
+ // used in checkGetBestColorMode.
+ using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+ std::array<Result, 15> expectedResults = {
+ /* clang-format off */
+ /* 0 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 1 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::ENHANCE},
+ // TODO(b/124317977): There is bug here.
+ /* 2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 3 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 4 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::ENHANCE},
+ /* 5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 6 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 7 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::ENHANCE},
+ /* 8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 9 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 10 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 12 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 13 */ Result{Dataspace::DISPLAY_P3, ColorMode::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+ /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* clang-format on */
+ };
+
+ checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2100PQSupport) {
+ auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+ // Note: This table of expected values goes with the table of arguments
+ // used in checkGetBestColorMode.
+ using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+ std::array<Result, 15> expectedResults = {
+ /* clang-format off */
+ /* 0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+ /* 8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+ /* 9 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+ /* 10 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+ /* 11 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, VendorRenderIntent},
+
+ /* 12 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+ /* 13 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+ /* 14 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, VendorRenderIntent},
+ /* clang-format on */
+ };
+
+ checkGetBestColorMode(profile, expectedResults);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
new file mode 100644
index 0000000..cd2d454
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 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 <cmath>
+
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <system/window.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+
+class DisplayTest : public testing::Test {
+public:
+ ~DisplayTest() override = default;
+
+ StrictMock<android::mock::HWComposer> mHwComposer;
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ impl::Display mDisplay{mCompositionEngine,
+ DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(DisplayTest, canInstantiateDisplay) {
+ {
+ constexpr DisplayId display1 = DisplayId{123u};
+ auto display =
+ impl::createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder().setDisplayId(display1).build());
+ EXPECT_FALSE(display->isSecure());
+ EXPECT_FALSE(display->isVirtual());
+ EXPECT_EQ(display1, display->getId());
+ }
+
+ {
+ constexpr DisplayId display2 = DisplayId{546u};
+ auto display = impl::createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder()
+ .setIsSecure(true)
+ .setDisplayId(display2)
+ .build());
+ EXPECT_TRUE(display->isSecure());
+ EXPECT_FALSE(display->isVirtual());
+ EXPECT_EQ(display2, display->getId());
+ }
+
+ {
+ constexpr DisplayId display3 = DisplayId{789u};
+ auto display = impl::createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder()
+ .setIsVirtual(true)
+ .setDisplayId(display3)
+ .build());
+ EXPECT_FALSE(display->isSecure());
+ EXPECT_TRUE(display->isVirtual());
+ EXPECT_EQ(display3, display->getId());
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * Display::disconnect()
+ */
+
+TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ // The first call to disconnect will disconnect the display with the HWC and
+ // set mHwcId to -1.
+ EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+ mDisplay.disconnect();
+ EXPECT_FALSE(mDisplay.getId());
+
+ // Subsequent calls will do nothing,
+ EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+ mDisplay.disconnect();
+ EXPECT_FALSE(mDisplay.getId());
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorTransform()
+ */
+
+TEST_F(DisplayTest, setColorTransformSetsTransform) {
+ // Identity matrix sets an identity state value
+ const mat4 identity;
+
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
+
+ mDisplay.setColorTransform(identity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+
+ // Non-identity matrix sets a non-identity state value
+ const mat4 nonIdentity = mat4() * 2;
+
+ EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+
+ mDisplay.setColorTransform(nonIdentity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorMode()
+ */
+
+TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+ mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+ mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ // These values are expected to be the initial state.
+ ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+ ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+ // Otherwise if the values are unchanged, nothing happens
+ mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+ // Otherwise if the values are different, updates happen
+ EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
+ EXPECT_CALL(mHwComposer,
+ setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC))
+ .Times(1);
+
+ mDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+}
+
+TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+ impl::Display virtualDisplay{mCompositionEngine,
+ DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+
+ virtualDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createDisplayColorProfile()
+ */
+
+TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
+ EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
+ mDisplay.createDisplayColorProfile(
+ DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+ DisplayColorProfileCreationArgs::HwcColorModes()});
+ EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createRenderSurface()
+ */
+
+TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+ EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
+ mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, nullptr, nullptr});
+ EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
new file mode 100644
index 0000000..ac04cb3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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 <compositionengine/impl/HwcBufferCache.h>
+#include <gtest/gtest.h>
+#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::compositionengine {
+namespace {
+
+class TestableHwcBufferCache : public impl::HwcBufferCache {
+public:
+ void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer) {
+ HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
+ }
+ bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
+ return HwcBufferCache::getSlot(buffer, outSlot);
+ }
+ uint32_t getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); }
+};
+
+class HwcBufferCacheTest : public testing::Test {
+public:
+ ~HwcBufferCacheTest() override = default;
+
+ TestableHwcBufferCache mCache;
+ sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+};
+
+TEST_F(HwcBufferCacheTest, testSlot) {
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // The first time, the output is the same as the input
+ mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(0, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ // The second time with the same buffer, the outBuffer is nullptr.
+ mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(0, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // With a new buffer, the outBuffer is the input.
+ mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(1, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
+
+ // Again, the second request with the same buffer sets outBuffer to nullptr.
+ mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(1, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // Setting a slot to use nullptr lookslike works, but note that
+ // the output values make it look like no new buffer is being set....
+ mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ EXPECT_EQ(2, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+}
+
+TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
+ int slot;
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // fill up cache
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
+ EXPECT_EQ(buf, outBuffer);
+ EXPECT_EQ(i, outSlot);
+ }
+
+ slot = mCache.getLeastRecentlyUsedSlot();
+ EXPECT_EQ(0, slot);
+
+ mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(0, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ slot = mCache.getLeastRecentlyUsedSlot();
+ EXPECT_EQ(1, slot);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
new file mode 100644
index 0000000..26115a3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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 <gtest/gtest.h>
+
+#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/impl/Layer.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/LayerFE.h>
+
+namespace android::compositionengine {
+namespace {
+
+using testing::StrictMock;
+
+class LayerTest : public testing::Test {
+public:
+ ~LayerTest() override = default;
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>();
+ impl::Layer mLayer{mCompositionEngine, LayerCreationArgs{mLayerFE}};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(LayerTest, canInstantiateLayer) {}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
index e6ac6bf..8c10341 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 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
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "MockHWC2.h"
-namespace android {
+namespace HWC2 {
+
+// This will go away once HWC2::Layer is moved into the "backend" library
+Layer::~Layer() = default;
+
namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Layer::Layer() = default;
+Layer::~Layer() = default;
} // namespace mock
-} // namespace android
+} // namespace HWC2
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
new file mode 100644
index 0000000..7fd6541
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+#include <ui/Fence.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+#include "DisplayHardware/HWC2.h"
+
+namespace HWC2 {
+namespace mock {
+
+class Layer : public HWC2::Layer {
+public:
+ Layer();
+ ~Layer() override;
+
+ MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+
+ MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t));
+ MOCK_METHOD3(setBuffer,
+ Error(uint32_t, const android::sp<android::GraphicBuffer>&,
+ const android::sp<android::Fence>&));
+ MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
+ MOCK_METHOD1(setBlendMode, Error(BlendMode));
+ MOCK_METHOD1(setColor, Error(hwc_color_t));
+ MOCK_METHOD1(setCompositionType, Error(Composition));
+ MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
+ MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
+ MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
+ MOCK_METHOD1(setPlaneAlpha, Error(float));
+ MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*));
+ MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&));
+ MOCK_METHOD1(setTransform, Error(Transform));
+ MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
+ MOCK_METHOD1(setZOrder, Error(uint32_t));
+ MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
+
+ MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
+};
+
+} // namespace mock
+} // namespace HWC2
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
index e6ac6bf..ae52670 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2018 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
@@ -14,14 +14,19 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "MockHWComposer.h"
namespace android {
+
+// This will go away once HWComposer is moved into the "backend" library
+HWComposer::~HWComposer() = default;
+
namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+HWComposer::HWComposer() = default;
+HWComposer::~HWComposer() = default;
} // namespace mock
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
new file mode 100644
index 0000000..94349de
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 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 <compositionengine/Output.h>
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android {
+namespace mock {
+
+class HWComposer : public android::HWComposer {
+public:
+ HWComposer();
+ ~HWComposer() override;
+
+ MOCK_METHOD2(registerCallback, void(HWC2::ComposerCallback*, int32_t));
+ MOCK_CONST_METHOD3(getDisplayIdentificationData,
+ bool(hwc2_display_t, uint8_t*, DisplayIdentificationData*));
+ MOCK_CONST_METHOD1(hasCapability, bool(HWC2::Capability));
+ MOCK_CONST_METHOD2(hasDisplayCapability,
+ bool(const std::optional<DisplayId>&, HWC2::DisplayCapability));
+
+ MOCK_METHOD3(allocateVirtualDisplay,
+ std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
+ MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
+ MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
+ MOCK_METHOD2(prepare, status_t(DisplayId, const compositionengine::Output&));
+ MOCK_METHOD5(setClientTarget,
+ status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
+ ui::Dataspace));
+ MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
+ MOCK_METHOD2(setPowerMode, status_t(DisplayId, int));
+ MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
+ MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
+ MOCK_METHOD1(disconnectDisplay, void(DisplayId));
+ MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
+ MOCK_CONST_METHOD1(hasFlipClientTargetRequest, bool(const std::optional<DisplayId>&));
+ MOCK_CONST_METHOD1(hasClientComposition, bool(const std::optional<DisplayId>&));
+ MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
+ MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
+ MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
+ MOCK_METHOD1(clearReleaseFences, void(DisplayId));
+ MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*));
+ MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId));
+ MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode));
+ MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace));
+ MOCK_METHOD4(getDisplayedContentSamplingAttributes,
+ status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
+ MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
+ MOCK_METHOD4(getDisplayedContentSample,
+ status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
+ MOCK_METHOD2(setDisplayBrightness, status_t(DisplayId, float));
+ MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*));
+
+ MOCK_METHOD2(onHotplug,
+ std::optional<DisplayIdentificationInfo>(hwc2_display_t, HWC2::Connection));
+ MOCK_METHOD2(onVsync, bool(hwc2_display_t, int64_t));
+ MOCK_METHOD2(setVsyncEnabled, void(DisplayId, HWC2::Vsync));
+ MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
+ MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
+ MOCK_CONST_METHOD1(getConfigs,
+ std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId));
+ MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId));
+ MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId));
+ MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
+ MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
+ MOCK_CONST_METHOD0(isUsingVrComposer, bool());
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
+ MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hwc2_display_t>(int32_t));
+ MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hwc2_display_t>());
+ MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hwc2_display_t>());
+ MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hwc2_display_t));
+ MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hwc2_display_t>(DisplayId));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
new file mode 100644
index 0000000..2060c5a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright 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 <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/Output.h>
+#include <gtest/gtest.h>
+
+#include "MockHWC2.h"
+#include "MockHWComposer.h"
+#include "RectMatcher.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::_;
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+
+constexpr auto TR_IDENT = 0u;
+constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H;
+constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V;
+constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
+constexpr auto TR_ROT_180 = TR_FLP_H | TR_FLP_V;
+constexpr auto TR_ROT_270 = TR_ROT_90 | TR_ROT_180;
+
+const std::string kOutputName{"Test Output"};
+
+class OutputLayerTest : public testing::Test {
+public:
+ OutputLayerTest() {
+ EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
+ EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
+
+ EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+ EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+ }
+
+ ~OutputLayerTest() override = default;
+
+ compositionengine::mock::Output mOutput;
+ std::shared_ptr<compositionengine::mock::Layer> mLayer{
+ new StrictMock<compositionengine::mock::Layer>()};
+ sp<compositionengine::mock::LayerFE> mLayerFE{
+ new StrictMock<compositionengine::mock::LayerFE>()};
+ impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+
+ impl::LayerCompositionState mLayerState;
+ impl::OutputCompositionState mOutputState;
+};
+
+/*
+ * Basic construction
+ */
+
+TEST_F(OutputLayerTest, canInstantiateOutputLayer) {}
+
+/*
+ * OutputLayer::initialize()
+ */
+
+TEST_F(OutputLayerTest, initializingOutputLayerWithoutHwcDoesNothingInteresting) {
+ StrictMock<compositionengine::mock::CompositionEngine> compositionEngine;
+
+ mOutputLayer.initialize(compositionEngine, std::nullopt);
+
+ EXPECT_FALSE(mOutputLayer.getState().hwc);
+}
+
+TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) {
+ StrictMock<compositionengine::mock::CompositionEngine> compositionEngine;
+ StrictMock<android::mock::HWComposer> hwc;
+ StrictMock<HWC2::mock::Layer> hwcLayer;
+
+ EXPECT_CALL(compositionEngine, getHwComposer()).WillOnce(ReturnRef(hwc));
+ EXPECT_CALL(hwc, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+
+ mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID);
+
+ const auto& outputLayerState = mOutputLayer.getState();
+ ASSERT_TRUE(outputLayerState.hwc);
+
+ const auto& hwcState = *outputLayerState.hwc;
+ EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get());
+
+ EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+ mOutputLayer.editState().hwc.reset();
+}
+
+/*
+ * OutputLayer::calculateOutputDisplayFrame()
+ */
+
+struct OutputLayerDisplayFrameTest : public OutputLayerTest {
+ OutputLayerDisplayFrameTest() {
+ // Set reasonable default values for a simple case. Each test will
+ // set one specific value to something different.
+
+ mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
+ mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
+ mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
+ mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+ mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080};
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+
+ mOutputState.viewport = Rect{0, 0, 1920, 1080};
+ mOutputState.transform = ui::Transform{TR_IDENT};
+ }
+
+ Rect calculateOutputDisplayFrame() {
+ mLayerState.frontEnd.geomInverseLayerTransform =
+ mLayerState.frontEnd.geomLayerTransform.inverse();
+
+ return mOutputLayer.calculateOutputDisplayFrame();
+ }
+};
+
+TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) {
+ const Rect expected{0, 0, 1920, 1080};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
+ mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}};
+ const Rect expected{0, 0, 0, 0};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
+ mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+ const Rect expected{100, 200, 300, 500};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
+ mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+ mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+ const Rect expected{1420, 100, 1720, 300};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
+ mLayerState.frontEnd.geomCrop = Rect{};
+ const Rect expected{0, 0, 1920, 1080};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, geomLayerSnapToBoundsAffectsFrame) {
+ mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
+ const Rect expected{0, 0, 960, 540};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
+ mOutputState.viewport = Rect{0, 0, 960, 540};
+ const Rect expected{0, 0, 960, 540};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) {
+ mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90};
+ const Rect expected{-1080, 0, 0, 1920};
+ EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+}
+
+/*
+ * OutputLayer::calculateOutputRelativeBufferTransform()
+ */
+
+TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) {
+ mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+
+ struct Entry {
+ uint32_t layer;
+ uint32_t buffer;
+ uint32_t display;
+ uint32_t expected;
+ };
+ // Not an exhaustive list of cases, but hopefully enough.
+ const std::array<Entry, 24> 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_ROT_90},
+ /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_ROT_180},
+ /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_ROT_270},
+
+ /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_IDENT},
+ /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90},
+ /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180},
+ /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_270},
+
+ /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V},
+ /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_180},
+ /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_IDENT},
+ /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_180},
+
+ /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90},
+ /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_180},
+ /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_270},
+ /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_IDENT},
+
+ /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180},
+ /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_270},
+ /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_IDENT},
+ /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_90},
+
+ /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270},
+ /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_IDENT},
+ /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_90},
+ /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180},
+ // clang-format on
+ };
+
+ for (size_t i = 0; i < testData.size(); i++) {
+ const auto& entry = testData[i];
+
+ mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080);
+ mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+ mOutputState.orientation = entry.display;
+
+ auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+ EXPECT_EQ(entry.expected, actual) << "entry " << i;
+ }
+}
+
+/*
+ * OutputLayer::writeStateToHWC()
+ */
+
+struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
+ static constexpr HWC2::Error kError = HWC2::Error::Unsupported;
+ static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
+ static constexpr uint32_t kZOrder = 21u;
+ static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
+ static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
+ static_cast<Hwc2::IComposerClient::BlendMode>(41);
+ static constexpr float kAlpha = 51.f;
+ static constexpr uint32_t kType = 61u;
+ static constexpr uint32_t kAppId = 62u;
+
+ static const Rect kDisplayFrame;
+
+ OutputLayerWriteStateToHWCTest() {
+ auto& outputLayerState = mOutputLayer.editState();
+ outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
+
+ outputLayerState.displayFrame = kDisplayFrame;
+ outputLayerState.sourceCrop = kSourceCrop;
+ outputLayerState.z = kZOrder;
+ outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
+
+ mLayerState.frontEnd.blendMode = kBlendMode;
+ mLayerState.frontEnd.alpha = kAlpha;
+ mLayerState.frontEnd.type = kType;
+ mLayerState.frontEnd.appId = kAppId;
+ }
+
+ void expectGeometryCommonCalls() {
+ EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setTransform(static_cast<HWC2::Transform>(kBufferTransform)))
+ .WillOnce(Return(kError));
+
+ EXPECT_CALL(*mHwcLayer, setBlendMode(static_cast<HWC2::BlendMode>(kBlendMode)))
+ .WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
+ }
+
+ std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+};
+
+const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
+ mOutputLayer.editState().hwc.reset();
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
+ mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) {
+ expectGeometryCommonCalls();
+
+ mOutputLayer.writeStateToHWC(true);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
new file mode 100644
index 0000000..a84af3a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright 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 <cmath>
+
+#include <compositionengine/impl/Output.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include "RegionMatcher.h"
+#include "TransformMatcher.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+class OutputTest : public testing::Test {
+public:
+ OutputTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ mOutput.editState().bounds = kDefaultDisplaySize;
+ }
+ ~OutputTest() override = default;
+
+ static const Rect kDefaultDisplaySize;
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ impl::Output mOutput{mCompositionEngine};
+};
+
+const Rect OutputTest::kDefaultDisplaySize{100, 200};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(OutputTest, canInstantiateOutput) {
+ // The validation check checks each required component.
+ EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
+
+ EXPECT_TRUE(mOutput.isValid());
+
+ // If we take away the required components, it is no longer valid.
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+
+ EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+
+ EXPECT_FALSE(mOutput.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setCompositionEnabled()
+ */
+
+TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
+ mOutput.editState().isEnabled = true;
+
+ mOutput.setCompositionEnabled(true);
+
+ EXPECT_TRUE(mOutput.getState().isEnabled);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
+ mOutput.editState().isEnabled = false;
+
+ mOutput.setCompositionEnabled(true);
+
+ EXPECT_TRUE(mOutput.getState().isEnabled);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
+ mOutput.editState().isEnabled = true;
+
+ mOutput.setCompositionEnabled(false);
+
+ EXPECT_FALSE(mOutput.getState().isEnabled);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setProjection()
+ */
+
+TEST_F(OutputTest, setProjectionTriviallyWorks) {
+ const ui::Transform transform{ui::Transform::ROT_180};
+ const int32_t orientation = 123;
+ const Rect frame{1, 2, 3, 4};
+ const Rect viewport{5, 6, 7, 8};
+ const Rect scissor{9, 10, 11, 12};
+ const bool needsFiltering = true;
+
+ mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+
+ EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
+ EXPECT_EQ(orientation, mOutput.getState().orientation);
+ EXPECT_EQ(frame, mOutput.getState().frame);
+ EXPECT_EQ(viewport, mOutput.getState().viewport);
+ EXPECT_EQ(scissor, mOutput.getState().scissor);
+ EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setBounds()
+ */
+
+TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
+ const ui::Size displaySize{200, 400};
+
+ EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
+ EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+
+ mOutput.setBounds(displaySize);
+
+ EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setLayerStackFilter()
+ */
+
+TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
+ const uint32_t layerStack = 123u;
+ mOutput.setLayerStackFilter(layerStack, true);
+
+ EXPECT_TRUE(mOutput.getState().layerStackInternal);
+ EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
+
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorTransform
+ */
+
+TEST_F(OutputTest, setColorTransformSetsTransform) {
+ // Identity matrix sets an identity state value
+ const mat4 identity;
+
+ mOutput.setColorTransform(identity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
+
+ // Since identity is the default, the dirty region should be unchanged (empty)
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+
+ // Non-identity matrix sets a non-identity state value
+ const mat4 nonIdentity = mat4() * 2;
+
+ mOutput.setColorTransform(nonIdentity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+
+ // Since this is a state change, the entire output should now be dirty.
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorMode
+ */
+
+TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+ EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
+
+ mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
+ mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3;
+ mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3;
+ mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+
+ mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setRenderSurface()
+ */
+
+TEST_F(OutputTest, setRenderSurfaceResetsBounds) {
+ const ui::Size newDisplaySize{640, 480};
+
+ mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+ EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
+
+ mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+
+ EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getDirtyRegion()
+ */
+
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+ const Rect viewport{100, 200};
+ mOutput.editState().viewport = viewport;
+ mOutput.editState().dirtyRegion.set(50, 300);
+
+ {
+ Region result = mOutput.getDirtyRegion(true);
+
+ EXPECT_THAT(result, RegionEq(Region(viewport)));
+ }
+}
+
+TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
+ const Rect viewport{100, 200};
+ mOutput.editState().viewport = viewport;
+ mOutput.editState().dirtyRegion.set(50, 300);
+
+ {
+ Region result = mOutput.getDirtyRegion(false);
+
+ // The dirtyRegion should be clipped to the display bounds.
+ EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * Output::belongsInOutput()
+ */
+
+TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
+ const uint32_t layerStack1 = 123u;
+ const uint32_t layerStack2 = 456u;
+
+ // If the output accepts layerStack1 and internal-only layers....
+ mOutput.setLayerStackFilter(layerStack1, true);
+
+ // Any layer with layerStack1 belongs to it, internal-only or not.
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+
+ // If the output accepts layerStack21 but not internal-only layers...
+ mOutput.setLayerStackFilter(layerStack1, false);
+
+ // Only non-internal layers with layerStack1 belong to it.
+ EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+ EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getOutputLayerForLayer()
+ */
+
+TEST_F(OutputTest, getOutputLayerForLayerWorks) {
+ mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>();
+ mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>();
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer1));
+ outputLayers.emplace_back(nullptr);
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer2));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ StrictMock<mock::Layer> layer;
+ StrictMock<mock::Layer> otherLayer;
+
+ // If the input layer matches the first OutputLayer, it will be returned.
+ EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer));
+ EXPECT_EQ(outputLayer1, mOutput.getOutputLayerForLayer(&layer));
+
+ // If the input layer matches the second OutputLayer, it will be returned.
+ EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
+ EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer));
+ EXPECT_EQ(outputLayer2, mOutput.getOutputLayerForLayer(&layer));
+
+ // If the input layer does not match an output layer, null will be returned.
+ EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
+ EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer));
+ EXPECT_EQ(nullptr, mOutput.getOutputLayerForLayer(&layer));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getOrCreateOutputLayer()
+ */
+
+TEST_F(OutputTest, getOrCreateOutputLayerWorks) {
+ mock::OutputLayer* existingOutputLayer = new StrictMock<mock::OutputLayer>();
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(nullptr);
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(existingOutputLayer));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ std::shared_ptr<mock::Layer> layer{new StrictMock<mock::Layer>()};
+ sp<LayerFE> layerFE{new StrictMock<mock::LayerFE>()};
+
+ StrictMock<mock::Layer> otherLayer;
+
+ {
+ // If there is no OutputLayer corresponding to the input layer, a
+ // new OutputLayer is constructed and returned.
+ EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer));
+ auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE);
+ EXPECT_NE(existingOutputLayer, result.get());
+ EXPECT_TRUE(result.get() != nullptr);
+ EXPECT_EQ(layer.get(), &result->getLayer());
+ EXPECT_EQ(layerFE.get(), &result->getLayerFE());
+
+ // The entries in the ordered array should be unchanged.
+ auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
+ EXPECT_EQ(nullptr, outputLayers[0].get());
+ EXPECT_EQ(existingOutputLayer, outputLayers[1].get());
+ }
+
+ {
+ // If there is an existing OutputLayer for the requested layer, an owned
+ // pointer is returned
+ EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer));
+ auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE);
+ EXPECT_EQ(existingOutputLayer, result.get());
+
+ // The corresponding entry in the ordered array should be cleared.
+ auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
+ EXPECT_EQ(nullptr, outputLayers[0].get());
+ EXPECT_EQ(nullptr, outputLayers[1].get());
+ }
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
new file mode 100644
index 0000000..d4c76bc
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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 <string>
+
+#include <android-base/stringprintf.h>
+#include <gmock/gmock.h>
+
+namespace {
+
+using android::base::StringAppendF;
+using Rect = android::Rect;
+
+void dumpRect(const Rect& rect, std::string& result, const char* name) {
+ StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+// Checks for a region match
+MATCHER_P(RectEq, expected, "") {
+ std::string buf;
+ buf.append("Rects are not equal\n");
+ dumpRect(expected, buf, "expected rect");
+ dumpRect(arg, buf, "actual rect");
+ *result_listener << buf;
+
+ return (expected.left == arg.left) && (expected.top == arg.top) &&
+ (expected.right == arg.right) && (expected.bottom == arg.bottom);
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
new file mode 100644
index 0000000..5a4efa9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+
+namespace {
+
+// Checks for a region match
+MATCHER_P(RegionEq, expected, "") {
+ std::string buf;
+ buf.append("Regions are not equal\n");
+ expected.dump(buf, "expected region");
+ arg.dump(buf, "actual region");
+ *result_listener << buf;
+
+ size_t expectedRectCount = 0;
+ android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
+ size_t actualRectCount = 0;
+ android::Rect const* actualRects = arg.getArray(&actualRectCount);
+
+ if (expectedRectCount != actualRectCount) return false;
+ for (size_t i = 0; i < expectedRectCount; i++) {
+ if (expectedRects[i] != actualRects[i]) return false;
+ }
+ return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
new file mode 100644
index 0000000..84af9b9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright 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 <cstdarg>
+#include <cstdint>
+
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+/* ------------------------------------------------------------------------
+ * MockNativeWindow
+ *
+ * An intentionally simplified Mock which implements a minimal subset of the full
+ * ANativeWindow interface.
+ */
+
+class MockNativeWindow : public ANativeObjectBase<ANativeWindow, MockNativeWindow, RefBase> {
+public:
+ MockNativeWindow() {
+ ANativeWindow::setSwapInterval = &forwardSetSwapInterval;
+ ANativeWindow::dequeueBuffer = &forwardDequeueBuffer;
+ ANativeWindow::cancelBuffer = &forwardCancelBuffer;
+ ANativeWindow::queueBuffer = &forwardQueueBuffer;
+ ANativeWindow::query = &forwardQuery;
+ ANativeWindow::perform = &forwardPerform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = &forwardDequeueBufferDeprecated;
+ ANativeWindow::cancelBuffer_DEPRECATED = &forwardCancelBufferDeprecated;
+ ANativeWindow::lockBuffer_DEPRECATED = &forwardLockBufferDeprecated;
+ ANativeWindow::queueBuffer_DEPRECATED = &forwardQueueBufferDeprecated;
+ }
+
+ MOCK_METHOD1(setSwapInterval, int(int));
+ MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
+ MOCK_METHOD2(cancelBuffer, int(struct ANativeWindowBuffer*, int));
+ MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
+ MOCK_CONST_METHOD2(query, int(int, int*));
+ MOCK_METHOD1(connect, int(int));
+ MOCK_METHOD1(lockBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+ MOCK_METHOD1(setBuffersFormat, int(PixelFormat));
+ MOCK_METHOD1(setBuffersDataSpace, int(ui::Dataspace));
+ MOCK_METHOD1(setUsage, int(uint64_t));
+
+ static void unexpectedCall(...) { LOG_ALWAYS_FATAL("Unexpected ANativeWindow API call"); }
+
+ static int forwardSetSwapInterval(ANativeWindow* window, int interval) {
+ return getSelf(window)->setSwapInterval(interval);
+ }
+
+ static int forwardDequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd) {
+ return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+ }
+
+ static int forwardCancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd) {
+ return getSelf(window)->cancelBuffer(buffer, fenceFd);
+ }
+
+ static int forwardQueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return getSelf(window)->queueBuffer(buffer, fenceFd);
+ }
+
+ static int forwardQuery(const ANativeWindow* window, int what, int* value) {
+ return getSelf(window)->query(what, value);
+ }
+
+ static int forwardPerform(ANativeWindow* window, int operation, ...) {
+ va_list args;
+ va_start(args, operation);
+ int result = NO_ERROR;
+ switch (operation) {
+ case NATIVE_WINDOW_API_CONNECT: {
+ int api = va_arg(args, int);
+ result = getSelf(window)->connect(api);
+ break;
+ }
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+ PixelFormat format = va_arg(args, PixelFormat);
+ result = getSelf(window)->setBuffersFormat(format);
+ break;
+ }
+ case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+ ui::Dataspace dataspace = static_cast<ui::Dataspace>(va_arg(args, int));
+ result = getSelf(window)->setBuffersDataSpace(dataspace);
+ break;
+ }
+ case NATIVE_WINDOW_SET_USAGE: {
+ // Note: Intentionally widens usage from 32 to 64 bits so we
+ // just have one implementation.
+ uint64_t usage = va_arg(args, uint32_t);
+ result = getSelf(window)->setUsage(usage);
+ break;
+ }
+ case NATIVE_WINDOW_SET_USAGE64: {
+ uint64_t usage = va_arg(args, uint64_t);
+ result = getSelf(window)->setUsage(usage);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unexpected operation %d", operation);
+ break;
+ }
+
+ va_end(args);
+ return result;
+ }
+
+ static int forwardDequeueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer** buffer) {
+ int ignoredFenceFd = -1;
+ return getSelf(window)->dequeueBuffer(buffer, &ignoredFenceFd);
+ }
+
+ static int forwardCancelBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+ return getSelf(window)->cancelBuffer(buffer, -1);
+ }
+
+ static int forwardLockBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+ return getSelf(window)->lockBuffer_DEPRECATED(buffer);
+ }
+
+ static int forwardQueueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+ return getSelf(window)->queueBuffer(buffer, -1);
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurfaceTest
+ */
+
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
+
+using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+class RenderSurfaceTest : public testing::Test {
+public:
+ RenderSurfaceTest() {
+ EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+ EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
+ EXPECT_CALL(mCompositionEngine, getHwComposer).WillRepeatedly(ReturnRef(mHwComposer));
+ EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
+ }
+ ~RenderSurfaceTest() override = default;
+
+ StrictMock<android::mock::HWComposer> mHwComposer;
+ StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ StrictMock<mock::Display> mDisplay;
+ sp<MockNativeWindow> mNativeWindow = new StrictMock<MockNativeWindow>();
+ sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
+ impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
+ RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+ mDisplaySurface}};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(RenderSurfaceTest, canInstantiate) {
+ EXPECT_TRUE(mSurface.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::initialize()
+ */
+
+TEST_F(RenderSurfaceTest, initializeConfiguresNativeWindow) {
+ EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+ mSurface.initialize();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getSize()
+ */
+
+TEST_F(RenderSurfaceTest, sizeReturnsConstructedSize) {
+ const ui::Size expected{DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT};
+
+ EXPECT_EQ(expected, mSurface.getSize());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getClientTargetAcquireFence()
+ */
+
+TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) {
+ sp<Fence> fence = new Fence();
+
+ EXPECT_CALL(*mDisplaySurface, getClientTargetAcquireFence()).WillOnce(ReturnRef(fence));
+
+ EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setDisplaySize()
+ */
+
+TEST_F(RenderSurfaceTest, setDisplaySizeAppliesChange) {
+ EXPECT_CALL(*mDisplaySurface, resizeBuffers(640, 480)).Times(1);
+
+ mSurface.setDisplaySize(ui::Size(640, 480));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setBufferDataspace()
+ */
+
+TEST_F(RenderSurfaceTest, setBufferDataspaceAppliesChange) {
+ EXPECT_CALL(*mNativeWindow, setBuffersDataSpace(ui::Dataspace::DISPLAY_P3))
+ .WillOnce(Return(NO_ERROR));
+
+ mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setProtected()
+ */
+
+TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ .WillOnce(Return(NO_ERROR));
+
+ mSurface.setProtected(true);
+}
+
+TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+ mSurface.setProtected(false);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::beginFrame()
+ */
+
+TEST_F(RenderSurfaceTest, beginFrameAppliesChange) {
+ EXPECT_CALL(*mDisplaySurface, beginFrame(true)).WillOnce(Return(NO_ERROR));
+
+ EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::prepareFrame()
+ */
+
+TEST_F(RenderSurfaceTest, prepareFramePassesOutputLayersToHwc) {
+ EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
+ .WillOnce(Return(INVALID_OPERATION));
+
+ EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) {
+ EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
+ .WillOnce(Return(INVALID_OPERATION));
+
+ EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
+ EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+ EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+ .WillOnce(Return(INVALID_OPERATION));
+
+ EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
+ EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+ EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+ .WillOnce(Return(NO_ERROR));
+
+ EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
+ EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+ EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+ .WillOnce(Return(NO_ERROR));
+
+ EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
+ EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+ EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+ .WillOnce(Return(NO_ERROR));
+
+ EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::dequeueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+ EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+
+ base::unique_fd fence;
+ EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence).get());
+
+ EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::queueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mSurface.mutableGraphicBufferForTest() = buffer;
+
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+ EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID))
+ .WillOnce(Return(false));
+ EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+ mSurface.queueBuffer(base::unique_fd());
+
+ EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mSurface.mutableGraphicBufferForTest() = buffer;
+
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+ mSurface.queueBuffer(base::unique_fd());
+
+ EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mSurface.mutableGraphicBufferForTest() = buffer;
+
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+ EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+ mSurface.queueBuffer(base::unique_fd());
+
+ EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+ EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+ EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+ mSurface.queueBuffer(base::unique_fd());
+
+ EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ mSurface.mutableGraphicBufferForTest() = buffer;
+
+ EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+ EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+ .WillOnce(Return(INVALID_OPERATION));
+ EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
+ EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+ mSurface.queueBuffer(base::unique_fd());
+
+ EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::onPresentDisplayCompleted()
+ */
+
+TEST_F(RenderSurfaceTest, onPresentDisplayCompletedForwardsSignal) {
+ EXPECT_CALL(*mDisplaySurface, onFrameCommitted()).Times(1);
+
+ mSurface.onPresentDisplayCompleted();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setViewportAndProjection()
+ */
+
+TEST_F(RenderSurfaceTest, setViewportAndProjectionAppliesChang) {
+ mSurface.setSizeForTest(ui::Size(100, 200));
+
+ EXPECT_CALL(mRenderEngine,
+ setViewportAndProjection(100, 200, Rect(100, 200), ui::Transform::ROT_0))
+ .Times(1);
+
+ mSurface.setViewportAndProjection();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::flip()
+ */
+
+TEST_F(RenderSurfaceTest, flipForwardsSignal) {
+ mSurface.setPageFlipCountForTest(500);
+
+ mSurface.flip();
+
+ EXPECT_EQ(501, mSurface.getPageFlipCount());
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
new file mode 100644
index 0000000..ea07bed
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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 <string>
+
+#include <gmock/gmock.h>
+
+namespace {
+
+// Check for a transform match
+MATCHER_P(TransformEq, expected, "") {
+ std::string buf;
+ buf.append("Transforms are not equal\n");
+ expected.dump(buf, "expected transform");
+ arg.dump(buf, "actual transform");
+ *result_listener << buf;
+
+ const float TOLERANCE = 1e-3f;
+
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index f259d93..7927fa9 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -22,18 +22,24 @@
namespace android {
-ContainerLayer::ContainerLayer(SurfaceFlinger* flinger, const sp<Client>& client,
- const String8& name, uint32_t w, uint32_t h, uint32_t flags)
- : Layer(flinger, client, name, w, h, flags) {
- mDrawingState = mCurrentState;
+ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {}
+
+ContainerLayer::~ContainerLayer() = default;
+
+bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&, const bool,
+ renderengine::LayerSettings&) {
+ return false;
}
-void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) const {}
-
bool ContainerLayer::isVisible() const {
+ return false;
+}
+
+bool ContainerLayer::canReceiveInput() const {
return !isHiddenByPolicy();
}
-void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&) {}
+void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
+ const Rect&, int32_t, const ui::Dataspace) {}
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index b352b96..7222a3e 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -25,18 +25,27 @@
class ContainerLayer : public Layer {
public:
- ContainerLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
- uint32_t w, uint32_t h, uint32_t flags);
- virtual ~ContainerLayer() = default;
+ explicit ContainerLayer(const LayerCreationArgs&);
+ ~ContainerLayer() override;
const char* getTypeId() const override { return "ContainerLayer"; }
- void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) const override;
bool isVisible() const override;
- void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
+ bool canReceiveInput() const override;
+
+ void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
+ const Rect& viewport, int32_t supportedPerFrameMetadata,
+ const ui::Dataspace targetDataspace) override;
bool isCreatedFromMainThread() const override { return true; }
+
+ bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+
+protected:
+ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer) override;
};
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 65c3839..4a13bfb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -18,49 +18,28 @@
#undef LOG_TAG
#define LOG_TAG "DisplayDevice"
-#include <array>
-#include <unordered_set>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
-#include <cutils/properties.h>
-
-#include <utils/RefBase.h>
-#include <utils/Log.h>
-
-#include <ui/DebugUtils.h>
-#include <ui/DisplayInfo.h>
-#include <ui/PixelFormat.h>
-
-#include <gui/Surface.h>
-
-#include <hardware/gralloc.h>
-
-#include "DisplayHardware/DisplaySurface.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWC2.h"
-#include "RenderEngine/RenderEngine.h"
-
-#include "clz.h"
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-#include "Layer.h"
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <configstore/Utils.h>
+#include <log/log.h>
+#include <system/window.h>
+#include <ui/GraphicTypes.h>
+
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "SurfaceFlinger.h"
namespace android {
-// retrieve triple buffer setting from configstore
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using android::ui::ColorMode;
-using android::ui::Dataspace;
-using android::ui::Hdr;
-using android::ui::RenderIntent;
+using android::base::StringAppendF;
/*
* Initialize the display to the specified values.
@@ -69,335 +48,72 @@
uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
-namespace {
+DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
+ const wp<IBinder>& displayToken,
+ const std::optional<DisplayId>& displayId)
+ : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
-// ordered list of known SDR color modes
-const std::array<ColorMode, 2> sSdrColorModes = {
- ColorMode::DISPLAY_P3,
- ColorMode::SRGB,
-};
+DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
+ : mFlinger(args.flinger),
+ mDisplayToken(args.displayToken),
+ mSequenceId(args.sequenceId),
+ mDisplayInstallOrientation(args.displayInstallOrientation),
+ mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
+ compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
+ args.displayId})},
+ mIsVirtual(args.isVirtual),
+ mOrientation(),
+ mActiveConfig(0),
+ mIsPrimary(args.isPrimary) {
+ mCompositionDisplay->createRenderSurface(
+ compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
+ args.nativeWindow.get()),
+ ANativeWindow_getHeight(
+ args.nativeWindow.get()),
+ args.nativeWindow, args.displaySurface});
-// ordered list of known HDR color modes
-const std::array<ColorMode, 2> sHdrColorModes = {
- ColorMode::BT2100_PQ,
- ColorMode::BT2100_HLG,
-};
+ mCompositionDisplay->createDisplayColorProfile(
+ compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut,
+ std::move(args.hdrCapabilities),
+ args.supportedPerFrameMetadata,
+ args.hwcColorModes});
-// ordered list of known SDR render intents
-const std::array<RenderIntent, 2> sSdrRenderIntents = {
- RenderIntent::ENHANCE,
- RenderIntent::COLORIMETRIC,
-};
-
-// ordered list of known HDR render intents
-const std::array<RenderIntent, 2> sHdrRenderIntents = {
- RenderIntent::TONE_MAP_ENHANCE,
- RenderIntent::TONE_MAP_COLORIMETRIC,
-};
-
-// map known color mode to dataspace
-Dataspace colorModeToDataspace(ColorMode mode) {
- switch (mode) {
- case ColorMode::SRGB:
- return Dataspace::SRGB;
- case ColorMode::DISPLAY_P3:
- return Dataspace::DISPLAY_P3;
- case ColorMode::BT2100_HLG:
- return Dataspace::BT2020_HLG;
- case ColorMode::BT2100_PQ:
- return Dataspace::BT2020_PQ;
- default:
- return Dataspace::UNKNOWN;
- }
-}
-
-// Return a list of candidate color modes.
-std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
- std::vector<ColorMode> candidates;
-
- // add mode itself
- candidates.push_back(mode);
-
- // check if mode is HDR
- bool isHdr = false;
- for (auto hdrMode : sHdrColorModes) {
- if (hdrMode == mode) {
- isHdr = true;
- break;
- }
+ if (!mCompositionDisplay->isValid()) {
+ ALOGE("Composition Display did not validate!");
}
- // add other HDR candidates when mode is HDR
- if (isHdr) {
- for (auto hdrMode : sHdrColorModes) {
- if (hdrMode != mode) {
- candidates.push_back(hdrMode);
- }
- }
- }
+ mCompositionDisplay->getRenderSurface()->initialize();
- // add other SDR candidates
- for (auto sdrMode : sSdrColorModes) {
- if (sdrMode != mode) {
- candidates.push_back(sdrMode);
- }
- }
-
- return candidates;
-}
-
-// Return a list of candidate render intents.
-std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
- std::vector<RenderIntent> candidates;
-
- // add intent itself
- candidates.push_back(intent);
-
- // check if intent is HDR
- bool isHdr = false;
- for (auto hdrIntent : sHdrRenderIntents) {
- if (hdrIntent == intent) {
- isHdr = true;
- break;
- }
- }
-
- if (isHdr) {
- // add other HDR candidates when intent is HDR
- for (auto hdrIntent : sHdrRenderIntents) {
- if (hdrIntent != intent) {
- candidates.push_back(hdrIntent);
- }
- }
- } else {
- // add other SDR candidates when intent is SDR
- for (auto sdrIntent : sSdrRenderIntents) {
- if (sdrIntent != intent) {
- candidates.push_back(sdrIntent);
- }
- }
- }
-
- return candidates;
-}
-
-// Return the best color mode supported by HWC.
-ColorMode getHwcColorMode(
- const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
- ColorMode mode) {
- std::vector<ColorMode> candidates = getColorModeCandidates(mode);
- for (auto candidate : candidates) {
- auto iter = hwcColorModes.find(candidate);
- if (iter != hwcColorModes.end()) {
- return candidate;
- }
- }
-
- return ColorMode::NATIVE;
-}
-
-// Return the best render intent supported by HWC.
-RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
- std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
- for (auto candidate : candidates) {
- for (auto hwcIntent : hwcIntents) {
- if (candidate == hwcIntent) {
- return candidate;
- }
- }
- }
-
- return RenderIntent::COLORIMETRIC;
-}
-
-} // anonymous namespace
-
-// clang-format off
-DisplayDevice::DisplayDevice(
- const sp<SurfaceFlinger>& flinger,
- DisplayType type,
- int32_t hwcId,
- bool isSecure,
- const wp<IBinder>& displayToken,
- const sp<ANativeWindow>& nativeWindow,
- const sp<DisplaySurface>& displaySurface,
- std::unique_ptr<RE::Surface> renderSurface,
- int displayWidth,
- int displayHeight,
- int displayInstallOrientation,
- bool hasWideColorGamut,
- const HdrCapabilities& hdrCapabilities,
- const int32_t supportedPerFrameMetadata,
- const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
- int initialPowerMode)
- : lastCompositionHadVisibleLayers(false),
- mFlinger(flinger),
- mType(type),
- mHwcDisplayId(hwcId),
- mDisplayToken(displayToken),
- mNativeWindow(nativeWindow),
- mDisplaySurface(displaySurface),
- mSurface{std::move(renderSurface)},
- mDisplayWidth(displayWidth),
- mDisplayHeight(displayHeight),
- mDisplayInstallOrientation(displayInstallOrientation),
- mPageFlipCount(0),
- mIsSecure(isSecure),
- mLayerStack(NO_LAYER_STACK),
- mOrientation(),
- mViewport(Rect::INVALID_RECT),
- mFrame(Rect::INVALID_RECT),
- mPowerMode(initialPowerMode),
- mActiveConfig(0),
- mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
- mHasWideColorGamut(hasWideColorGamut),
- mHasHdr10(false),
- mHasHLG(false),
- mHasDolbyVision(false),
- mSupportedPerFrameMetadata(supportedPerFrameMetadata)
-{
- // clang-format on
- populateColorModes(hwcColorModes);
-
- std::vector<Hdr> types = hdrCapabilities.getSupportedHdrTypes();
- for (Hdr hdrType : types) {
- switch (hdrType) {
- case Hdr::HDR10:
- mHasHdr10 = true;
- break;
- case Hdr::HLG:
- mHasHLG = true;
- break;
- case Hdr::DOLBY_VISION:
- mHasDolbyVision = true;
- break;
- default:
- ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
- }
- }
-
- float minLuminance = hdrCapabilities.getDesiredMinLuminance();
- float maxLuminance = hdrCapabilities.getDesiredMaxLuminance();
- float maxAverageLuminance = hdrCapabilities.getDesiredMaxAverageLuminance();
-
- minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
- maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
- maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
- if (this->hasWideColorGamut()) {
- // insert HDR10/HLG as we will force client composition for HDR10/HLG
- // layers
- if (!hasHDR10Support()) {
- types.push_back(Hdr::HDR10);
- }
-
- if (!hasHLGSupport()) {
- types.push_back(Hdr::HLG);
- }
- }
- mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
+ setPowerMode(args.initialPowerMode);
// initialize the display orientation transform.
- setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
+ setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT);
}
DisplayDevice::~DisplayDevice() = default;
-void DisplayDevice::disconnect(HWComposer& hwc) {
- if (mHwcDisplayId >= 0) {
- hwc.disconnectDisplay(mHwcDisplayId);
- mHwcDisplayId = -1;
- }
-}
-
-bool DisplayDevice::isValid() const {
- return mFlinger != nullptr;
+void DisplayDevice::disconnect() {
+ mCompositionDisplay->disconnect();
}
int DisplayDevice::getWidth() const {
- return mDisplayWidth;
+ return mCompositionDisplay->getState().bounds.getWidth();
}
int DisplayDevice::getHeight() const {
- return mDisplayHeight;
+ return mCompositionDisplay->getState().bounds.getHeight();
}
-void DisplayDevice::setDisplayName(const String8& displayName) {
- if (!displayName.isEmpty()) {
+void DisplayDevice::setDisplayName(const std::string& displayName) {
+ if (!displayName.empty()) {
// never override the name with an empty name
mDisplayName = displayName;
+ mCompositionDisplay->setName(displayName);
}
}
uint32_t DisplayDevice::getPageFlipCount() const {
- return mPageFlipCount;
-}
-
-void DisplayDevice::flip() const
-{
- mFlinger->getRenderEngine().checkErrors();
- mPageFlipCount++;
-}
-
-status_t DisplayDevice::beginFrame(bool mustRecompose) const {
- return mDisplaySurface->beginFrame(mustRecompose);
-}
-
-status_t DisplayDevice::prepareFrame(HWComposer& hwc) {
- status_t error = hwc.prepare(*this);
- if (error != NO_ERROR) {
- return error;
- }
-
- DisplaySurface::CompositionType compositionType;
- bool hasClient = hwc.hasClientComposition(mHwcDisplayId);
- bool hasDevice = hwc.hasDeviceComposition(mHwcDisplayId);
- if (hasClient && hasDevice) {
- compositionType = DisplaySurface::COMPOSITION_MIXED;
- } else if (hasClient) {
- compositionType = DisplaySurface::COMPOSITION_GLES;
- } else if (hasDevice) {
- compositionType = DisplaySurface::COMPOSITION_HWC;
- } else {
- // Nothing to do -- when turning the screen off we get a frame like
- // this. Call it a HWC frame since we won't be doing any GLES work but
- // will do a prepare/set cycle.
- compositionType = DisplaySurface::COMPOSITION_HWC;
- }
- return mDisplaySurface->prepareFrame(compositionType);
-}
-
-void DisplayDevice::swapBuffers(HWComposer& hwc) const {
- if (hwc.hasClientComposition(mHwcDisplayId) || hwc.hasFlipClientTargetRequest(mHwcDisplayId)) {
- mSurface->swapBuffers();
- }
-
- status_t result = mDisplaySurface->advanceFrame();
- if (result != NO_ERROR) {
- ALOGE("[%s] failed pushing new frame to HWC: %d",
- mDisplayName.string(), result);
- }
-}
-
-void DisplayDevice::onSwapBuffersCompleted() const {
- mDisplaySurface->onFrameCommitted();
-}
-
-bool DisplayDevice::makeCurrent() const {
- bool success = mFlinger->getRenderEngine().setCurrentSurface(*mSurface);
- setViewportAndProjection();
- return success;
-}
-
-void DisplayDevice::setViewportAndProjection() const {
- size_t w = mDisplayWidth;
- size_t h = mDisplayHeight;
- Rect sourceCrop(0, 0, w, h);
- mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, h,
- false, Transform::ROT_0);
-}
-
-const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const {
- return mDisplaySurface->getClientTargetAcquireFence();
+ return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
// ----------------------------------------------------------------------------
@@ -418,29 +134,18 @@
return mLayersNeedingFences;
}
-Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
- Region dirty;
- if (repaintEverything) {
- dirty.set(getBounds());
- } else {
- const Transform& planeTransform(mGlobalTransform);
- dirty = planeTransform.transform(this->dirtyRegion);
- dirty.andSelf(getBounds());
- }
- return dirty;
-}
-
// ----------------------------------------------------------------------------
void DisplayDevice::setPowerMode(int mode) {
mPowerMode = mode;
+ getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF);
}
int DisplayDevice::getPowerMode() const {
return mPowerMode;
}
-bool DisplayDevice::isDisplayOn() const {
- return (mPowerMode != HWC_POWER_MODE_OFF);
+bool DisplayDevice::isPoweredOn() const {
+ return mPowerMode != HWC_POWER_MODE_OFF;
}
// ----------------------------------------------------------------------------
@@ -453,88 +158,37 @@
}
// ----------------------------------------------------------------------------
-void DisplayDevice::setActiveColorMode(ColorMode mode) {
- mActiveColorMode = mode;
-}
-
-ColorMode DisplayDevice::getActiveColorMode() const {
- return mActiveColorMode;
-}
-
-RenderIntent DisplayDevice::getActiveRenderIntent() const {
- return mActiveRenderIntent;
-}
-
-void DisplayDevice::setActiveRenderIntent(RenderIntent renderIntent) {
- mActiveRenderIntent = renderIntent;
-}
-
-void DisplayDevice::setColorTransform(const mat4& transform) {
- const bool isIdentity = (transform == mat4());
- mColorTransform =
- isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-}
-
-android_color_transform_t DisplayDevice::getColorTransform() const {
- return mColorTransform;
-}
-
-void DisplayDevice::setCompositionDataSpace(ui::Dataspace dataspace) {
- mCompositionDataSpace = dataspace;
- ANativeWindow* const window = mNativeWindow.get();
- native_window_set_buffers_data_space(window, static_cast<android_dataspace>(dataspace));
-}
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
- return mCompositionDataSpace;
+ return mCompositionDisplay->getState().dataspace;
}
// ----------------------------------------------------------------------------
void DisplayDevice::setLayerStack(uint32_t stack) {
- mLayerStack = stack;
- dirtyRegion.set(bounds());
+ mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
}
// ----------------------------------------------------------------------------
-uint32_t DisplayDevice::getOrientationTransform() const {
- uint32_t transform = 0;
- switch (mOrientation) {
- case DisplayState::eOrientationDefault:
- transform = Transform::ROT_0;
- break;
- case DisplayState::eOrientation90:
- transform = Transform::ROT_90;
- break;
- case DisplayState::eOrientation180:
- transform = Transform::ROT_180;
- break;
- case DisplayState::eOrientation270:
- transform = Transform::ROT_270;
- break;
- }
- return transform;
-}
-
-status_t DisplayDevice::orientationToTransfrom(
- int orientation, int w, int h, Transform* tr)
-{
- uint32_t flags = 0;
+uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) {
switch (orientation) {
case DisplayState::eOrientationDefault:
- flags = Transform::ROT_0;
- break;
+ return ui::Transform::ROT_0;
case DisplayState::eOrientation90:
- flags = Transform::ROT_90;
- break;
+ return ui::Transform::ROT_90;
case DisplayState::eOrientation180:
- flags = Transform::ROT_180;
- break;
+ return ui::Transform::ROT_180;
case DisplayState::eOrientation270:
- flags = Transform::ROT_270;
- break;
+ return ui::Transform::ROT_270;
default:
+ return ui::Transform::ROT_INVALID;
+ }
+}
+
+status_t DisplayDevice::orientationToTransfrom(int orientation, int w, int h, ui::Transform* tr) {
+ uint32_t flags = displayStateOrientationToTransformOrientation(orientation);
+ if (flags == ui::Transform::ROT_INVALID) {
return BAD_VALUE;
}
tr->set(flags, w, h);
@@ -542,21 +196,7 @@
}
void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
- dirtyRegion.set(getBounds());
-
- mSurface->setNativeWindow(nullptr);
-
- mDisplaySurface->resizeBuffers(newWidth, newHeight);
-
- ANativeWindow* const window = mNativeWindow.get();
- mSurface->setNativeWindow(window);
- mDisplayWidth = mSurface->queryWidth();
- mDisplayHeight = mSurface->queryHeight();
-
- LOG_FATAL_IF(mDisplayWidth != newWidth,
- "Unable to set new width to %d", newWidth);
- LOG_FATAL_IF(mDisplayHeight != newHeight,
- "Unable to set new height to %d", newHeight);
+ mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
}
void DisplayDevice::setProjection(int orientation,
@@ -564,10 +204,13 @@
Rect viewport(newViewport);
Rect frame(newFrame);
- const int w = mDisplayWidth;
- const int h = mDisplayHeight;
+ mOrientation = orientation;
- Transform R;
+ const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
+ const int w = displayBounds.width();
+ const int h = displayBounds.height();
+
+ ui::Transform R;
DisplayDevice::orientationToTransfrom(orientation, w, h, &R);
if (!frame.isValid()) {
@@ -582,16 +225,14 @@
// 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() & Transform::ROT_90) {
+ if (R.getOrientation() & ui::Transform::ROT_90) {
// viewport is always specified in the logical orientation
// of the display (ie: post-rotation).
- swap(viewport.right, viewport.bottom);
+ std::swap(viewport.right, viewport.bottom);
}
}
- dirtyRegion.set(getBounds());
-
- Transform TL, TP, S;
+ ui::Transform TL, TP, S;
float src_width = viewport.width();
float src_height = viewport.height();
float dst_width = frame.width();
@@ -609,9 +250,9 @@
TL.set(-src_x, -src_y);
TP.set(dst_x, dst_y);
- // need to take care of primary display rotation for mGlobalTransform
+ // need to take care of primary display rotation for globalTransform
// for case if the panel is not installed aligned with device orientation
- if (mType == DisplayType::DISPLAY_PRIMARY) {
+ if (isPrimary()) {
DisplayDevice::orientationToTransfrom(
(orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1),
w, h, &R);
@@ -620,193 +261,122 @@
// 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.
- mGlobalTransform = R * TP * S * TL;
+ ui::Transform globalTransform = R * TP * S * TL;
- const uint8_t type = mGlobalTransform.getType();
- mNeedsFiltering = (!mGlobalTransform.preserveRects() ||
- (type >= Transform::SCALE));
+ const uint8_t type = globalTransform.getType();
+ const bool needsFiltering =
+ (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
- mScissor = mGlobalTransform.transform(viewport);
- if (mScissor.isEmpty()) {
- mScissor = getBounds();
+ Rect scissor = globalTransform.transform(viewport);
+ if (scissor.isEmpty()) {
+ scissor = displayBounds;
}
- mOrientation = orientation;
- if (mType == DisplayType::DISPLAY_PRIMARY) {
- uint32_t transform = 0;
- switch (mOrientation) {
- case DisplayState::eOrientationDefault:
- transform = Transform::ROT_0;
- break;
- case DisplayState::eOrientation90:
- transform = Transform::ROT_90;
- break;
- case DisplayState::eOrientation180:
- transform = Transform::ROT_180;
- break;
- case DisplayState::eOrientation270:
- transform = Transform::ROT_270;
- break;
- }
- sPrimaryDisplayOrientation = transform;
+ if (isPrimary()) {
+ sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
}
- mViewport = viewport;
- mFrame = frame;
+
+ getCompositionDisplay()->setProjection(globalTransform,
+ displayStateOrientationToTransformOrientation(
+ orientation),
+ frame, viewport, scissor, needsFiltering);
}
uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
return sPrimaryDisplayOrientation;
}
-void DisplayDevice::dump(String8& result) const {
- const Transform& tr(mGlobalTransform);
- ANativeWindow* const window = mNativeWindow.get();
- result.appendFormat("+ DisplayDevice: %s\n", mDisplayName.string());
- result.appendFormat(" type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p "
- "(%d:%d:%d:%d), orient=%2d (type=%08x), "
- "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n",
- mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, window,
- mSurface->queryRedSize(), mSurface->queryGreenSize(),
- mSurface->queryBlueSize(), mSurface->queryAlphaSize(), mOrientation,
- tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
- mVisibleLayersSortedByZ.size());
- result.appendFormat(" v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
- "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
- mViewport.left, mViewport.top, mViewport.right, mViewport.bottom,
- mFrame.left, mFrame.top, mFrame.right, mFrame.bottom, mScissor.left,
- mScissor.top, mScissor.right, mScissor.bottom, tr[0][0], tr[1][0], tr[2][0],
- tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
- auto const surface = static_cast<Surface*>(window);
- ui::Dataspace dataspace = surface->getBuffersDataSpace();
- result.appendFormat(" wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
- mHasWideColorGamut, mHasHdr10,
- decodeColorMode(mActiveColorMode).c_str(),
- dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
-
- String8 surfaceDump;
- mDisplaySurface->dumpAsString(surfaceDump);
- result.append(surfaceDump);
+std::string DisplayDevice::getDebugName() const {
+ const auto id = getId() ? to_string(*getId()) + ", " : std::string();
+ return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(),
+ isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "",
+ mDisplayName.c_str());
}
-// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
-// supported by HWC.
-void DisplayDevice::addColorMode(
- const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
- const ColorMode mode, const RenderIntent intent) {
- // find the best color mode
- const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+void DisplayDevice::dump(std::string& result) const {
+ StringAppendF(&result, "+ %s\n", getDebugName().c_str());
- // find the best render intent
- auto iter = hwcColorModes.find(hwcColorMode);
- const auto& hwcIntents =
- iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
- const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
-
- const Dataspace dataspace = colorModeToDataspace(mode);
- const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
-
- ALOGV("DisplayDevice %d/%d: map (%s, %s) to (%s, %s, %s)", mType, mHwcDisplayId,
- dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
- decodeRenderIntent(intent).c_str(),
- dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
- decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
-
- mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+ result.append(" ");
+ StringAppendF(&result, "powerMode=%d, ", mPowerMode);
+ StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
+ StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size());
+ getCompositionDisplay()->dump(result);
}
-void DisplayDevice::populateColorModes(
- const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes) {
- if (!hasWideColorGamut()) {
- return;
- }
-
- // collect all known SDR render intents
- std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
- sSdrRenderIntents.end());
- auto iter = hwcColorModes.find(ColorMode::SRGB);
- if (iter != hwcColorModes.end()) {
- for (auto intent : iter->second) {
- sdrRenderIntents.insert(intent);
- }
- }
-
- // add all known SDR combinations
- for (auto intent : sdrRenderIntents) {
- for (auto mode : sSdrColorModes) {
- addColorMode(hwcColorModes, mode, intent);
- }
- }
-
- // collect all known HDR render intents
- std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
- sHdrRenderIntents.end());
- iter = hwcColorModes.find(ColorMode::BT2100_PQ);
- if (iter != hwcColorModes.end()) {
- for (auto intent : iter->second) {
- hdrRenderIntents.insert(intent);
- }
- }
-
- // add all known HDR combinations
- for (auto intent : sHdrRenderIntents) {
- for (auto mode : sHdrColorModes) {
- addColorMode(hwcColorModes, mode, intent);
- }
- }
+bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const {
+ return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
}
-bool DisplayDevice::hasRenderIntent(RenderIntent intent) const {
- // assume a render intent is supported when SRGB supports it; we should
- // get rid of that assumption.
- auto iter = mColorModes.find(getColorModeKey(Dataspace::SRGB, intent));
- return iter != mColorModes.end() && iter->second.renderIntent == intent;
+// ----------------------------------------------------------------------------
+
+const std::optional<DisplayId>& DisplayDevice::getId() const {
+ return mCompositionDisplay->getId();
}
-bool DisplayDevice::hasLegacyHdrSupport(Dataspace dataspace) const {
- if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
- (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
- auto iter =
- mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
- return iter == mColorModes.end() || iter->second.dataspace != dataspace;
- }
-
- return false;
+bool DisplayDevice::isSecure() const {
+ return mCompositionDisplay->isSecure();
}
-void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent,
- Dataspace* outDataspace, ColorMode* outMode,
- RenderIntent* outIntent) const {
- auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
- if (iter != mColorModes.end()) {
- *outDataspace = iter->second.dataspace;
- *outMode = iter->second.colorMode;
- *outIntent = iter->second.renderIntent;
- } else {
- // this is unexpected on a WCG display
- if (hasWideColorGamut()) {
- ALOGE("map unknown (%s)/(%s) to default color mode",
- dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
- decodeRenderIntent(intent).c_str());
- }
-
- *outDataspace = Dataspace::UNKNOWN;
- *outMode = ColorMode::NATIVE;
- *outIntent = RenderIntent::COLORIMETRIC;
- }
+const Rect& DisplayDevice::getBounds() const {
+ return mCompositionDisplay->getState().bounds;
}
-std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1);
-
-DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure)
- : type(type),
- layerStack(DisplayDevice::NO_LAYER_STACK),
- orientation(0),
- width(0),
- height(0),
- isSecure(isSecure)
-{
- viewport.makeInvalid();
- frame.makeInvalid();
+const Region& DisplayDevice::getUndefinedRegion() const {
+ return mCompositionDisplay->getState().undefinedRegion;
}
+bool DisplayDevice::needsFiltering() const {
+ return mCompositionDisplay->getState().needsFiltering;
+}
+
+uint32_t DisplayDevice::getLayerStack() const {
+ return mCompositionDisplay->getState().layerStackId;
+}
+
+const ui::Transform& DisplayDevice::getTransform() const {
+ return mCompositionDisplay->getState().transform;
+}
+
+const Rect& DisplayDevice::getViewport() const {
+ return mCompositionDisplay->getState().viewport;
+}
+
+const Rect& DisplayDevice::getFrame() const {
+ return mCompositionDisplay->getState().frame;
+}
+
+const Rect& DisplayDevice::getScissor() const {
+ return mCompositionDisplay->getState().scissor;
+}
+
+bool DisplayDevice::hasWideColorGamut() const {
+ return mCompositionDisplay->getDisplayColorProfile()->hasWideColorGamut();
+}
+
+bool DisplayDevice::hasHDR10PlusSupport() const {
+ return mCompositionDisplay->getDisplayColorProfile()->hasHDR10PlusSupport();
+}
+
+bool DisplayDevice::hasHDR10Support() const {
+ return mCompositionDisplay->getDisplayColorProfile()->hasHDR10Support();
+}
+
+bool DisplayDevice::hasHLGSupport() const {
+ return mCompositionDisplay->getDisplayColorProfile()->hasHLGSupport();
+}
+
+bool DisplayDevice::hasDolbyVisionSupport() const {
+ return mCompositionDisplay->getDisplayColorProfile()->hasDolbyVisionSupport();
+}
+
+int DisplayDevice::getSupportedPerFrameMetadata() const {
+ return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
+}
+
+const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
+ return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
+}
+
+std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
+
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3cf06bc..c80925e 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -17,98 +17,70 @@
#ifndef ANDROID_DISPLAY_DEVICE_H
#define ANDROID_DISPLAY_DEVICE_H
-#include "Transform.h"
-
#include <stdlib.h>
+
+#include <memory>
+#include <optional>
+#include <string>
#include <unordered_map>
-#include <math/mat4.h>
-
+#include <android/native_window.h>
#include <binder/IBinder.h>
#include <gui/LayerState.h>
#include <hardware/hwcomposer_defs.h>
+#include <math/mat4.h>
+#include <renderengine/RenderEngine.h>
+#include <system/window.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
-#include <utils/RefBase.h>
+#include <ui/Transform.h>
#include <utils/Mutex.h>
-#include <utils/String8.h>
+#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include "DisplayHardware/DisplayIdentification.h"
#include "RenderArea.h"
-#include "RenderEngine/Surface.h"
-
-#include <memory>
-
-struct ANativeWindow;
namespace android {
-struct DisplayInfo;
-class DisplaySurface;
class Fence;
+class HWComposer;
class IGraphicBufferProducer;
class Layer;
class SurfaceFlinger;
-class HWComposer;
-class DisplayDevice : public LightRefBase<DisplayDevice>
-{
+struct CompositionInfo;
+struct DisplayDeviceCreationArgs;
+struct DisplayInfo;
+
+namespace compositionengine {
+class Display;
+class DisplaySurface;
+} // namespace compositionengine
+
+class DisplayDevice : public LightRefBase<DisplayDevice> {
public:
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
- // region in layer-stack space
- mutable Region dirtyRegion;
- // region in screen space
- Region undefinedRegion;
- bool lastCompositionHadVisibleLayers;
-
- enum DisplayType {
- DISPLAY_ID_INVALID = -1,
- DISPLAY_PRIMARY = HWC_DISPLAY_PRIMARY,
- DISPLAY_EXTERNAL = HWC_DISPLAY_EXTERNAL,
- DISPLAY_VIRTUAL = HWC_DISPLAY_VIRTUAL,
- NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
- };
-
enum {
NO_LAYER_STACK = 0xFFFFFFFF,
};
- // clang-format off
- DisplayDevice(
- const sp<SurfaceFlinger>& flinger,
- DisplayType type,
- int32_t hwcId,
- bool isSecure,
- const wp<IBinder>& displayToken,
- const sp<ANativeWindow>& nativeWindow,
- const sp<DisplaySurface>& displaySurface,
- std::unique_ptr<RE::Surface> renderSurface,
- int displayWidth,
- int displayHeight,
- int displayInstallOrientation,
- bool hasWideColorGamut,
- const HdrCapabilities& hdrCapabilities,
- const int32_t supportedPerFrameMetadata,
- const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
- int initialPowerMode);
- // clang-format on
+ explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
+ virtual ~DisplayDevice();
- ~DisplayDevice();
+ std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
+ return mCompositionDisplay;
+ }
- // whether this is a valid object. An invalid DisplayDevice is returned
- // when an non existing id is requested
- bool isValid() const;
+ bool isVirtual() const { return mIsVirtual; }
+ bool isPrimary() const { return mIsPrimary; }
// isSecure indicates whether this display can be trusted to display
// secure surfaces.
- bool isSecure() const { return mIsSecure; }
-
- // Flip the front and back buffers if the back buffer is "dirty". Might
- // be instantaneous, might involve copying the frame buffer around.
- void flip() const;
+ bool isSecure() const;
int getWidth() const;
int getHeight() const;
@@ -118,43 +90,34 @@
const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
void setLayersNeedingFences(const Vector< sp<Layer> >& layers);
const Vector< sp<Layer> >& getLayersNeedingFences() const;
- Region getDirtyRegion(bool repaintEverything) const;
void setLayerStack(uint32_t stack);
void setDisplaySize(const int newWidth, const int newHeight);
void setProjection(int orientation, const Rect& viewport, const Rect& frame);
int getOrientation() const { return mOrientation; }
- uint32_t getOrientationTransform() const;
static uint32_t getPrimaryDisplayOrientationTransform();
- const Transform& getTransform() const { return mGlobalTransform; }
- const Rect getViewport() const { return mViewport; }
- const Rect getFrame() const { return mFrame; }
- const Rect& getScissor() const { return mScissor; }
- bool needsFiltering() const { return mNeedsFiltering; }
+ const ui::Transform& getTransform() const;
+ const Rect& getViewport() const;
+ const Rect& getFrame() const;
+ const Rect& getScissor() const;
+ bool needsFiltering() const;
+ uint32_t getLayerStack() const;
- uint32_t getLayerStack() const { return mLayerStack; }
- int32_t getDisplayType() const { return mType; }
- bool isPrimary() const { return mType == DISPLAY_PRIMARY; }
- int32_t getHwcDisplayId() const { return mHwcDisplayId; }
- const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
+ const std::optional<DisplayId>& getId() const;
+ const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
+ int32_t getSequenceId() const { return mSequenceId; }
- int32_t getSupportedPerFrameMetadata() const { return mSupportedPerFrameMetadata; }
+ const Region& getUndefinedRegion() const;
- // We pass in mustRecompose so we can keep VirtualDisplaySurface's state
- // machine happy without actually queueing a buffer if nothing has changed
- status_t beginFrame(bool mustRecompose) const;
- status_t prepareFrame(HWComposer& hwc);
+ int32_t getSupportedPerFrameMetadata() const;
- bool hasWideColorGamut() const { return mHasWideColorGamut; }
+ bool hasWideColorGamut() const;
// Whether h/w composer has native support for specific HDR type.
- bool hasHDR10Support() const { return mHasHdr10; }
- bool hasHLGSupport() const { return mHasHLG; }
- bool hasDolbyVisionSupport() const { return mHasDolbyVision; }
-
- // Return true if the HDR dataspace is supported but
- // there is no corresponding color mode.
- bool hasLegacyHdrSupport(ui::Dataspace dataspace) const;
+ bool hasHDR10PlusSupport() const;
+ bool hasHDR10Support() const;
+ bool hasHLGSupport() const;
+ bool hasDolbyVisionSupport() const;
// The returned HdrCapabilities is the combination of HDR capabilities from
// hardware composer and RenderEngine. When the DisplayDevice supports wide
@@ -162,47 +125,24 @@
// color space for both PQ and HLG HDR contents. The minimum and maximum
// luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
// respectively if hardware composer doesn't return meaningful values.
- const HdrCapabilities& getHdrCapabilities() const { return mHdrCapabilities; }
+ const HdrCapabilities& getHdrCapabilities() const;
// Return true if intent is supported by the display.
bool hasRenderIntent(ui::RenderIntent intent) const;
- void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
- ui::Dataspace* outDataspace, ui::ColorMode* outMode,
- ui::RenderIntent* outIntent) const;
+ const Rect& getBounds() const;
+ const Rect& bounds() const { return getBounds(); }
- void swapBuffers(HWComposer& hwc) const;
-
- // called after h/w composer has completed its set() call
- void onSwapBuffersCompleted() const;
-
- Rect getBounds() const {
- return Rect(mDisplayWidth, mDisplayHeight);
- }
- inline Rect bounds() const { return getBounds(); }
-
- void setDisplayName(const String8& displayName);
- const String8& getDisplayName() const { return mDisplayName; }
-
- bool makeCurrent() const;
- void setViewportAndProjection() const;
-
- const sp<Fence>& getClientTargetAcquireFence() const;
+ void setDisplayName(const std::string& displayName);
+ const std::string& getDisplayName() const { return mDisplayName; }
/* ------------------------------------------------------------------------
* Display power mode management.
*/
int getPowerMode() const;
void setPowerMode(int mode);
- bool isDisplayOn() const;
+ bool isPoweredOn() const;
- ui::ColorMode getActiveColorMode() const;
- void setActiveColorMode(ui::ColorMode mode);
- ui::RenderIntent getActiveRenderIntent() const;
- void setActiveRenderIntent(ui::RenderIntent renderIntent);
- android_color_transform_t getColorTransform() const;
- void setColorTransform(const mat4& transform);
- void setCompositionDataSpace(ui::Dataspace dataspace);
ui::Dataspace getCompositionDataSpace() const;
/* ------------------------------------------------------------------------
@@ -212,34 +152,28 @@
void setActiveConfig(int mode);
// release HWC resources (if any) for removable displays
- void disconnect(HWComposer& hwc);
+ void disconnect();
/* ------------------------------------------------------------------------
* Debugging
*/
uint32_t getPageFlipCount() const;
- void dump(String8& result) const;
+ std::string getDebugName() const;
+ void dump(std::string& result) const;
private:
/*
* Constants, set during initialization
*/
- sp<SurfaceFlinger> mFlinger;
- DisplayType mType;
- int32_t mHwcDisplayId;
- wp<IBinder> mDisplayToken;
+ const sp<SurfaceFlinger> mFlinger;
+ const wp<IBinder> mDisplayToken;
+ const int32_t mSequenceId;
- // ANativeWindow this display is rendering into
- sp<ANativeWindow> mNativeWindow;
- sp<DisplaySurface> mDisplaySurface;
+ const int mDisplayInstallOrientation;
+ const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
- std::unique_ptr<RE::Surface> mSurface;
- int mDisplayWidth;
- int mDisplayHeight;
- const int mDisplayInstallOrientation;
- mutable uint32_t mPageFlipCount;
- String8 mDisplayName;
- bool mIsSecure;
+ std::string mDisplayName;
+ const bool mIsVirtual;
/*
* Can only accessed from the main thread, these members
@@ -254,78 +188,27 @@
/*
* Transaction state
*/
+ static uint32_t displayStateOrientationToTransformOrientation(int orientation);
static status_t orientationToTransfrom(int orientation,
- int w, int h, Transform* tr);
-
- // The identifier of the active layer stack for this display. Several displays
- // can use the same layer stack: A z-ordered group of layers (sometimes called
- // "surfaces"). Any given layer can only be on a single layer stack.
- uint32_t mLayerStack;
+ int w, int h, ui::Transform* tr);
int mOrientation;
static uint32_t sPrimaryDisplayOrientation;
- // user-provided visible area of the layer stack
- Rect mViewport;
- // user-provided rectangle where mViewport gets mapped to
- Rect mFrame;
- // pre-computed scissor to apply to the display
- Rect mScissor;
- Transform mGlobalTransform;
- bool mNeedsFiltering;
+
// Current power mode
int mPowerMode;
// Current active config
int mActiveConfig;
- // current active color mode
- ui::ColorMode mActiveColorMode = ui::ColorMode::NATIVE;
- // Current active render intent.
- ui::RenderIntent mActiveRenderIntent = ui::RenderIntent::COLORIMETRIC;
- ui::Dataspace mCompositionDataSpace = ui::Dataspace::UNKNOWN;
- // Current color transform
- android_color_transform_t mColorTransform;
- // Need to know if display is wide-color capable or not.
- // Initialized by SurfaceFlinger when the DisplayDevice is created.
- // Fed to RenderEngine during composition.
- bool mHasWideColorGamut;
- bool mHasHdr10;
- bool mHasHLG;
- bool mHasDolbyVision;
- HdrCapabilities mHdrCapabilities;
- const int32_t mSupportedPerFrameMetadata;
-
- // Mappings from desired Dataspace/RenderIntent to the supported
- // Dataspace/ColorMode/RenderIntent.
- using ColorModeKey = uint64_t;
- struct ColorModeValue {
- ui::Dataspace dataspace;
- ui::ColorMode colorMode;
- ui::RenderIntent renderIntent;
- };
-
- static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
- return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
- }
- void populateColorModes(
- const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes);
- void addColorMode(
- const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
- const ui::ColorMode mode, const ui::RenderIntent intent);
-
- std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
+ // TODO(b/74619554): Remove special cases for primary display.
+ const bool mIsPrimary;
};
struct DisplayDeviceState {
- DisplayDeviceState() = default;
- DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure);
+ bool isVirtual() const { return !displayId.has_value(); }
- bool isValid() const { return type >= 0; }
- bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
- bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
-
- static std::atomic<int32_t> nextDisplayId;
- int32_t displayId = nextDisplayId++;
- DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_ID_INVALID;
+ int32_t sequenceId = sNextSequenceId++;
+ std::optional<DisplayId> displayId;
sp<IGraphicBufferProducer> surface;
uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
Rect viewport;
@@ -333,28 +216,58 @@
uint8_t orientation = 0;
uint32_t width = 0;
uint32_t height = 0;
- String8 displayName;
+ std::string displayName;
bool isSecure = false;
+
+private:
+ static std::atomic<int32_t> sNextSequenceId;
+};
+
+struct DisplayDeviceCreationArgs {
+ // We use a constructor to ensure some of the values are set, without
+ // assuming a default value.
+ DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
+ const std::optional<DisplayId>& displayId);
+
+ const sp<SurfaceFlinger> flinger;
+ const wp<IBinder> displayToken;
+ const std::optional<DisplayId> displayId;
+
+ int32_t sequenceId{0};
+ bool isVirtual{false};
+ bool isSecure{false};
+ sp<ANativeWindow> nativeWindow;
+ sp<compositionengine::DisplaySurface> displaySurface;
+ int displayInstallOrientation{DisplayState::eOrientationDefault};
+ bool hasWideColorGamut{false};
+ HdrCapabilities hdrCapabilities;
+ int32_t supportedPerFrameMetadata{0};
+ std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
+ int initialPowerMode{HWC_POWER_MODE_NORMAL};
+ bool isPrimary{false};
};
class DisplayRenderArea : public RenderArea {
public:
DisplayRenderArea(const sp<const DisplayDevice> device,
- Transform::orientation_flags rotation = Transform::ROT_0)
+ ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
- rotation) {}
+ device->getCompositionDataSpace(), rotation) {}
DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, Transform::orientation_flags rotation)
- : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE,
+ uint32_t reqHeight, ui::Dataspace reqDataSpace,
+ ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
getDisplayRotation(rotation, device->getInstallOrientation())),
mDevice(device),
- mSourceCrop(sourceCrop) {}
+ mSourceCrop(sourceCrop),
+ mAllowSecureLayers(allowSecureLayers) {}
- const Transform& getTransform() const override { return mDevice->getTransform(); }
+ const ui::Transform& getTransform() const override { return mDevice->getTransform(); }
Rect getBounds() const override { return mDevice->getBounds(); }
int getHeight() const override { return mDevice->getHeight(); }
int getWidth() const override { return mDevice->getWidth(); }
- bool isSecure() const override { return mDevice->isSecure(); }
+ bool isSecure() const override { return mAllowSecureLayers && mDevice->isSecure(); }
+ const sp<const DisplayDevice> getDisplayDevice() const override { return mDevice; }
bool needsFiltering() const override {
// check if the projection from the logical display to the physical
@@ -368,7 +281,7 @@
const Rect sourceCrop = getSourceCrop();
int width = sourceCrop.width();
int height = sourceCrop.height();
- if (getRotationFlags() & Transform::ROT_90) {
+ if (getRotationFlags() & ui::Transform::ROT_90) {
std::swap(width, height);
}
return width != getReqWidth() || height != getReqHeight();
@@ -389,16 +302,16 @@
uint32_t flags = 0x00;
switch (orientation) {
case DisplayState::eOrientation90:
- flags = Transform::ROT_90;
+ flags = ui::Transform::ROT_90;
break;
case DisplayState::eOrientation180:
- flags = Transform::ROT_180;
+ flags = ui::Transform::ROT_180;
break;
case DisplayState::eOrientation270:
- flags = Transform::ROT_270;
+ flags = ui::Transform::ROT_270;
break;
}
- Transform tr;
+ ui::Transform tr;
tr.set(flags, getWidth(), getHeight());
return tr.transform(mSourceCrop);
}
@@ -406,8 +319,8 @@
private:
// Install orientation is transparent to the callers. We need to cancel
// it out by modifying rotation flags.
- static Transform::orientation_flags getDisplayRotation(
- Transform::orientation_flags rotation, int orientation) {
+ static ui::Transform::orientation_flags getDisplayRotation(
+ ui::Transform::orientation_flags rotation, int orientation) {
if (orientation == DisplayState::eOrientationDefault) {
return rotation;
}
@@ -418,32 +331,33 @@
uint8_t hw_flip_hv = 0x00;
switch (orientation) {
case DisplayState::eOrientation90:
- hw_rot_90 = Transform::ROT_90;
- hw_flip_hv = Transform::ROT_180;
+ hw_rot_90 = ui::Transform::ROT_90;
+ hw_flip_hv = ui::Transform::ROT_180;
break;
case DisplayState::eOrientation180:
- hw_flip_hv = Transform::ROT_180;
+ hw_flip_hv = ui::Transform::ROT_180;
break;
case DisplayState::eOrientation270:
- hw_rot_90 = Transform::ROT_90;
+ hw_rot_90 = ui::Transform::ROT_90;
break;
}
// transform flags operation
// 1) flip H V if both have ROT_90 flag
// 2) XOR these flags
- uint8_t rotation_rot_90 = rotation & Transform::ROT_90;
- uint8_t rotation_flip_hv = rotation & Transform::ROT_180;
+ uint8_t rotation_rot_90 = rotation & ui::Transform::ROT_90;
+ uint8_t rotation_flip_hv = rotation & ui::Transform::ROT_180;
if (rotation_rot_90 & hw_rot_90) {
- rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180;
+ rotation_flip_hv = (~rotation_flip_hv) & ui::Transform::ROT_180;
}
- return static_cast<Transform::orientation_flags>(
+ return static_cast<ui::Transform::orientation_flags>(
(rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
}
const sp<const DisplayDevice> mDevice;
const Rect mSourceCrop;
+ const bool mAllowSecureLayers;
};
}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index f190b71..9cb43bc 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -22,7 +22,6 @@
#include "ComposerHal.h"
-#include <android/hardware/graphics/composer/2.2/IComposer.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <gui/BufferQueue.h>
#include <hidl/HidlTransportUtils.h>
@@ -171,22 +170,31 @@
LOG_ALWAYS_FATAL("failed to get hwcomposer service");
}
- mComposer->createClient(
- [&](const auto& tmpError, const auto& tmpClient)
- {
- if (tmpError == Error::NONE) {
- mClient = tmpClient;
- }
- });
- if (mClient == nullptr) {
- LOG_ALWAYS_FATAL("failed to create composer client");
+ if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) {
+ composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError == Error::NONE) {
+ mClient = tmpClient;
+ mClient_2_2 = tmpClient;
+ mClient_2_3 = tmpClient;
+ }
+ });
+ } else {
+ mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ mClient = tmpClient;
+ if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
+ mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
+ LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
+ "IComposer 2.2 did not return IComposerClient 2.2");
+ }
+ });
}
- // 2.2 support is optional
- sp<IComposer> composer_2_2 = IComposer::castFrom(mComposer);
- if (composer_2_2 != nullptr) {
- mClient_2_2 = IComposerClient::castFrom(mClient);
- LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr, "IComposer 2.2 did not return IComposerClient 2.2");
+ if (mClient == nullptr) {
+ LOG_ALWAYS_FATAL("failed to create composer client");
}
if (mIsUsingVrComposer) {
@@ -206,7 +214,6 @@
[&](const auto& tmpCapabilities) {
capabilities = tmpCapabilities;
});
-
return capabilities;
}
@@ -252,17 +259,20 @@
const uint32_t bufferSlotCount = 1;
Error error = kDefaultError;
if (mClient_2_2) {
- mClient_2_2->createVirtualDisplay_2_2(width, height, *format, bufferSlotCount,
- [&](const auto& tmpError, const auto& tmpDisplay,
- const auto& tmpFormat) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ mClient_2_2->createVirtualDisplay_2_2(width, height,
+ static_cast<types::V1_1::PixelFormat>(*format),
+ bufferSlotCount,
+ [&](const auto& tmpError, const auto& tmpDisplay,
+ const auto& tmpFormat) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outDisplay = tmpDisplay;
- *format = tmpFormat;
- });
+ *outDisplay = tmpDisplay;
+ *format = static_cast<types::V1_2::PixelFormat>(
+ tmpFormat);
+ });
} else {
mClient->createVirtualDisplay(width, height,
static_cast<types::V1_0::PixelFormat>(*format), bufferSlotCount,
@@ -345,16 +355,26 @@
{
Error error = kDefaultError;
- if (mClient_2_2) {
- mClient_2_2->getColorModes_2_2(display,
- [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ if (mClient_2_3) {
+ mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outModes = tmpModes;
- });
+ *outModes = tmpModes;
+ });
+ } else if (mClient_2_2) {
+ mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ for (types::V1_1::ColorMode colorMode : tmpModes) {
+ outModes->push_back(static_cast<ColorMode>(colorMode));
+ }
+ });
} else {
mClient->getColorModes(display,
[&](const auto& tmpError, const auto& tmpModes) {
@@ -468,21 +488,43 @@
float* outMaxAverageLuminance, float* outMinLuminance)
{
Error error = kDefaultError;
- mClient->getHdrCapabilities(display,
- [&](const auto& tmpError, const auto& tmpTypes,
- const auto& tmpMaxLuminance,
- const auto& tmpMaxAverageLuminance,
- const auto& tmpMinLuminance) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ if (mClient_2_3) {
+ mClient_2_3->getHdrCapabilities_2_3(display,
+ [&](const auto& tmpError, const auto& tmpTypes,
+ const auto& tmpMaxLuminance,
+ const auto& tmpMaxAverageLuminance,
+ const auto& tmpMinLuminance) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outTypes = tmpTypes;
- *outMaxLuminance = tmpMaxLuminance;
- *outMaxAverageLuminance = tmpMaxAverageLuminance;
- *outMinLuminance = tmpMinLuminance;
- });
+ *outTypes = tmpTypes;
+ *outMaxLuminance = tmpMaxLuminance;
+ *outMaxAverageLuminance = tmpMaxAverageLuminance;
+ *outMinLuminance = tmpMinLuminance;
+ });
+ } else {
+ mClient->getHdrCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpTypes,
+ const auto& tmpMaxLuminance,
+ const auto& tmpMaxAverageLuminance,
+ const auto& tmpMinLuminance) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ outTypes->clear();
+ for (auto type : tmpTypes) {
+ outTypes->push_back(static_cast<Hdr>(type));
+ }
+
+ *outMaxLuminance = tmpMaxLuminance;
+ *outMaxAverageLuminance = tmpMaxAverageLuminance;
+ *outMinLuminance = tmpMinLuminance;
+ });
+ }
return error;
}
@@ -546,8 +588,11 @@
RenderIntent renderIntent)
{
hardware::Return<Error> ret(kDefaultError);
- if (mClient_2_2) {
- ret = mClient_2_2->setColorMode_2_2(display, mode, renderIntent);
+ if (mClient_2_3) {
+ ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
+ } else if (mClient_2_2) {
+ ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
+ renderIntent);
} else {
ret = mClient->setColorMode(display,
static_cast<types::V1_0::ColorMode>(mode));
@@ -896,23 +941,43 @@
return Error::NONE;
}
-Error Composer::getPerFrameMetadataKeys(
- Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
+std::vector<IComposerClient::PerFrameMetadataKey> Composer::getPerFrameMetadataKeys(
+ Display display) {
+ std::vector<IComposerClient::PerFrameMetadataKey> keys;
if (!mClient_2_2) {
- return Error::UNSUPPORTED;
+ return keys;
}
Error error = kDefaultError;
- mClient_2_2->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ if (mClient_2_3) {
+ mClient_2_3->getPerFrameMetadataKeys_2_3(display,
+ [&](const auto& tmpError, const auto& tmpKeys) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ ALOGW("getPerFrameMetadataKeys failed "
+ "with %d",
+ tmpError);
+ return;
+ }
+ keys = tmpKeys;
+ });
+ } else {
+ mClient_2_2
+ ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
+ return;
+ }
- *outKeys = tmpKeys;
- });
+ keys.clear();
+ for (auto key : tmpKeys) {
+ keys.push_back(static_cast<IComposerClient::PerFrameMetadataKey>(key));
+ }
+ });
+ }
- return error;
+ return keys;
}
Error Composer::getRenderIntents(Display display, ColorMode colorMode,
@@ -923,15 +988,22 @@
}
Error error = kDefaultError;
- mClient_2_2->getRenderIntents(display, colorMode,
- [&](const auto& tmpError, const auto& tmpKeys) {
+
+ auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
error = tmpError;
if (error != Error::NONE) {
return;
}
*outRenderIntents = tmpKeys;
- });
+ };
+
+ if (mClient_2_3) {
+ mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
+ } else {
+ mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
+ getRenderIntentsLambda);
+ }
return error;
}
@@ -944,18 +1016,172 @@
}
Error error = kDefaultError;
- mClient_2_2->getDataspaceSaturationMatrix(dataspace, [&](const auto& tmpError, const auto& tmpMatrix) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outMatrix = mat4(tmpMatrix.data());
- });
+ mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
+ [&](const auto& tmpError, const auto& tmpMatrix) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outMatrix = mat4(tmpMatrix.data());
+ });
return error;
}
+// Composer HAL 2.3
+
+Error Composer::getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayIdentificationData(display,
+ [&](const auto& tmpError, const auto& tmpPort,
+ const auto& tmpData) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outPort = tmpPort;
+ *outData = tmpData;
+ });
+
+ return error;
+}
+
+Error Composer::setLayerColorTransform(Display display, Layer layer, const float* matrix)
+{
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerColorTransform(matrix);
+ return Error::NONE;
+}
+
+Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ if (!outFormat || !outDataspace || !outComponentMask) {
+ return Error::BAD_PARAMETER;
+ }
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayedContentSamplingAttributes(display,
+ [&](const auto tmpError,
+ const auto& tmpFormat,
+ const auto& tmpDataspace,
+ const auto& tmpComponentMask) {
+ error = tmpError;
+ if (error == Error::NONE) {
+ *outFormat = tmpFormat;
+ *outDataspace = tmpDataspace;
+ *outComponentMask =
+ static_cast<uint8_t>(
+ tmpComponentMask);
+ }
+ });
+ return error;
+}
+
+Error Composer::getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpCapabilities) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outCapabilities = tmpCapabilities;
+ });
+ return error;
+}
+
+Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled,
+ uint8_t componentMask, uint64_t maxFrames) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
+ : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
+ return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+ maxFrames);
+}
+
+Error Composer::getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) {
+ if (!outStats) {
+ return Error::BAD_PARAMETER;
+ }
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
+ [&](const auto tmpError, auto tmpNumFrames,
+ const auto& tmpSamples0, const auto& tmpSamples1,
+ const auto& tmpSamples2, const auto& tmpSamples3) {
+ error = tmpError;
+ if (error == Error::NONE) {
+ outStats->numFrames = tmpNumFrames;
+ outStats->component_0_sample = tmpSamples0;
+ outStats->component_1_sample = tmpSamples1;
+ outStats->component_2_sample = tmpSamples2;
+ outStats->component_3_sample = tmpSamples3;
+ }
+ });
+ return error;
+}
+
+Error Composer::setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerPerFrameMetadataBlobs(metadata);
+ return Error::NONE;
+}
+
+Error Composer::getDisplayBrightnessSupport(Display display, bool* outSupport) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayBrightnessSupport(display,
+ [&](const auto& tmpError, const auto& tmpSupport) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupport = tmpSupport;
+ });
+ return error;
+}
+
+Error Composer::setDisplayBrightness(Display display, float brightness) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ return mClient_2_3->setDisplayBrightness(display, brightness);
+}
+
CommandReader::~CommandReader()
{
resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 442006c..e24db15 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -25,11 +25,12 @@
#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
#include <android/hardware/graphics/common/1.1/types.h>
-#include <android/hardware/graphics/composer/2.2/IComposer.h>
-#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
-#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
+#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
+#include <ui/DisplayedFrameStats.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
@@ -43,29 +44,29 @@
namespace V2_1 = hardware::graphics::composer::V2_1;
namespace V2_2 = hardware::graphics::composer::V2_2;
+namespace V2_3 = hardware::graphics::composer::V2_3;
using types::V1_0::ColorTransform;
-using types::V1_0::Hdr;
using types::V1_0::Transform;
-
-using types::V1_1::ColorMode;
-using types::V1_1::Dataspace;
-using types::V1_1::PixelFormat;
using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
using V2_1::Config;
using V2_1::Display;
using V2_1::Error;
using V2_1::IComposerCallback;
using V2_1::Layer;
-
-using V2_2::CommandReaderBase;
-using V2_2::CommandWriterBase;
-using V2_2::IComposer;
-using V2_2::IComposerClient;
-
+using V2_3::CommandReaderBase;
+using V2_3::CommandWriterBase;
+using V2_3::IComposer;
+using V2_3::IComposerClient;
+using DisplayCapability = IComposerClient::DisplayCapability;
using PerFrameMetadata = IComposerClient::PerFrameMetadata;
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
+using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
class Composer {
public:
@@ -180,11 +181,30 @@
virtual Error setLayerPerFrameMetadata(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) = 0;
- virtual Error getPerFrameMetadataKeys(
- Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) = 0;
+ virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+ Display display) = 0;
virtual Error getRenderIntents(Display display, ColorMode colorMode,
std::vector<RenderIntent>* outRenderIntents) = 0;
virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0;
+
+ // Composer HAL 2.3
+ virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) = 0;
+ virtual Error setLayerColorTransform(Display display, Layer layer,
+ const float* matrix) = 0;
+ virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) = 0;
+ virtual Error setDisplayContentSamplingEnabled(Display display, bool enabled,
+ uint8_t componentMask, uint64_t maxFrames) = 0;
+ virtual Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) = 0;
+ virtual Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) = 0;
+ virtual Error setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0;
+ virtual Error getDisplayBrightnessSupport(Display display, bool* outSupport) = 0;
+ virtual Error setDisplayBrightness(Display display, float brightness) = 0;
};
namespace impl {
@@ -374,12 +394,31 @@
Error setLayerPerFrameMetadata(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
- Error getPerFrameMetadataKeys(
- Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
+ std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+ Display display) override;
Error getRenderIntents(Display display, ColorMode colorMode,
std::vector<RenderIntent>* outRenderIntents) override;
Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
+ // Composer HAL 2.3
+ Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) override;
+ Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+ Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) override;
+ Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) override;
+ Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) override;
+ Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) override;
+ Error setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
+ Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
+ Error setDisplayBrightness(Display display, float brightness) override;
+
private:
class CommandWriter : public CommandWriterBase {
public:
@@ -405,7 +444,8 @@
sp<V2_1::IComposer> mComposer;
sp<V2_1::IComposerClient> mClient;
- sp<IComposerClient> mClient_2_2;
+ sp<V2_2::IComposerClient> mClient_2_2;
+ sp<IComposerClient> mClient_2_3;
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize =
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
new file mode 100644
index 0000000..ba7818d
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 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 "DisplayIdentification"
+
+#include <algorithm>
+#include <cctype>
+#include <numeric>
+#include <optional>
+
+#include <log/log.h>
+
+#include "DisplayIdentification.h"
+
+namespace android {
+namespace {
+
+using byte_view = std::basic_string_view<uint8_t>;
+
+constexpr size_t kEdidHeaderLength = 5;
+
+constexpr uint16_t kFallbackEdidManufacturerId = 0;
+constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu;
+
+std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) {
+ if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) {
+ return {};
+ }
+
+ return view[3];
+}
+
+std::string_view parseEdidText(const byte_view& view) {
+ std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
+ text = text.substr(0, text.find('\n'));
+
+ if (!std::all_of(text.begin(), text.end(), ::isprint)) {
+ ALOGW("Invalid EDID: ASCII text is not printable.");
+ return {};
+ }
+
+ return text;
+}
+
+// Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001.
+template <size_t I>
+char getPnpLetter(uint16_t id) {
+ static_assert(I < 3);
+ const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1;
+ return letter < 'A' || letter > 'Z' ? '\0' : letter;
+}
+
+} // namespace
+
+uint16_t DisplayId::manufacturerId() const {
+ return static_cast<uint16_t>(value >> 40);
+}
+
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
+ return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
+ port};
+}
+
+bool isEdid(const DisplayIdentificationData& data) {
+ const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
+ return data.size() >= sizeof(kMagic) &&
+ std::equal(std::begin(kMagic), std::end(kMagic), data.begin());
+}
+
+std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
+ constexpr size_t kMinLength = 128;
+ if (edid.size() < kMinLength) {
+ ALOGW("Invalid EDID: structure is truncated.");
+ // Attempt parsing even if EDID is malformed.
+ } else {
+ ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported.");
+ ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)),
+ "Invalid EDID: structure does not checksum.");
+ }
+
+ constexpr size_t kManufacturerOffset = 8;
+ if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) {
+ ALOGE("Invalid EDID: manufacturer ID is truncated.");
+ return {};
+ }
+
+ // Plug and play ID encoded as big-endian 16-bit value.
+ const uint16_t manufacturerId =
+ (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1];
+
+ const auto pnpId = getPnpId(manufacturerId);
+ if (!pnpId) {
+ ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID.");
+ return {};
+ }
+
+ constexpr size_t kDescriptorOffset = 54;
+ if (edid.size() < kDescriptorOffset) {
+ ALOGE("Invalid EDID: descriptors are missing.");
+ return {};
+ }
+
+ byte_view view(edid.data(), edid.size());
+ view.remove_prefix(kDescriptorOffset);
+
+ std::string_view displayName;
+ std::string_view serialNumber;
+ std::string_view asciiText;
+
+ constexpr size_t kDescriptorCount = 4;
+ constexpr size_t kDescriptorLength = 18;
+
+ for (size_t i = 0; i < kDescriptorCount; i++) {
+ if (view.size() < kDescriptorLength) {
+ break;
+ }
+
+ if (const auto type = getEdidDescriptorType(view)) {
+ byte_view descriptor(view.data(), kDescriptorLength);
+ descriptor.remove_prefix(kEdidHeaderLength);
+
+ switch (*type) {
+ case 0xfc:
+ displayName = parseEdidText(descriptor);
+ break;
+ case 0xfe:
+ asciiText = parseEdidText(descriptor);
+ break;
+ case 0xff:
+ serialNumber = parseEdidText(descriptor);
+ break;
+ }
+ }
+
+ view.remove_prefix(kDescriptorLength);
+ }
+
+ if (displayName.empty()) {
+ ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
+ displayName = serialNumber;
+ }
+ if (displayName.empty()) {
+ ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
+ displayName = asciiText;
+ }
+ if (displayName.empty()) {
+ ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
+ return {};
+ }
+
+ return Edid{manufacturerId, *pnpId, displayName};
+}
+
+std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
+ const char a = getPnpLetter<0>(manufacturerId);
+ const char b = getPnpLetter<1>(manufacturerId);
+ const char c = getPnpLetter<2>(manufacturerId);
+ return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
+}
+
+std::optional<PnpId> getPnpId(DisplayId displayId) {
+ return getPnpId(displayId.manufacturerId());
+}
+
+std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
+ uint8_t port, const DisplayIdentificationData& data) {
+ if (!isEdid(data)) {
+ ALOGE("Display identification data has unknown format.");
+ return {};
+ }
+
+ const auto edid = parseEdid(data);
+ if (!edid) {
+ return {};
+ }
+
+ // Hash display name instead of using product code or serial number, since the latter have been
+ // observed to change on some displays with multiple inputs.
+ const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
+ return DisplayIdentificationInfo{DisplayId::fromEdid(port, edid->manufacturerId, hash),
+ std::string(edid->displayName)};
+}
+
+DisplayId getFallbackDisplayId(uint8_t port) {
+ return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0);
+}
+
+DisplayId getVirtualDisplayId(uint32_t id) {
+ return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
new file mode 100644
index 0000000..d63cd79
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 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 <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+struct DisplayId {
+ using Type = PhysicalDisplayId;
+ Type value;
+
+ uint16_t manufacturerId() const;
+
+ static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+};
+
+inline bool operator==(DisplayId lhs, DisplayId rhs) {
+ return lhs.value == rhs.value;
+}
+
+inline bool operator!=(DisplayId lhs, DisplayId rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::string to_string(DisplayId displayId) {
+ return std::to_string(displayId.value);
+}
+
+using DisplayIdentificationData = std::vector<uint8_t>;
+
+struct DisplayIdentificationInfo {
+ DisplayId id;
+ std::string name;
+};
+
+// NUL-terminated plug and play ID.
+using PnpId = std::array<char, 4>;
+
+struct Edid {
+ uint16_t manufacturerId;
+ PnpId pnpId;
+ std::string_view displayName;
+};
+
+bool isEdid(const DisplayIdentificationData&);
+std::optional<Edid> parseEdid(const DisplayIdentificationData&);
+std::optional<PnpId> getPnpId(uint16_t manufacturerId);
+std::optional<PnpId> getPnpId(DisplayId);
+
+std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
+ uint8_t port, const DisplayIdentificationData&);
+
+DisplayId getFallbackDisplayId(uint8_t port);
+DisplayId getVirtualDisplayId(uint32_t id);
+
+} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::DisplayId> {
+ size_t operator()(android::DisplayId displayId) const {
+ return hash<android::DisplayId::Type>()(displayId.value);
+ }
+};
+
+} // namespace std
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index e6d7834..775fb80 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -52,26 +52,25 @@
*
*/
-FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp,
- const sp<IGraphicBufferConsumer>& consumer) :
- ConsumerBase(consumer),
- mDisplayType(disp),
- mCurrentBufferSlot(-1),
- mCurrentBuffer(),
- mCurrentFence(Fence::NO_FENCE),
- mHwc(hwc),
- mHasPendingRelease(false),
- mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mPreviousBuffer()
-{
- ALOGV("Creating for display %d", disp);
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+ const sp<IGraphicBufferConsumer>& consumer)
+ : ConsumerBase(consumer),
+ mDisplayId(displayId),
+ mCurrentBufferSlot(-1),
+ mCurrentBuffer(),
+ mCurrentFence(Fence::NO_FENCE),
+ mHwc(hwc),
+ mHasPendingRelease(false),
+ mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
+ mPreviousBuffer() {
+ ALOGV("Creating for display %s", to_string(displayId).c_str());
mName = "FramebufferSurface";
mConsumer->setConsumerName(mName);
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
- const auto& activeConfig = mHwc.getActiveConfig(disp);
+ const auto& activeConfig = mHwc.getActiveConfig(displayId);
mConsumer->setDefaultBufferSize(activeConfig->getWidth(),
activeConfig->getHeight());
mConsumer->setMaxAcquiredBufferCount(
@@ -112,8 +111,7 @@
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
- &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -139,11 +137,9 @@
mCurrentFence = item.mFence;
outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
- &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
outDataspace = static_cast<Dataspace>(item.mDataSpace);
- status_t result =
- mHwc.setClientTarget(mDisplayType, outSlot, outFence, outBuffer, outDataspace);
+ status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
if (result != NO_ERROR) {
ALOGE("error posting framebuffer: %d", result);
return result;
@@ -161,7 +157,7 @@
void FramebufferSurface::onFrameCommitted() {
if (mHasPendingRelease) {
- sp<Fence> fence = mHwc.getPresentFence(mDisplayType);
+ sp<Fence> fence = mHwc.getPresentFence(mDisplayId);
if (fence->isValid()) {
status_t result = addReleaseFence(mPreviousBufferSlot,
mPreviousBuffer, fence);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 0fd8e9e..7f451a5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,14 +17,15 @@
#ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
#define ANDROID_SF_FRAMEBUFFER_SURFACE_H
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
-
#include <stdint.h>
#include <sys/types.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
+#include "DisplayIdentification.h"
+
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
@@ -35,10 +36,10 @@
// ---------------------------------------------------------------------------
-class FramebufferSurface : public ConsumerBase,
- public DisplaySurface {
+class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
public:
- FramebufferSurface(HWComposer& hwc, int disp, const sp<IGraphicBufferConsumer>& consumer);
+ FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+ const sp<IGraphicBufferConsumer>& consumer);
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
@@ -63,8 +64,7 @@
status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
sp<Fence>& outFence, ui::Dataspace& outDataspace);
- // mDisplayType must match one of the HWC display types
- int mDisplayType;
+ const DisplayId mDisplayId;
// mCurrentBufferIndex is the slot index of the current buffer or
// INVALID_BUFFER_SLOT to indicate that either there is no current buffer
@@ -88,7 +88,7 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
- HWComposerBufferCache mHwcBufferCache;
+ compositionengine::impl::HwcBufferCache mHwcBufferCache;
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 1a60c83..62073b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -124,6 +124,12 @@
return mComposer->getMaxVirtualDisplayCount();
}
+Error Device::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+ std::vector<uint8_t>* outData) const {
+ auto intError = mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+ return static_cast<Error>(intError);
+}
+
Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
PixelFormat* format, Display** outDisplay)
{
@@ -137,8 +143,8 @@
return error;
}
- auto display = std::make_unique<Display>(
- *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, DisplayType::Virtual);
+ auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId,
+ DisplayType::Virtual);
display->setConnected(true);
*outDisplay = display.get();
mDisplays.emplace(displayId, std::move(display));
@@ -176,8 +182,8 @@
return;
}
- auto newDisplay = std::make_unique<Display>(
- *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, displayType);
+ auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
+ displayId, displayType);
newDisplay->setConnected(true);
mDisplays.emplace(displayId, std::move(newDisplay));
} else if (connection == Connection::Disconnected) {
@@ -218,16 +224,65 @@
}
// Display methods
+Display::~Display() = default;
-Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
+Display::Config::Config(Display& display, hwc2_config_t id)
+ : mDisplay(display),
+ mId(id),
+ mWidth(-1),
+ mHeight(-1),
+ mVsyncPeriod(-1),
+ mDpiX(-1),
+ mDpiY(-1) {}
+
+Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
+ : mConfig(new Config(display, id)) {}
+
+float Display::Config::Builder::getDefaultDensity() {
+ // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+ // resolution displays get TV density. Maybe eventually we'll need to update
+ // it for 4k displays, though hopefully those will just report accurate DPI
+ // information to begin with. This is also used for virtual displays and
+ // older HWC implementations, so be careful about orientation.
+
+ auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
+ if (longDimension >= 1080) {
+ return ACONFIGURATION_DENSITY_XHIGH;
+ } else {
+ return ACONFIGURATION_DENSITY_TV;
+ }
+}
+
+namespace impl {
+Display::Display(android::Hwc2::Composer& composer,
const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
DisplayType type)
: mComposer(composer),
- mPowerAdvisor(advisor),
mCapabilities(capabilities),
mId(id),
mIsConnected(false),
mType(type) {
+ std::vector<Hwc2::DisplayCapability> tmpCapabilities;
+ auto error = static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
+ if (error == Error::None) {
+ for (auto capability : tmpCapabilities) {
+ mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+ }
+ } else if (error == Error::Unsupported) {
+ if (capabilities.count(Capability::SkipClientColorTransform)) {
+ mDisplayCapabilities.emplace(DisplayCapability::SkipClientColorTransform);
+ }
+ bool dozeSupport = false;
+ error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
+ if (error == Error::None && dozeSupport) {
+ mDisplayCapabilities.emplace(DisplayCapability::Doze);
+ }
+ bool brightnessSupport = false;
+ error = static_cast<Error>(mComposer.getDisplayBrightnessSupport(mId, &brightnessSupport));
+ if (error == Error::None && brightnessSupport) {
+ mDisplayCapabilities.emplace(DisplayCapability::Brightness);
+ }
+ }
ALOGV("Created display %" PRIu64, id);
}
@@ -250,43 +305,14 @@
}
}
-Display::Config::Config(Display& display, hwc2_config_t id)
- : mDisplay(display),
- mId(id),
- mWidth(-1),
- mHeight(-1),
- mVsyncPeriod(-1),
- mDpiX(-1),
- mDpiY(-1) {}
-
-Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
- : mConfig(new Config(display, id)) {}
-
-float Display::Config::Builder::getDefaultDensity() {
- // Default density is based on TVs: 1080p displays get XHIGH density, lower-
- // resolution displays get TV density. Maybe eventually we'll need to update
- // it for 4k displays, though hopefully those will just report accurate DPI
- // information to begin with. This is also used for virtual displays and
- // older HWC implementations, so be careful about orientation.
-
- auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
- if (longDimension >= 1080) {
- return ACONFIGURATION_DENSITY_XHIGH;
- } else {
- return ACONFIGURATION_DENSITY_TV;
- }
-}
-
// Required by HWC2 display
-
Error Display::acceptChanges()
{
auto intError = mComposer.acceptDisplayChanges(mId);
return static_cast<Error>(intError);
}
-Error Display::createLayer(Layer** outLayer)
-{
+Error Display::createLayer(HWC2::Layer** outLayer) {
if (!outLayer) {
return Error::BadParameter;
}
@@ -297,15 +323,13 @@
return error;
}
- auto layer = std::make_unique<Layer>(
- mComposer, mCapabilities, mId, layerId);
+ auto layer = std::make_unique<impl::Layer>(mComposer, mCapabilities, mId, layerId);
*outLayer = layer.get();
mLayers.emplace(layerId, std::move(layer));
return Error::None;
}
-Error Display::destroyLayer(Layer* layer)
-{
+Error Display::destroyLayer(HWC2::Layer* layer) {
if (!layer) {
return Error::BadParameter;
}
@@ -365,9 +389,7 @@
return Error::None;
}
-Error Display::getChangedCompositionTypes(
- std::unordered_map<Layer*, Composition>* outTypes)
-{
+Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
std::vector<Hwc2::Layer> layerIds;
std::vector<Hwc2::IComposerClient::Composition> types;
auto intError = mComposer.getChangedCompositionTypes(
@@ -403,21 +425,17 @@
return static_cast<Error>(intError);
}
-Error Display::getSupportedPerFrameMetadata(int32_t* outSupportedPerFrameMetadata) const
+int32_t Display::getSupportedPerFrameMetadata() const
{
- *outSupportedPerFrameMetadata = 0;
- std::vector<Hwc2::PerFrameMetadataKey> tmpKeys;
- auto intError = mComposer.getPerFrameMetadataKeys(mId, &tmpKeys);
- auto error = static_cast<Error>(intError);
- if (error != Error::None) {
- return error;
- }
+ int32_t supportedPerFrameMetadata = 0;
+
+ std::vector<Hwc2::PerFrameMetadataKey> tmpKeys = mComposer.getPerFrameMetadataKeys(mId);
+ std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end());
// Check whether a specific metadata type is supported. A metadata type is considered
// supported if and only if all required fields are supported.
// SMPTE2086
- std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end());
if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X) &&
hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y) &&
hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X) &&
@@ -428,15 +446,20 @@
hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::WHITE_POINT_Y) &&
hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_LUMINANCE) &&
hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MIN_LUMINANCE)) {
- *outSupportedPerFrameMetadata |= HdrMetadata::Type::SMPTE2086;
+ supportedPerFrameMetadata |= HdrMetadata::Type::SMPTE2086;
}
// CTA861_3
if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL) &&
hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL)) {
- *outSupportedPerFrameMetadata |= HdrMetadata::Type::CTA861_3;
+ supportedPerFrameMetadata |= HdrMetadata::Type::CTA861_3;
}
- return Error::None;
+ // HDR10PLUS
+ if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI)) {
+ supportedPerFrameMetadata |= HdrMetadata::Type::HDR10PLUS;
+ }
+
+ return supportedPerFrameMetadata;
}
Error Display::getRenderIntents(ColorMode colorMode,
@@ -468,8 +491,7 @@
}
Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
- std::unordered_map<Layer*, LayerRequest>* outLayerRequests)
-{
+ std::unordered_map<HWC2::Layer*, LayerRequest>* outLayerRequests) {
uint32_t intDisplayRequests;
std::vector<Hwc2::Layer> layerIds;
std::vector<uint32_t> layerRequests;
@@ -505,15 +527,8 @@
return Error::None;
}
-Error Display::supportsDoze(bool* outSupport) const
-{
- bool intSupport = false;
- auto intError = mComposer.getDozeSupport(mId, &intSupport);
- auto error = static_cast<Error>(intError);
- if (error != Error::None) {
- return error;
- }
- *outSupport = static_cast<bool>(intSupport);
+Error Display::supportsDoze(bool* outSupport) const {
+ *outSupport = mDisplayCapabilities.count(DisplayCapability::Doze) > 0;
return Error::None;
}
@@ -536,9 +551,28 @@
return Error::None;
}
-Error Display::getReleaseFences(
- std::unordered_map<Layer*, sp<Fence>>* outFences) const
-{
+Error Display::getDisplayedContentSamplingAttributes(PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) const {
+ auto intError = mComposer.getDisplayedContentSamplingAttributes(mId, outFormat, outDataspace,
+ outComponentMask);
+ return static_cast<Error>(intError);
+}
+
+Error Display::setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) const {
+ auto intError =
+ mComposer.setDisplayContentSamplingEnabled(mId, enabled, componentMask, maxFrames);
+ return static_cast<Error>(intError);
+}
+
+Error Display::getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+ android::DisplayedFrameStats* outStats) const {
+ auto intError = mComposer.getDisplayedContentSample(mId, maxFrames, timestamp, outStats);
+ return static_cast<Error>(intError);
+}
+
+Error Display::getReleaseFences(std::unordered_map<HWC2::Layer*, sp<Fence>>* outFences) const {
std::vector<Hwc2::Layer> layerIds;
std::vector<int> fenceFds;
auto intError = mComposer.getReleaseFences(mId, &layerIds, &fenceFds);
@@ -548,7 +582,7 @@
return error;
}
- std::unordered_map<Layer*, sp<Fence>> releaseFences;
+ std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
releaseFences.reserve(numElements);
for (uint32_t element = 0; element < numElements; ++element) {
auto layer = getLayerById(layerIds[element]);
@@ -606,12 +640,6 @@
Error Display::setColorMode(ColorMode mode, RenderIntent renderIntent)
{
- // When the color mode is switched to DISPLAY_P3, we want to boost the GPU frequency
- // so that GPU composition can finish in time. When color mode is switched from
- // DISPLAY_P3, we want to reset GPU frequency.
- const bool expensiveRenderingExpected = (mode == ColorMode::DISPLAY_P3);
- mPowerAdvisor.setExpensiveRenderingExpected(mId, expensiveRenderingExpected);
-
auto intError = mComposer.setColorMode(mId, mode, renderIntent);
return static_cast<Error>(intError);
}
@@ -687,6 +715,11 @@
return error;
}
+Error Display::setDisplayBrightness(float brightness) const {
+ auto intError = mComposer.setDisplayBrightness(mId, brightness);
+ return static_cast<Error>(intError);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
@@ -749,23 +782,28 @@
// Other Display methods
-Layer* Display::getLayerById(hwc2_layer_t id) const
-{
+HWC2::Layer* Display::getLayerById(hwc2_layer_t id) const {
if (mLayers.count(id) == 0) {
return nullptr;
}
return mLayers.at(id).get();
}
+} // namespace impl
// Layer methods
+Layer::~Layer() = default;
+
+namespace impl {
+
Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
hwc2_display_t displayId, hwc2_layer_t layerId)
: mComposer(composer),
mCapabilities(capabilities),
mDisplayId(displayId),
- mId(layerId)
+ mId(layerId),
+ mColorMatrix(android::mat4())
{
ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
}
@@ -777,15 +815,6 @@
ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
" failed: %s (%d)", mDisplayId, mId, to_string(error).c_str(),
intError);
- if (mLayerDestroyedListener) {
- mLayerDestroyedListener(this);
- }
-}
-
-void Layer::setLayerDestroyedListener(std::function<void(Layer*)> listener) {
- LOG_ALWAYS_FATAL_IF(mLayerDestroyedListener && listener,
- "Attempt to set layer destroyed listener multiple times");
- mLayerDestroyedListener = listener;
}
Error Layer::setCursorPosition(int32_t x, int32_t y)
@@ -872,37 +901,49 @@
if (validTypes & HdrMetadata::SMPTE2086) {
perFrameMetadatas.insert(perFrameMetadatas.end(),
{{Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
- mHdrMetadata.smpte2086.displayPrimaryRed.x},
- {Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
- mHdrMetadata.smpte2086.displayPrimaryRed.y},
- {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
- mHdrMetadata.smpte2086.displayPrimaryGreen.x},
- {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
- mHdrMetadata.smpte2086.displayPrimaryGreen.y},
- {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
- mHdrMetadata.smpte2086.displayPrimaryBlue.x},
- {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
- mHdrMetadata.smpte2086.displayPrimaryBlue.y},
- {Hwc2::PerFrameMetadataKey::WHITE_POINT_X,
- mHdrMetadata.smpte2086.whitePoint.x},
- {Hwc2::PerFrameMetadataKey::WHITE_POINT_Y,
- mHdrMetadata.smpte2086.whitePoint.y},
- {Hwc2::PerFrameMetadataKey::MAX_LUMINANCE,
- mHdrMetadata.smpte2086.maxLuminance},
- {Hwc2::PerFrameMetadataKey::MIN_LUMINANCE,
- mHdrMetadata.smpte2086.minLuminance}});
+ mHdrMetadata.smpte2086.displayPrimaryRed.x},
+ {Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ mHdrMetadata.smpte2086.displayPrimaryRed.y},
+ {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ mHdrMetadata.smpte2086.displayPrimaryGreen.x},
+ {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ mHdrMetadata.smpte2086.displayPrimaryGreen.y},
+ {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ mHdrMetadata.smpte2086.displayPrimaryBlue.x},
+ {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ mHdrMetadata.smpte2086.displayPrimaryBlue.y},
+ {Hwc2::PerFrameMetadataKey::WHITE_POINT_X,
+ mHdrMetadata.smpte2086.whitePoint.x},
+ {Hwc2::PerFrameMetadataKey::WHITE_POINT_Y,
+ mHdrMetadata.smpte2086.whitePoint.y},
+ {Hwc2::PerFrameMetadataKey::MAX_LUMINANCE,
+ mHdrMetadata.smpte2086.maxLuminance},
+ {Hwc2::PerFrameMetadataKey::MIN_LUMINANCE,
+ mHdrMetadata.smpte2086.minLuminance}});
}
if (validTypes & HdrMetadata::CTA861_3) {
perFrameMetadatas.insert(perFrameMetadatas.end(),
{{Hwc2::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
- mHdrMetadata.cta8613.maxContentLightLevel},
- {Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
- mHdrMetadata.cta8613.maxFrameAverageLightLevel}});
+ mHdrMetadata.cta8613.maxContentLightLevel},
+ {Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ mHdrMetadata.cta8613.maxFrameAverageLightLevel}});
}
- auto intError = mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas);
- return static_cast<Error>(intError);
+ Error error = static_cast<Error>(
+ mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
+
+ if (validTypes & HdrMetadata::HDR10PLUS) {
+ std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
+ perFrameMetadataBlobs.push_back(
+ {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
+ Error setMetadataBlobsError = static_cast<Error>(
+ mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs));
+ if (error == Error::None) {
+ return setMetadataBlobsError;
+ }
+ }
+ return error;
}
Error Layer::setDisplayFrame(const Rect& frame)
@@ -972,4 +1013,15 @@
return static_cast<Error>(intError);
}
+// Composer HAL 2.3
+Error Layer::setColorTransform(const android::mat4& matrix) {
+ if (matrix == mColorMatrix) {
+ return Error::None;
+ }
+ mColorMatrix = matrix;
+ auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray());
+ return static_cast<Error>(intError);
+}
+
+} // namespace impl
} // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e423167..4209e45 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -37,9 +37,8 @@
#include <unordered_set>
#include <vector>
-#include "PowerAdvisor.h"
-
namespace android {
+ struct DisplayedFrameStats;
class Fence;
class FloatRect;
class GraphicBuffer;
@@ -95,6 +94,9 @@
};
uint32_t getMaxVirtualDisplayCount() const;
+ Error getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+ std::vector<uint8_t>* outData) const;
+
Error createVirtualDisplay(uint32_t width, uint32_t height,
android::ui::PixelFormat* format, Display** outDisplay);
void destroyDisplay(hwc2_display_t displayId);
@@ -121,21 +123,15 @@
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<Capability> mCapabilities;
std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
- android::Hwc2::impl::PowerAdvisor mPowerAdvisor;
bool mRegisteredCallback = false;
};
// Convenience C++ class to access hwc2_device_t Display functions directly.
-class Display
-{
+class Display {
public:
- Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
- const std::unordered_set<Capability>& capabilities,
- hwc2_display_t id, DisplayType type);
- ~Display();
+ virtual ~Display();
- class Config
- {
+ class Config {
public:
class Builder
{
@@ -203,71 +199,139 @@
float mDpiY;
};
- // Required by HWC2
+ virtual hwc2_display_t getId() const = 0;
+ virtual bool isConnected() const = 0;
+ virtual void setConnected(bool connected) = 0; // For use by Device only
+ virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
- [[clang::warn_unused_result]] Error acceptChanges();
- [[clang::warn_unused_result]] Error createLayer(Layer** outLayer);
- [[clang::warn_unused_result]] Error destroyLayer(Layer* layer);
- [[clang::warn_unused_result]] Error getActiveConfig(
- std::shared_ptr<const Config>* outConfig) const;
- [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const;
- [[clang::warn_unused_result]] Error getChangedCompositionTypes(
- std::unordered_map<Layer*, Composition>* outTypes);
- [[clang::warn_unused_result]] Error getColorModes(
- std::vector<android::ui::ColorMode>* outModes) const;
- // outSupportedPerFrameMetadata is an opaque bitmask to the callers
- // but contains HdrMetadata::Type::*.
- [[clang::warn_unused_result]] Error getSupportedPerFrameMetadata(
- int32_t* outSupportedPerFrameMetadata) const;
- [[clang::warn_unused_result]] Error getRenderIntents(
+ [[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
+ [[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
+ [[clang::warn_unused_result]] virtual Error destroyLayer(Layer* layer) = 0;
+ [[clang::warn_unused_result]] virtual Error getActiveConfig(
+ std::shared_ptr<const Config>* outConfig) const = 0;
+ [[clang::warn_unused_result]] virtual Error getActiveConfigIndex(int* outIndex) const = 0;
+ [[clang::warn_unused_result]] virtual Error getChangedCompositionTypes(
+ std::unordered_map<Layer*, Composition>* outTypes) = 0;
+ [[clang::warn_unused_result]] virtual Error getColorModes(
+ std::vector<android::ui::ColorMode>* outModes) const = 0;
+ // Returns a bitmask which contains HdrMetadata::Type::*.
+ [[clang::warn_unused_result]] virtual int32_t getSupportedPerFrameMetadata() const = 0;
+ [[clang::warn_unused_result]] virtual Error getRenderIntents(
android::ui::ColorMode colorMode,
- std::vector<android::ui::RenderIntent>* outRenderIntents) const;
- [[clang::warn_unused_result]] Error getDataspaceSaturationMatrix(
- android::ui::Dataspace dataspace, android::mat4* outMatrix);
+ std::vector<android::ui::RenderIntent>* outRenderIntents) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDataspaceSaturationMatrix(
+ android::ui::Dataspace dataspace, android::mat4* outMatrix) = 0;
+
+ // Doesn't call into the HWC2 device, so no Errors are possible
+ virtual std::vector<std::shared_ptr<const Config>> getConfigs() const = 0;
+
+ [[clang::warn_unused_result]] virtual Error getName(std::string* outName) const = 0;
+ [[clang::warn_unused_result]] virtual Error getRequests(
+ DisplayRequest* outDisplayRequests,
+ std::unordered_map<Layer*, LayerRequest>* outLayerRequests) = 0;
+ [[clang::warn_unused_result]] virtual Error getType(DisplayType* outType) const = 0;
+ [[clang::warn_unused_result]] virtual Error supportsDoze(bool* outSupport) const = 0;
+ [[clang::warn_unused_result]] virtual Error getHdrCapabilities(
+ android::HdrCapabilities* outCapabilities) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDisplayedContentSamplingAttributes(
+ android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const = 0;
+ [[clang::warn_unused_result]] virtual Error setDisplayContentSamplingEnabled(
+ bool enabled, uint8_t componentMask, uint64_t maxFrames) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDisplayedContentSample(
+ uint64_t maxFrames, uint64_t timestamp,
+ android::DisplayedFrameStats* outStats) const = 0;
+ [[clang::warn_unused_result]] virtual Error getReleaseFences(
+ std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
+ [[clang::warn_unused_result]] virtual Error present(
+ android::sp<android::Fence>* outPresentFence) = 0;
+ [[clang::warn_unused_result]] virtual Error setActiveConfig(
+ const std::shared_ptr<const Config>& config) = 0;
+ [[clang::warn_unused_result]] virtual Error setClientTarget(
+ uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+ const android::sp<android::Fence>& acquireFence, android::ui::Dataspace dataspace) = 0;
+ [[clang::warn_unused_result]] virtual Error setColorMode(
+ android::ui::ColorMode mode, android::ui::RenderIntent renderIntent) = 0;
+ [[clang::warn_unused_result]] virtual Error setColorTransform(
+ const android::mat4& matrix, android_color_transform_t hint) = 0;
+ [[clang::warn_unused_result]] virtual Error setOutputBuffer(
+ const android::sp<android::GraphicBuffer>& buffer,
+ const android::sp<android::Fence>& releaseFence) = 0;
+ [[clang::warn_unused_result]] virtual Error setPowerMode(PowerMode mode) = 0;
+ [[clang::warn_unused_result]] virtual Error setVsyncEnabled(Vsync enabled) = 0;
+ [[clang::warn_unused_result]] virtual Error validate(uint32_t* outNumTypes,
+ uint32_t* outNumRequests) = 0;
+ [[clang::warn_unused_result]] virtual Error presentOrValidate(
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
+ [[clang::warn_unused_result]] virtual Error setDisplayBrightness(float brightness) const = 0;
+};
+
+namespace impl {
+
+class Display : public HWC2::Display {
+public:
+ Display(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
+ hwc2_display_t id, DisplayType type);
+ ~Display() override;
+
+ // Required by HWC2
+ Error acceptChanges() override;
+ Error createLayer(Layer** outLayer) override;
+ Error destroyLayer(Layer* layer) override;
+ Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
+ Error getActiveConfigIndex(int* outIndex) const override;
+ Error getChangedCompositionTypes(std::unordered_map<Layer*, Composition>* outTypes) override;
+ Error getColorModes(std::vector<android::ui::ColorMode>* outModes) const override;
+ // Returns a bitmask which contains HdrMetadata::Type::*.
+ int32_t getSupportedPerFrameMetadata() const override;
+ Error getRenderIntents(android::ui::ColorMode colorMode,
+ std::vector<android::ui::RenderIntent>* outRenderIntents) const override;
+ Error getDataspaceSaturationMatrix(android::ui::Dataspace dataspace,
+ android::mat4* outMatrix) override;
// Doesn't call into the HWC2 device, so no errors are possible
- std::vector<std::shared_ptr<const Config>> getConfigs() const;
+ std::vector<std::shared_ptr<const Config>> getConfigs() const override;
- [[clang::warn_unused_result]] Error getName(std::string* outName) const;
- [[clang::warn_unused_result]] Error getRequests(
- DisplayRequest* outDisplayRequests,
- std::unordered_map<Layer*, LayerRequest>* outLayerRequests);
- [[clang::warn_unused_result]] Error getType(DisplayType* outType) const;
- [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
- [[clang::warn_unused_result]] Error getHdrCapabilities(
- android::HdrCapabilities* outCapabilities) const;
- [[clang::warn_unused_result]] Error getReleaseFences(
- std::unordered_map<Layer*,
- android::sp<android::Fence>>* outFences) const;
- [[clang::warn_unused_result]] Error present(
- android::sp<android::Fence>* outPresentFence);
- [[clang::warn_unused_result]] Error setActiveConfig(
- const std::shared_ptr<const Config>& config);
- [[clang::warn_unused_result]] Error setClientTarget(
- uint32_t slot, const android::sp<android::GraphicBuffer>& target,
- const android::sp<android::Fence>& acquireFence,
- android::ui::Dataspace dataspace);
- [[clang::warn_unused_result]] Error setColorMode(
- android::ui::ColorMode mode,
- android::ui::RenderIntent renderIntent);
- [[clang::warn_unused_result]] Error setColorTransform(
- const android::mat4& matrix, android_color_transform_t hint);
- [[clang::warn_unused_result]] Error setOutputBuffer(
- const android::sp<android::GraphicBuffer>& buffer,
- const android::sp<android::Fence>& releaseFence);
- [[clang::warn_unused_result]] Error setPowerMode(PowerMode mode);
- [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
- [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
- uint32_t* outNumRequests);
- [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
- uint32_t* outNumRequests,
- android::sp<android::Fence>* outPresentFence, uint32_t* state);
+ Error getName(std::string* outName) const override;
+ Error getRequests(DisplayRequest* outDisplayRequests,
+ std::unordered_map<Layer*, LayerRequest>* outLayerRequests) override;
+ Error getType(DisplayType* outType) const override;
+ Error supportsDoze(bool* outSupport) const override;
+ Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+ Error getDisplayedContentSamplingAttributes(android::ui::PixelFormat* outFormat,
+ android::ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const override;
+ Error setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) const override;
+ Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+ android::DisplayedFrameStats* outStats) const override;
+ Error getReleaseFences(
+ std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
+ Error present(android::sp<android::Fence>* outPresentFence) override;
+ Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
+ Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+ const android::sp<android::Fence>& acquireFence,
+ android::ui::Dataspace dataspace) override;
+ Error setColorMode(android::ui::ColorMode mode,
+ android::ui::RenderIntent renderIntent) override;
+ Error setColorTransform(const android::mat4& matrix, android_color_transform_t hint) override;
+ Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+ const android::sp<android::Fence>& releaseFence) override;
+ Error setPowerMode(PowerMode mode) override;
+ Error setVsyncEnabled(Vsync enabled) override;
+ Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
+ Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+ android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
+ Error setDisplayBrightness(float brightness) const override;
// Other Display methods
-
- hwc2_display_t getId() const { return mId; }
- bool isConnected() const { return mIsConnected; }
- void setConnected(bool connected); // For use by Device only
+ hwc2_display_t getId() const override { return mId; }
+ bool isConnected() const override { return mIsConnected; }
+ void setConnected(bool connected) override; // For use by Device only
+ const std::unordered_set<DisplayCapability>& getCapabilities() const override {
+ return mDisplayCapabilities;
+ };
private:
int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -286,7 +350,6 @@
// this HWC2::Display, so these references are guaranteed to be valid for
// the lifetime of this object.
android::Hwc2::Composer& mComposer;
- android::Hwc2::PowerAdvisor& mPowerAdvisor;
const std::unordered_set<Capability>& mCapabilities;
hwc2_display_t mId;
@@ -294,51 +357,77 @@
DisplayType mType;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+ std::unordered_set<DisplayCapability> mDisplayCapabilities;
+};
+} // namespace impl
+
+class Layer {
+public:
+ virtual ~Layer();
+
+ virtual hwc2_layer_t getId() const = 0;
+
+ [[clang::warn_unused_result]] virtual Error setCursorPosition(int32_t x, int32_t y) = 0;
+ [[clang::warn_unused_result]] virtual Error setBuffer(
+ uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
+ const android::sp<android::Fence>& acquireFence) = 0;
+ [[clang::warn_unused_result]] virtual Error setSurfaceDamage(const android::Region& damage) = 0;
+
+ [[clang::warn_unused_result]] virtual Error setBlendMode(BlendMode mode) = 0;
+ [[clang::warn_unused_result]] virtual Error setColor(hwc_color_t color) = 0;
+ [[clang::warn_unused_result]] virtual Error setCompositionType(Composition type) = 0;
+ [[clang::warn_unused_result]] virtual Error setDataspace(android::ui::Dataspace dataspace) = 0;
+ [[clang::warn_unused_result]] virtual Error setPerFrameMetadata(
+ const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) = 0;
+ [[clang::warn_unused_result]] virtual Error setDisplayFrame(const android::Rect& frame) = 0;
+ [[clang::warn_unused_result]] virtual Error setPlaneAlpha(float alpha) = 0;
+ [[clang::warn_unused_result]] virtual Error setSidebandStream(
+ const native_handle_t* stream) = 0;
+ [[clang::warn_unused_result]] virtual Error setSourceCrop(const android::FloatRect& crop) = 0;
+ [[clang::warn_unused_result]] virtual Error setTransform(Transform transform) = 0;
+ [[clang::warn_unused_result]] virtual Error setVisibleRegion(const android::Region& region) = 0;
+ [[clang::warn_unused_result]] virtual Error setZOrder(uint32_t z) = 0;
+ [[clang::warn_unused_result]] virtual Error setInfo(uint32_t type, uint32_t appId) = 0;
+
+ // Composer HAL 2.3
+ [[clang::warn_unused_result]] virtual Error setColorTransform(const android::mat4& matrix) = 0;
};
+namespace impl {
+
// Convenience C++ class to access hwc2_device_t Layer functions directly.
-class Layer
-{
+
+class Layer : public HWC2::Layer {
public:
Layer(android::Hwc2::Composer& composer,
const std::unordered_set<Capability>& capabilities,
hwc2_display_t displayId, hwc2_layer_t layerId);
- ~Layer();
+ ~Layer() override;
- hwc2_layer_t getId() const { return mId; }
+ hwc2_layer_t getId() const override { return mId; }
- // Register a listener to be notified when the layer is destroyed. When the
- // listener function is called, the Layer will be in the process of being
- // destroyed, so it's not safe to call methods on it.
- void setLayerDestroyedListener(std::function<void(Layer*)> listener);
+ Error setCursorPosition(int32_t x, int32_t y) override;
+ Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
+ const android::sp<android::Fence>& acquireFence) override;
+ Error setSurfaceDamage(const android::Region& damage) override;
- [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y);
- [[clang::warn_unused_result]] Error setBuffer(uint32_t slot,
- const android::sp<android::GraphicBuffer>& buffer,
- const android::sp<android::Fence>& acquireFence);
- [[clang::warn_unused_result]] Error setSurfaceDamage(
- const android::Region& damage);
+ Error setBlendMode(BlendMode mode) override;
+ Error setColor(hwc_color_t color) override;
+ Error setCompositionType(Composition type) override;
+ Error setDataspace(android::ui::Dataspace dataspace) override;
+ Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
+ const android::HdrMetadata& metadata) override;
+ Error setDisplayFrame(const android::Rect& frame) override;
+ Error setPlaneAlpha(float alpha) override;
+ Error setSidebandStream(const native_handle_t* stream) override;
+ Error setSourceCrop(const android::FloatRect& crop) override;
+ Error setTransform(Transform transform) override;
+ Error setVisibleRegion(const android::Region& region) override;
+ Error setZOrder(uint32_t z) override;
+ Error setInfo(uint32_t type, uint32_t appId) override;
- [[clang::warn_unused_result]] Error setBlendMode(BlendMode mode);
- [[clang::warn_unused_result]] Error setColor(hwc_color_t color);
- [[clang::warn_unused_result]] Error setCompositionType(Composition type);
- [[clang::warn_unused_result]] Error setDataspace(
- android::ui::Dataspace dataspace);
- [[clang::warn_unused_result]] Error setPerFrameMetadata(
- const int32_t supportedPerFrameMetadata,
- const android::HdrMetadata& metadata);
- [[clang::warn_unused_result]] Error setDisplayFrame(
- const android::Rect& frame);
- [[clang::warn_unused_result]] Error setPlaneAlpha(float alpha);
- [[clang::warn_unused_result]] Error setSidebandStream(
- const native_handle_t* stream);
- [[clang::warn_unused_result]] Error setSourceCrop(
- const android::FloatRect& crop);
- [[clang::warn_unused_result]] Error setTransform(Transform transform);
- [[clang::warn_unused_result]] Error setVisibleRegion(
- const android::Region& region);
- [[clang::warn_unused_result]] Error setZOrder(uint32_t z);
- [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId);
+ // Composer HAL 2.3
+ Error setColorTransform(const android::mat4& matrix) override;
private:
// These are references to data owned by HWC2::Device, which will outlive
@@ -351,9 +440,11 @@
hwc2_layer_t mId;
android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN;
android::HdrMetadata mHdrMetadata;
- std::function<void(Layer*)> mLayerDestroyedListener;
+ android::mat4 mColorMatrix;
};
+} // namespace impl
+
} // namespace HWC2
#endif // ANDROID_SF_HWC2_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f5f7a82..1099041 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -20,32 +20,14 @@
#define LOG_TAG "HWComposer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/misc.h>
-#include <utils/NativeHandle.h>
-#include <utils/String8.h>
-#include <utils/Thread.h>
-#include <utils/Trace.h>
-#include <utils/Vector.h>
-
+#include <compositionengine/Output.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <log/log.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
-
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <android/configuration.h>
-
-#include <cutils/properties.h>
-#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/Trace.h>
#include "HWComposer.h"
#include "HWC2.h"
@@ -54,16 +36,19 @@
#include "../Layer.h" // needed only for debugging
#include "../SurfaceFlinger.h"
-#define LOG_DISPLAY_ERROR(displayId, msg) \
- ALOGE("%s failed for display %d: %s", __FUNCTION__, displayId, msg)
+#define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \
+ ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg)
-#define LOG_HWC_ERROR(what, error, displayId) \
- ALOGE("%s: %s failed for display %d: %s (%d)", __FUNCTION__, what, displayId, \
- to_string(error).c_str(), static_cast<int32_t>(error))
+#define LOG_DISPLAY_ERROR(displayId, msg) \
+ ALOGE("%s failed for display %s: %s", __FUNCTION__, to_string(displayId).c_str(), msg)
+
+#define LOG_HWC_ERROR(what, error, displayId) \
+ ALOGE("%s: %s failed for display %s: %s (%d)", __FUNCTION__, what, \
+ to_string(displayId).c_str(), to_string(error).c_str(), static_cast<int32_t>(error))
#define RETURN_IF_INVALID_DISPLAY(displayId, ...) \
do { \
- if (!isValidDisplay(displayId)) { \
+ if (mDisplayData.count(displayId) == 0) { \
LOG_DISPLAY_ERROR(displayId, "Invalid display"); \
return __VA_ARGS__; \
} \
@@ -82,28 +67,50 @@
namespace android {
-#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION
+HWComposer::~HWComposer() = default;
-// ---------------------------------------------------------------------------
+namespace impl {
-HWComposer::HWComposer(std::unique_ptr<android::Hwc2::Composer> composer)
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
: mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
-HWComposer::~HWComposer() = default;
+HWComposer::~HWComposer() {
+ mDisplayData.clear();
+}
void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
int32_t sequenceId) {
mHwcDevice->registerCallback(callback, sequenceId);
}
+bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+ DisplayIdentificationData* outData) const {
+ const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+ if (error != HWC2::Error::None) {
+ if (error != HWC2::Error::Unsupported) {
+ LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
+ }
+ return false;
+ }
+ return true;
+}
+
bool HWComposer::hasCapability(HWC2::Capability capability) const
{
return mHwcDevice->getCapabilities().count(capability) > 0;
}
-bool HWComposer::isValidDisplay(int32_t displayId) const {
- return static_cast<size_t>(displayId) < mDisplayData.size() &&
- mDisplayData[displayId].hwcDisplay;
+bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
+ HWC2::DisplayCapability capability) const {
+ if (!displayId) {
+ // Checkout global capabilities for displays without a corresponding HWC display.
+ if (capability == HWC2::DisplayCapability::SkipClientColorTransform) {
+ return hasCapability(HWC2::Capability::SkipClientColorTransform);
+ }
+ return false;
+ }
+ RETURN_IF_INVALID_DISPLAY(*displayId, false);
+ return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0;
}
void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) {
@@ -131,126 +138,115 @@
}
}
-void HWComposer::onHotplug(hwc2_display_t displayId, int32_t displayType,
- HWC2::Connection connection) {
- if (displayType >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
- ALOGE("Invalid display type of %d", displayType);
- return;
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
+ HWC2::Connection connection) {
+ std::optional<DisplayIdentificationInfo> info;
+
+ if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+ info = DisplayIdentificationInfo{*displayId, std::string()};
+ } else {
+ if (connection == HWC2::Connection::Disconnected) {
+ ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+ return {};
+ }
+
+ info = onHotplugConnect(hwcDisplayId);
+ if (!info) return {};
}
- ALOGV("hotplug: %" PRIu64 ", %s %s", displayId,
- displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external",
- to_string(connection).c_str());
- mHwcDevice->onHotplug(displayId, connection);
+ ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
+ hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
+ to_string(info->id).c_str(), hwcDisplayId);
+
+ mHwcDevice->onHotplug(hwcDisplayId, connection);
+
// Disconnect is handled through HWComposer::disconnectDisplay via
// SurfaceFlinger's onHotplugReceived callback handling
if (connection == HWC2::Connection::Connected) {
- mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(displayId);
- mHwcDisplaySlots[displayId] = displayType;
+ mDisplayData[info->id].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId);
+ mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
}
+
+ return info;
}
-bool HWComposer::onVsync(hwc2_display_t displayId, int64_t timestamp,
- int32_t* outDisplay) {
- auto display = mHwcDevice->getDisplayById(displayId);
- if (!display) {
- ALOGE("onVsync Failed to find display %" PRIu64, displayId);
- return false;
- }
- auto displayType = HWC2::DisplayType::Invalid;
- auto error = display->getType(&displayType);
- if (error != HWC2::Error::None) {
- ALOGE("onVsync: Failed to determine type of display %" PRIu64,
- display->getId());
+bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) {
+ const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+ if (!displayId) {
+ LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
return false;
}
- if (displayType == HWC2::DisplayType::Virtual) {
- ALOGE("Virtual display %" PRIu64 " passed to vsync callback",
- display->getId());
+ RETURN_IF_INVALID_DISPLAY(*displayId, false);
+
+ auto& displayData = mDisplayData[*displayId];
+ if (displayData.isVirtual) {
+ LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
return false;
}
- if (mHwcDisplaySlots.count(display->getId()) == 0) {
- ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback",
- display->getId());
- return false;
- }
-
- int32_t disp = mHwcDisplaySlots[display->getId()];
{
- Mutex::Autolock _l(mLock);
+ std::lock_guard lock(displayData.lastHwVsyncLock);
// There have been reports of HWCs that signal several vsync events
// with the same timestamp when turning the display off and on. This
// is a bug in the HWC implementation, but filter the extra events
// out here so they don't cause havoc downstream.
- if (timestamp == mLastHwVSync[disp]) {
- ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
- timestamp);
+ if (timestamp == displayData.lastHwVsync) {
+ ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
+ to_string(*displayId).c_str(), timestamp);
return false;
}
- mLastHwVSync[disp] = timestamp;
+ displayData.lastHwVsync = timestamp;
}
- if (outDisplay) {
- *outDisplay = disp;
- }
-
- char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
- ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
+ const auto tag = "HW_VSYNC_" + to_string(*displayId);
+ ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);
+ displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;
return true;
}
-status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format, int32_t *outId) {
+std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
+ ui::PixelFormat* format) {
if (mRemainingHwcVirtualDisplays == 0) {
- ALOGE("allocateVirtualDisplay: No remaining virtual displays");
- return NO_MEMORY;
+ ALOGE("%s: No remaining virtual displays", __FUNCTION__);
+ return {};
}
if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
(width > SurfaceFlinger::maxVirtualDisplaySize ||
height > SurfaceFlinger::maxVirtualDisplaySize)) {
- ALOGE("createVirtualDisplay: Can't create a virtual display with"
- " a dimension > %" PRIu64 " (tried %u x %u)",
- SurfaceFlinger::maxVirtualDisplaySize, width, height);
- return INVALID_OPERATION;
+ ALOGE("%s: Display size %ux%u exceeds maximum dimension of %" PRIu64, __FUNCTION__, width,
+ height, SurfaceFlinger::maxVirtualDisplaySize);
+ return {};
}
-
HWC2::Display* display;
auto error = mHwcDevice->createVirtualDisplay(width, height, format,
&display);
if (error != HWC2::Error::None) {
- ALOGE("allocateVirtualDisplay: Failed to create HWC virtual display");
- return NO_MEMORY;
+ ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
+ return {};
}
- size_t displaySlot = 0;
- if (!mFreeDisplaySlots.empty()) {
- displaySlot = *mFreeDisplaySlots.begin();
- mFreeDisplaySlots.erase(displaySlot);
- } else if (mDisplayData.size() < INT32_MAX) {
- // Don't bother allocating a slot larger than we can return
- displaySlot = mDisplayData.size();
- mDisplayData.resize(displaySlot + 1);
+ DisplayId displayId;
+ if (mFreeVirtualDisplayIds.empty()) {
+ displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
} else {
- ALOGE("allocateVirtualDisplay: Unable to allocate a display slot");
- return NO_MEMORY;
+ displayId = *mFreeVirtualDisplayIds.begin();
+ mFreeVirtualDisplayIds.erase(displayId);
}
- mDisplayData[displaySlot].hwcDisplay = display;
+ auto& displayData = mDisplayData[displayId];
+ displayData.hwcDisplay = display;
+ displayData.isVirtual = true;
--mRemainingHwcVirtualDisplays;
- *outId = static_cast<int32_t>(displaySlot);
-
- return NO_ERROR;
+ return displayId;
}
-HWC2::Layer* HWComposer::createLayer(int32_t displayId) {
+HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
auto display = mDisplayData[displayId].hwcDisplay;
@@ -260,7 +256,7 @@
return layer;
}
-void HWComposer::destroyLayer(int32_t displayId, HWC2::Layer* layer) {
+void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto display = mDisplayData[displayId].hwcDisplay;
@@ -268,27 +264,29 @@
RETURN_IF_HWC_ERROR(error, displayId);
}
-nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) const {
+nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
+ RETURN_IF_INVALID_DISPLAY(displayId, 0);
+ const auto& displayData = mDisplayData.at(displayId);
// this returns the last refresh timestamp.
// if the last one is not available, we estimate it based on
// the refresh period and whatever closest timestamp we have.
- Mutex::Autolock _l(mLock);
+ std::lock_guard lock(displayData.lastHwVsyncLock);
nsecs_t now = systemTime(CLOCK_MONOTONIC);
auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
- return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
+ return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
}
-bool HWComposer::isConnected(int32_t displayId) const {
+bool HWComposer::isConnected(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
- return mDisplayData[displayId].hwcDisplay->isConnected();
+ return mDisplayData.at(displayId).hwcDisplay->isConnected();
}
-std::vector<std::shared_ptr<const HWC2::Display::Config>>
- HWComposer::getConfigs(int32_t displayId) const {
+std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
+ DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
- auto& displayData = mDisplayData[displayId];
- auto configs = mDisplayData[displayId].hwcDisplay->getConfigs();
+ const auto& displayData = mDisplayData.at(displayId);
+ auto configs = displayData.hwcDisplay->getConfigs();
if (displayData.configMap.empty()) {
for (size_t i = 0; i < configs.size(); ++i) {
displayData.configMap[i] = configs[i];
@@ -297,12 +295,12 @@
return configs;
}
-std::shared_ptr<const HWC2::Display::Config>
- HWComposer::getActiveConfig(int32_t displayId) const {
+std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
+ DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
std::shared_ptr<const HWC2::Display::Config> config;
- auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config);
+ auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfig(&config);
if (error == HWC2::Error::BadConfig) {
LOG_DISPLAY_ERROR(displayId, "No active config");
return nullptr;
@@ -318,39 +316,37 @@
return config;
}
-int HWComposer::getActiveConfigIndex(int32_t displayId) const {
- if (!isValidDisplay(displayId)) {
- ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId);
+int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
+ RETURN_IF_INVALID_DISPLAY(displayId, -1);
+
+ int index;
+ auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfigIndex(&index);
+ if (error == HWC2::Error::BadConfig) {
+ LOG_DISPLAY_ERROR(displayId, "No active config");
return -1;
}
- int index;
- auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index);
- if (error == HWC2::Error::BadConfig) {
- ALOGE("getActiveConfigIndex: No config active, returning -1");
- return -1;
- } else if (error != HWC2::Error::None) {
- ALOGE("getActiveConfigIndex failed for display %d: %s (%d)", displayId,
- to_string(error).c_str(), static_cast<int32_t>(error));
- return -1;
- } else if (index < 0) {
- ALOGE("getActiveConfigIndex returned an unknown config for display %d", displayId);
+
+ RETURN_IF_HWC_ERROR(error, displayId, -1);
+
+ if (index < 0) {
+ LOG_DISPLAY_ERROR(displayId, "Unknown config");
return -1;
}
return index;
}
-std::vector<ui::ColorMode> HWComposer::getColorModes(int32_t displayId) const {
+std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
std::vector<ui::ColorMode> modes;
- auto error = mDisplayData[displayId].hwcDisplay->getColorModes(&modes);
+ auto error = mDisplayData.at(displayId).hwcDisplay->getColorModes(&modes);
RETURN_IF_HWC_ERROR(error, displayId, {});
return modes;
}
-status_t HWComposer::setActiveColorMode(int32_t displayId, ui::ColorMode mode,
- ui::RenderIntent renderIntent) {
+status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+ ui::RenderIntent renderIntent) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -363,56 +359,49 @@
return NO_ERROR;
}
+void HWComposer::setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) {
+ RETURN_IF_INVALID_DISPLAY(displayId);
+ auto& displayData = mDisplayData[displayId];
-void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
- if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
- ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
+ if (displayData.isVirtual) {
+ LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
return;
}
- RETURN_IF_INVALID_DISPLAY(displayId);
-
// NOTE: we use our own internal lock here because we have to call
// into the HWC with the lock held, and we want to make sure
// that even if HWC blocks (which it shouldn't), it won't
// affect other threads.
- Mutex::Autolock _l(mVsyncLock);
- auto& displayData = mDisplayData[displayId];
- if (enabled != displayData.vsyncEnabled) {
- ATRACE_CALL();
- auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
- RETURN_IF_HWC_ERROR(error, displayId);
-
- displayData.vsyncEnabled = enabled;
-
- char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
- ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
+ std::lock_guard lock(displayData.vsyncEnabledLock);
+ if (enabled == displayData.vsyncEnabled) {
+ return;
}
+
+ ATRACE_CALL();
+ auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
+ RETURN_IF_HWC_ERROR(error, displayId);
+
+ displayData.vsyncEnabled = enabled;
+
+ const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
+ ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
}
-status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
- ui::Dataspace dataspace) {
+status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
+ const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
+ ui::Dataspace dataspace) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- ALOGV("setClientTarget for display %d", displayId);
+ ALOGV("%s for display %s", __FUNCTION__, to_string(displayId).c_str());
auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace);
RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
return NO_ERROR;
}
-status_t HWComposer::prepare(DisplayDevice& displayDevice) {
+status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Output& output) {
ATRACE_CALL();
- Mutex::Autolock _l(mDisplayLock);
- auto displayId = displayDevice.getHwcDisplayId();
- if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
- ALOGV("Skipping HWComposer prepare for non-HWC display");
- return NO_ERROR;
- }
-
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -436,7 +425,7 @@
// back to validate when there is any client layer.
displayData.validateWasSkipped = false;
if (!displayData.hasClientComposition) {
- sp<android::Fence> outPresentFence;
+ sp<Fence> outPresentFence;
uint32_t state = UINT32_MAX;
error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
if (error != HWC2::Error::HasChanges) {
@@ -474,42 +463,42 @@
displayData.hasClientComposition = false;
displayData.hasDeviceComposition = false;
- for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) {
- auto hwcLayer = layer->getHwcLayer(displayId);
+ for (auto& outputLayer : output.getOutputLayersOrderedByZ()) {
+ auto& state = outputLayer->editState();
+ LOG_FATAL_IF(!state.hwc.);
+ auto hwcLayer = (*state.hwc).hwcLayer;
- if (changedTypes.count(hwcLayer) != 0) {
- // We pass false so we only update our state and don't call back
- // into the HWC device
- validateChange(layer->getCompositionType(displayId),
- changedTypes[hwcLayer]);
- layer->setCompositionType(displayId, changedTypes[hwcLayer], false);
+ if (auto it = changedTypes.find(hwcLayer.get()); it != changedTypes.end()) {
+ auto newCompositionType = it->second;
+ validateChange(static_cast<HWC2::Composition>((*state.hwc).hwcCompositionType),
+ newCompositionType);
+ (*state.hwc).hwcCompositionType =
+ static_cast<Hwc2::IComposerClient::Composition>(newCompositionType);
}
- switch (layer->getCompositionType(displayId)) {
- case HWC2::Composition::Client:
+ switch ((*state.hwc).hwcCompositionType) {
+ case Hwc2::IComposerClient::Composition::CLIENT:
displayData.hasClientComposition = true;
break;
- case HWC2::Composition::Device:
- case HWC2::Composition::SolidColor:
- case HWC2::Composition::Cursor:
- case HWC2::Composition::Sideband:
+ case Hwc2::IComposerClient::Composition::DEVICE:
+ case Hwc2::IComposerClient::Composition::SOLID_COLOR:
+ case Hwc2::IComposerClient::Composition::CURSOR:
+ case Hwc2::IComposerClient::Composition::SIDEBAND:
displayData.hasDeviceComposition = true;
break;
default:
break;
}
- if (layerRequests.count(hwcLayer) != 0 &&
- layerRequests[hwcLayer] ==
- HWC2::LayerRequest::ClearClientTarget) {
- layer->setClearClientTarget(displayId, true);
- } else {
- if (layerRequests.count(hwcLayer) != 0) {
+ state.clearClientTarget = false;
+ if (auto it = layerRequests.find(hwcLayer.get()); it != layerRequests.end()) {
+ auto request = it->second;
+ if (request == HWC2::LayerRequest::ClearClientTarget) {
+ state.clearClientTarget = true;
+ } else {
LOG_DISPLAY_ERROR(displayId,
- ("Unknown layer request " + to_string(layerRequests[hwcLayer]))
- .c_str());
+ ("Unknown layer request " + to_string(request)).c_str());
}
- layer->setClearClientTarget(displayId, false);
}
}
@@ -519,49 +508,48 @@
return NO_ERROR;
}
-bool HWComposer::hasDeviceComposition(int32_t displayId) const {
- if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+bool HWComposer::hasDeviceComposition(const std::optional<DisplayId>& displayId) const {
+ if (!displayId) {
// Displays without a corresponding HWC display are never composed by
// the device
return false;
}
- RETURN_IF_INVALID_DISPLAY(displayId, false);
- return mDisplayData[displayId].hasDeviceComposition;
+ RETURN_IF_INVALID_DISPLAY(*displayId, false);
+ return mDisplayData.at(*displayId).hasDeviceComposition;
}
-bool HWComposer::hasFlipClientTargetRequest(int32_t displayId) const {
- if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+bool HWComposer::hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const {
+ if (!displayId) {
// Displays without a corresponding HWC display are never composed by
// the device
return false;
}
- RETURN_IF_INVALID_DISPLAY(displayId, false);
- return ((static_cast<uint32_t>(mDisplayData[displayId].displayRequests) &
+ RETURN_IF_INVALID_DISPLAY(*displayId, false);
+ return ((static_cast<uint32_t>(mDisplayData.at(*displayId).displayRequests) &
static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0);
}
-bool HWComposer::hasClientComposition(int32_t displayId) const {
- if (displayId == DisplayDevice::DISPLAY_ID_INVALID) {
+bool HWComposer::hasClientComposition(const std::optional<DisplayId>& displayId) const {
+ if (!displayId) {
// Displays without a corresponding HWC display are always composed by
// the client
return true;
}
- RETURN_IF_INVALID_DISPLAY(displayId, true);
- return mDisplayData[displayId].hasClientComposition;
+ RETURN_IF_INVALID_DISPLAY(*displayId, true);
+ return mDisplayData.at(*displayId).hasClientComposition;
}
-sp<Fence> HWComposer::getPresentFence(int32_t displayId) const {
+sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
- return mDisplayData[displayId].lastPresentFence;
+ return mDisplayData.at(displayId).lastPresentFence;
}
-sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
- HWC2::Layer* layer) const {
+sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
- auto displayFences = mDisplayData[displayId].releaseFences;
+ auto displayFences = mDisplayData.at(displayId).releaseFences;
if (displayFences.count(layer) == 0) {
ALOGV("getLayerReleaseFence: Release fence not found");
return Fence::NO_FENCE;
@@ -569,7 +557,7 @@
return displayFences[layer];
}
-status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) {
+status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
ATRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -597,11 +585,11 @@
return NO_ERROR;
}
-status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) {
- ALOGV("setPowerMode(%d, %d)", displayId, intMode);
+status_t HWComposer::setPowerMode(DisplayId displayId, int32_t intMode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- if (displayId >= VIRTUAL_DISPLAY_ID_BASE) {
+ const auto& displayData = mDisplayData[displayId];
+ if (displayData.isVirtual) {
LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
return INVALID_OPERATION;
}
@@ -611,7 +599,7 @@
setVsyncEnabled(displayId, HWC2::Vsync::Disable);
}
- auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto& hwcDisplay = displayData.hwcDisplay;
switch (mode) {
case HWC2::PowerMode::Off:
case HWC2::PowerMode::On:
@@ -653,7 +641,7 @@
return NO_ERROR;
}
-status_t HWComposer::setActiveConfig(int32_t displayId, size_t configId) {
+status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -667,8 +655,7 @@
return NO_ERROR;
}
-status_t HWComposer::setColorTransform(int32_t displayId,
- const mat4& transform) {
+status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -680,54 +667,52 @@
return NO_ERROR;
}
-void HWComposer::disconnectDisplay(int displayId) {
- LOG_ALWAYS_FATAL_IF(displayId < 0);
+void HWComposer::disconnectDisplay(DisplayId displayId) {
+ RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
- auto displayType = HWC2::DisplayType::Invalid;
- auto error = displayData.hwcDisplay->getType(&displayType);
- RETURN_IF_HWC_ERROR_FOR("getType", error, displayId);
-
// If this was a virtual display, add its slot back for reuse by future
// virtual displays
- if (displayType == HWC2::DisplayType::Virtual) {
- mFreeDisplaySlots.insert(displayId);
+ if (displayData.isVirtual) {
+ mFreeVirtualDisplayIds.insert(displayId);
++mRemainingHwcVirtualDisplays;
}
- auto hwcId = displayData.hwcDisplay->getId();
- mHwcDisplaySlots.erase(hwcId);
- displayData.reset();
+ const auto hwcDisplayId = displayData.hwcDisplay->getId();
+ mPhysicalDisplayIdMap.erase(hwcDisplayId);
+ mDisplayData.erase(displayId);
- mHwcDevice->destroyDisplay(hwcId);
+ // TODO(b/74619554): Select internal/external display from remaining displays.
+ if (hwcDisplayId == mInternalHwcDisplayId) {
+ mInternalHwcDisplayId.reset();
+ } else if (hwcDisplayId == mExternalHwcDisplayId) {
+ mExternalHwcDisplayId.reset();
+ }
+
+ mHwcDevice->destroyDisplay(hwcDisplayId);
}
-status_t HWComposer::setOutputBuffer(int32_t displayId,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) {
+status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& buffer) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto& displayData = mDisplayData[displayId];
- auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
- auto displayType = HWC2::DisplayType::Invalid;
- auto error = hwcDisplay->getType(&displayType);
- RETURN_IF_HWC_ERROR_FOR("getType", error, displayId, NAME_NOT_FOUND);
-
- if (displayType != HWC2::DisplayType::Virtual) {
+ if (!displayData.isVirtual) {
LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display");
return INVALID_OPERATION;
}
- error = hwcDisplay->setOutputBuffer(buffer, acquireFence);
+ auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
-void HWComposer::clearReleaseFences(int32_t displayId) {
+void HWComposer::clearReleaseFences(DisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId);
mDisplayData[displayId].releaseFences.clear();
}
-status_t HWComposer::getHdrCapabilities(
- int32_t displayId, HdrCapabilities* outCapabilities) {
+status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
@@ -736,27 +721,22 @@
return NO_ERROR;
}
-int32_t HWComposer::getSupportedPerFrameMetadata(int32_t displayId) const {
+int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
-
- int32_t supportedMetadata;
- auto error = mDisplayData[displayId].hwcDisplay->getSupportedPerFrameMetadata(
- &supportedMetadata);
- RETURN_IF_HWC_ERROR(error, displayId, 0);
- return supportedMetadata;
+ return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
}
-std::vector<ui::RenderIntent> HWComposer::getRenderIntents(int32_t displayId,
- ui::ColorMode colorMode) const {
+std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId,
+ ui::ColorMode colorMode) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
std::vector<ui::RenderIntent> renderIntents;
- auto error = mDisplayData[displayId].hwcDisplay->getRenderIntents(colorMode, &renderIntents);
+ auto error = mDisplayData.at(displayId).hwcDisplay->getRenderIntents(colorMode, &renderIntents);
RETURN_IF_HWC_ERROR(error, displayId, {});
return renderIntents;
}
-mat4 HWComposer::getDataspaceSaturationMatrix(int32_t displayId, ui::Dataspace dataspace) {
+mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) {
RETURN_IF_INVALID_DISPLAY(displayId, {});
mat4 matrix;
@@ -766,65 +746,130 @@
return matrix;
}
-// Converts a PixelFormat to a human-readable string. Max 11 chars.
-// (Could use a table of prefab String8 objects.)
-/*
-static String8 getFormatStr(PixelFormat format) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888");
- case PIXEL_FORMAT_RGBX_8888: return String8("RGBx_8888");
- case PIXEL_FORMAT_RGB_888: return String8("RGB_888");
- case PIXEL_FORMAT_RGB_565: return String8("RGB_565");
- case PIXEL_FORMAT_BGRA_8888: return String8("BGRA_8888");
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
- return String8("ImplDef");
- default:
- String8 result;
- result.appendFormat("? %08x", format);
- return result;
- }
+status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId]
+ .hwcDisplay->getDisplayedContentSamplingAttributes(outFormat, outDataspace,
+ outComponentMask);
+ if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
}
-*/
+
+status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+ uint8_t componentMask, uint64_t maxFrames) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId].hwcDisplay->setDisplayContentSamplingEnabled(enabled,
+ componentMask,
+ maxFrames);
+
+ if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ if (error == HWC2::Error::BadParameter) RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
+status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+ uint64_t timestamp, DisplayedFrameStats* outStats) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId].hwcDisplay->getDisplayedContentSample(maxFrames, timestamp,
+ outStats);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
+status_t HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setDisplayBrightness(brightness);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
bool HWComposer::isUsingVrComposer() const {
return getComposer()->isUsingVrComposer();
}
-void HWComposer::dump(String8& result) const {
+void HWComposer::dump(std::string& result) const {
// TODO: In order to provide a dump equivalent to HWC1, we need to shadow
// all the state going into the layers. This is probably better done in
// Layer itself, but it's going to take a bit of work to get there.
- result.append(mHwcDevice->dump().c_str());
+ result.append(mHwcDevice->dump());
}
-std::optional<hwc2_display_t>
-HWComposer::getHwcDisplayId(int32_t displayId) const {
- if (!isValidDisplay(displayId)) {
+std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
+ if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId);
+ it != mPhysicalDisplayIdMap.end()) {
+ return it->second;
+ }
+ return {};
+}
+
+std::optional<hwc2_display_t> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
+ if (const auto it = mDisplayData.find(displayId);
+ it != mDisplayData.end() && !it->second.isVirtual) {
+ return it->second.hwcDisplay->getId();
+ }
+ return {};
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+ if (isUsingVrComposer() && mInternalHwcDisplayId) {
+ ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
return {};
}
- return mDisplayData[displayId].hwcDisplay->getId();
+
+ uint8_t port;
+ DisplayIdentificationData data;
+ const bool hasMultiDisplaySupport = getDisplayIdentificationData(hwcDisplayId, &port, &data);
+
+ if (mPhysicalDisplayIdMap.empty()) {
+ mHasMultiDisplaySupport = hasMultiDisplaySupport;
+ ALOGI("Switching to %s multi-display mode",
+ hasMultiDisplaySupport ? "generalized" : "legacy");
+ } else if (mHasMultiDisplaySupport && !hasMultiDisplaySupport) {
+ ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
+ hwcDisplayId);
+ return {};
+ }
+
+ std::optional<DisplayIdentificationInfo> info;
+
+ if (mHasMultiDisplaySupport) {
+ info = parseDisplayIdentificationData(port, data);
+ ALOGE_IF(!info, "Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+ } else if (mInternalHwcDisplayId && mExternalHwcDisplayId) {
+ ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
+ return {};
+ } else {
+ ALOGW_IF(hasMultiDisplaySupport, "Ignoring identification data for display %" PRIu64,
+ hwcDisplayId);
+ port = mInternalHwcDisplayId ? HWC_DISPLAY_EXTERNAL : HWC_DISPLAY_PRIMARY;
+ }
+
+ if (!mInternalHwcDisplayId) {
+ mInternalHwcDisplayId = hwcDisplayId;
+ } else if (!mExternalHwcDisplayId) {
+ mExternalHwcDisplayId = hwcDisplayId;
+ }
+
+ if (info) return info;
+
+ return DisplayIdentificationInfo{getFallbackDisplayId(port),
+ hwcDisplayId == mInternalHwcDisplayId ? "Internal display"
+ : "External display"};
}
-// ---------------------------------------------------------------------------
-
-HWComposer::DisplayData::DisplayData()
- : hasClientComposition(false),
- hasDeviceComposition(false),
- hwcDisplay(nullptr),
- lastPresentFence(Fence::NO_FENCE),
- outbufHandle(nullptr),
- outbufAcquireFence(Fence::NO_FENCE),
- vsyncEnabled(HWC2::Vsync::Disable) {
- ALOGV("Created new DisplayData");
-}
-
-HWComposer::DisplayData::~DisplayData() {
-}
-
-void HWComposer::DisplayData::reset() {
- ALOGV("DisplayData reset");
- *this = DisplayData();
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f968948..de863b8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -17,223 +17,359 @@
#ifndef ANDROID_SF_HWCOMPOSER_H
#define ANDROID_SF_HWCOMPOSER_H
-#include "HWC2.h"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Fence.h>
-#include <ui/GraphicTypes.h>
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
+#include <cstdint>
#include <memory>
+#include <mutex>
#include <optional>
-#include <set>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
- const struct timespec *request,
- struct timespec *remain);
+#include <android-base/thread_annotations.h>
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
-struct framebuffer_device_t;
-
-namespace HWC2 {
- class Device;
- class Display;
-}
+#include "DisplayIdentification.h"
+#include "HWC2.h"
namespace android {
-// ---------------------------------------------------------------------------
-class DisplayDevice;
-class Fence;
-class FloatRect;
+struct DisplayedFrameStats;
class GraphicBuffer;
-class NativeHandle;
-class Region;
-class String8;
class TestableSurfaceFlinger;
+struct CompositionInfo;
namespace Hwc2 {
class Composer;
} // namespace Hwc2
-class HWComposer
-{
+namespace compositionengine {
+class Output;
+} // namespace compositionengine
+
+class HWComposer {
public:
- explicit HWComposer(std::unique_ptr<android::Hwc2::Composer> composer);
+ virtual ~HWComposer();
- ~HWComposer();
+ virtual void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
- void registerCallback(HWC2::ComposerCallback* callback,
- int32_t sequenceId);
+ virtual bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+ DisplayIdentificationData* outData) const = 0;
- bool hasCapability(HWC2::Capability capability) const;
+ virtual bool hasCapability(HWC2::Capability capability) const = 0;
+ virtual bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
+ HWC2::DisplayCapability capability) const = 0;
- // Attempts to allocate a virtual display. If the virtual display is created
- // on the HWC device, outId will contain its HWC ID.
- status_t allocateVirtualDisplay(uint32_t width, uint32_t height,
- ui::PixelFormat* format, int32_t* outId);
+ // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
+ virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
+ ui::PixelFormat* format) = 0;
// Attempts to create a new layer on this display
- HWC2::Layer* createLayer(int32_t displayId);
+ virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
// Destroy a previously created layer
- void destroyLayer(int32_t displayId, HWC2::Layer* layer);
+ virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
// Asks the HAL what it can do
- status_t prepare(DisplayDevice& displayDevice);
+ virtual status_t prepare(DisplayId displayId, const compositionengine::Output&) = 0;
- status_t setClientTarget(int32_t displayId, uint32_t slot,
- const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& target, ui::Dataspace dataspace);
+ virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
+ const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
+ ui::Dataspace dataspace) = 0;
// Present layers to the display and read releaseFences.
- status_t presentAndGetReleaseFences(int32_t displayId);
+ virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0;
// set power mode
- status_t setPowerMode(int32_t displayId, int mode);
+ virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
// set active config
- status_t setActiveConfig(int32_t displayId, size_t configId);
+ virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
// Sets a color transform to be applied to the result of composition
- status_t setColorTransform(int32_t displayId, const mat4& transform);
+ virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
// reset state when an external, non-virtual display is disconnected
- void disconnectDisplay(int32_t displayId);
+ virtual void disconnectDisplay(DisplayId displayId) = 0;
// does this display have layers handled by HWC
- bool hasDeviceComposition(int32_t displayId) const;
+ virtual bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const = 0;
// does this display have pending request to flip client target
- bool hasFlipClientTargetRequest(int32_t displayId) const;
+ virtual bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const = 0;
// does this display have layers handled by GLES
- bool hasClientComposition(int32_t displayId) const;
+ virtual bool hasClientComposition(const std::optional<DisplayId>& displayId) const = 0;
// get the present fence received from the last call to present.
- sp<Fence> getPresentFence(int32_t displayId) const;
+ virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
// Get last release fence for the given layer
- sp<Fence> getLayerReleaseFence(int32_t displayId,
- HWC2::Layer* layer) const;
+ virtual sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const = 0;
// Set the output buffer and acquire fence for a virtual display.
// Returns INVALID_OPERATION if displayId is not a virtual display.
- status_t setOutputBuffer(int32_t displayId, const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& buf);
+ virtual status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& buffer) = 0;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- void clearReleaseFences(int32_t displayId);
+ virtual void clearReleaseFences(DisplayId displayId) = 0;
// Fetches the HDR capabilities of the given display
- status_t getHdrCapabilities(int32_t displayId, HdrCapabilities* outCapabilities);
+ virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0;
- int32_t getSupportedPerFrameMetadata(int32_t displayId) const;
+ virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0;
// Returns the available RenderIntent of the given display.
- std::vector<ui::RenderIntent> getRenderIntents(int32_t displayId, ui::ColorMode colorMode) const;
+ virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
+ ui::ColorMode colorMode) const = 0;
- mat4 getDataspaceSaturationMatrix(int32_t displayId, ui::Dataspace dataspace);
+ virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0;
+
+ // Returns the attributes of the color sampling engine.
+ virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) = 0;
+ virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+ uint8_t componentMask,
+ uint64_t maxFrames) = 0;
+ virtual status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+ uint64_t timestamp,
+ DisplayedFrameStats* outStats) = 0;
+
+ // Sets the brightness of a display.
+ virtual status_t setDisplayBrightness(DisplayId displayId, float brightness) = 0;
// Events handling ---------------------------------------------------------
- // Returns true if successful, false otherwise. The
- // DisplayDevice::DisplayType of the display is returned as an output param.
- bool onVsync(hwc2_display_t displayId, int64_t timestamp,
- int32_t* outDisplay);
- void onHotplug(hwc2_display_t displayId, int32_t displayType, HWC2::Connection connection);
+ // Returns stable display ID (and display name on connection of new or previously disconnected
+ // display), or std::nullopt if hotplug event was ignored.
+ virtual std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
+ HWC2::Connection connection) = 0;
- void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
+ virtual bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) = 0;
+ virtual void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) = 0;
- // Query display parameters. Pass in a display index (e.g.
- // HWC_DISPLAY_PRIMARY).
- nsecs_t getRefreshTimestamp(int32_t displayId) const;
- bool isConnected(int32_t displayId) const;
+ virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
+ virtual bool isConnected(DisplayId displayId) const = 0;
// Non-const because it can update configMap inside of mDisplayData
- std::vector<std::shared_ptr<const HWC2::Display::Config>>
- getConfigs(int32_t displayId) const;
+ virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
+ DisplayId displayId) const = 0;
- std::shared_ptr<const HWC2::Display::Config>
- getActiveConfig(int32_t displayId) const;
- int getActiveConfigIndex(int32_t displayId) const;
+ virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
+ DisplayId displayId) const = 0;
+ virtual int getActiveConfigIndex(DisplayId displayId) const = 0;
- std::vector<ui::ColorMode> getColorModes(int32_t displayId) const;
+ virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0;
- status_t setActiveColorMode(int32_t displayId, ui::ColorMode mode,
- ui::RenderIntent renderIntent);
+ virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+ ui::RenderIntent renderIntent) = 0;
- bool isUsingVrComposer() const;
+ virtual bool isUsingVrComposer() const = 0;
// for debugging ----------------------------------------------------------
- void dump(String8& out) const;
+ virtual void dump(std::string& out) const = 0;
- android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
+ virtual Hwc2::Composer* getComposer() const = 0;
- std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const;
+ // TODO(b/74619554): Remove special cases for internal/external display.
+ virtual std::optional<hwc2_display_t> getInternalHwcDisplayId() const = 0;
+ virtual std::optional<hwc2_display_t> getExternalHwcDisplayId() const = 0;
+
+ virtual std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const = 0;
+ virtual std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const = 0;
+};
+
+namespace impl {
+
+class HWComposer final : public android::HWComposer {
+public:
+ explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+
+ ~HWComposer() override;
+
+ void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
+
+ bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+ DisplayIdentificationData* outData) const override;
+
+ bool hasCapability(HWC2::Capability capability) const override;
+ bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
+ HWC2::DisplayCapability capability) const override;
+
+ // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
+ std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
+ ui::PixelFormat* format) override;
+
+ // Attempts to create a new layer on this display
+ HWC2::Layer* createLayer(DisplayId displayId) override;
+ // Destroy a previously created layer
+ void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
+
+ // Asks the HAL what it can do
+ status_t prepare(DisplayId displayId, const compositionengine::Output&) override;
+
+ status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
+
+ // Present layers to the display and read releaseFences.
+ status_t presentAndGetReleaseFences(DisplayId displayId) override;
+
+ // set power mode
+ status_t setPowerMode(DisplayId displayId, int mode) override;
+
+ // set active config
+ status_t setActiveConfig(DisplayId displayId, size_t configId) override;
+
+ // Sets a color transform to be applied to the result of composition
+ status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
+
+ // reset state when an external, non-virtual display is disconnected
+ void disconnectDisplay(DisplayId displayId) override;
+
+ // does this display have layers handled by HWC
+ bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const override;
+
+ // does this display have pending request to flip client target
+ bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const override;
+
+ // does this display have layers handled by GLES
+ bool hasClientComposition(const std::optional<DisplayId>& displayId) const override;
+
+ // get the present fence received from the last call to present.
+ sp<Fence> getPresentFence(DisplayId displayId) const override;
+
+ // Get last release fence for the given layer
+ sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override;
+
+ // Set the output buffer and acquire fence for a virtual display.
+ // Returns INVALID_OPERATION if displayId is not a virtual display.
+ status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+ const sp<GraphicBuffer>& buffer) override;
+
+ // After SurfaceFlinger has retrieved the release fences for all the frames,
+ // it can call this to clear the shared pointers in the release fence map
+ void clearReleaseFences(DisplayId displayId) override;
+
+ // Fetches the HDR capabilities of the given display
+ status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override;
+
+ int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override;
+
+ // Returns the available RenderIntent of the given display.
+ std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId,
+ ui::ColorMode colorMode) const override;
+
+ mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override;
+
+ // Returns the attributes of the color sampling engine.
+ status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) override;
+ status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+ uint8_t componentMask, uint64_t maxFrames) override;
+ status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) override;
+ status_t setDisplayBrightness(DisplayId displayId, float brightness) override;
+
+ // Events handling ---------------------------------------------------------
+
+ // Returns stable display ID (and display name on connection of new or previously disconnected
+ // display), or std::nullopt if hotplug event was ignored.
+ std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
+ HWC2::Connection connection) override;
+
+ bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) override;
+ void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) override;
+
+ nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
+ bool isConnected(DisplayId displayId) const override;
+
+ // Non-const because it can update configMap inside of mDisplayData
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
+ DisplayId displayId) const override;
+
+ std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
+ DisplayId displayId) const override;
+ int getActiveConfigIndex(DisplayId displayId) const override;
+
+ std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override;
+
+ status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+ ui::RenderIntent renderIntent) override;
+
+ bool isUsingVrComposer() const override;
+
+ // for debugging ----------------------------------------------------------
+ void dump(std::string& out) const override;
+
+ Hwc2::Composer* getComposer() const override { return mHwcDevice->getComposer(); }
+
+ // TODO(b/74619554): Remove special cases for internal/external display.
+ std::optional<hwc2_display_t> getInternalHwcDisplayId() const override {
+ return mInternalHwcDisplayId;
+ }
+ std::optional<hwc2_display_t> getExternalHwcDisplayId() const override {
+ return mExternalHwcDisplayId;
+ }
+
+ std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const override;
+ std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const override;
+
private:
// For unit tests
friend TestableSurfaceFlinger;
- static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2;
+ std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
- bool isValidDisplay(int32_t displayId) const;
static void validateChange(HWC2::Composition from, HWC2::Composition to);
- struct cb_context;
-
struct DisplayData {
- DisplayData();
- ~DisplayData();
- void reset();
-
- bool hasClientComposition;
- bool hasDeviceComposition;
- HWC2::Display* hwcDisplay;
+ bool isVirtual = false;
+ bool hasClientComposition = false;
+ bool hasDeviceComposition = false;
+ HWC2::Display* hwcDisplay = nullptr;
HWC2::DisplayRequest displayRequests;
- sp<Fence> lastPresentFence; // signals when the last set op retires
+ sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
- buffer_handle_t outbufHandle;
- sp<Fence> outbufAcquireFence;
+ buffer_handle_t outbufHandle = nullptr;
+ sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
mutable std::unordered_map<int32_t,
std::shared_ptr<const HWC2::Display::Config>> configMap;
- // protected by mVsyncLock
- HWC2::Vsync vsyncEnabled;
-
bool validateWasSkipped;
HWC2::Error presentError;
+
+ bool vsyncTraceToggle = false;
+
+ std::mutex vsyncEnabledLock;
+ HWC2::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = HWC2::Vsync::Disable;
+
+ mutable std::mutex lastHwVsyncLock;
+ nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
};
- std::unique_ptr<HWC2::Device> mHwcDevice;
- std::vector<DisplayData> mDisplayData{HWC_NUM_PHYSICAL_DISPLAY_TYPES};
- std::set<size_t> mFreeDisplaySlots;
- std::unordered_map<hwc2_display_t, int32_t> mHwcDisplaySlots;
- // protect mDisplayData from races between prepare and dump
- mutable Mutex mDisplayLock;
+ std::unordered_map<DisplayId, DisplayData> mDisplayData;
- cb_context* mCBContext = nullptr;
- size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]{0, 0};
+ // This must be destroyed before mDisplayData, because destructor may call back into HWComposer
+ // and look up DisplayData.
+ std::unique_ptr<HWC2::Device> mHwcDevice;
+
+ std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
+ std::optional<hwc2_display_t> mInternalHwcDisplayId;
+ std::optional<hwc2_display_t> mExternalHwcDisplayId;
+ bool mHasMultiDisplaySupport = false;
+
+ std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
+ uint32_t mNextVirtualDisplayId = 0;
uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
-
- // protected by mLock
- mutable Mutex mLock;
- mutable std::unordered_map<int32_t, nsecs_t> mLastHwVSync{
- {{HWC_DISPLAY_PRIMARY, 0}, {HWC_DISPLAY_EXTERNAL, 0}}};
-
- // thread-safe
- mutable Mutex mVsyncLock;
};
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace impl
+} // namespace android
#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
deleted file mode 100644
index a234b63..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "HWComposerBufferCache.h"
-
-#include <gui/BufferQueue.h>
-
-namespace android {
-
-HWComposerBufferCache::HWComposerBufferCache()
-{
- mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
-}
-
-void HWComposerBufferCache::getHwcBuffer(int slot,
- const sp<GraphicBuffer>& buffer,
- uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
-{
- if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
- // default to slot 0
- slot = 0;
- }
-
- if (static_cast<size_t>(slot) >= mBuffers.size()) {
- mBuffers.resize(slot + 1);
- }
-
- *outSlot = slot;
-
- if (mBuffers[slot] == buffer) {
- // already cached in HWC, skip sending the buffer
- *outBuffer = nullptr;
- } else {
- *outBuffer = buffer;
-
- // update cache
- mBuffers[slot] = buffer;
- }
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
deleted file mode 100644
index a008ca9..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
-#define ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
-
-#include <stdint.h>
-
-#include <utils/StrongPointer.h>
-
-#include <vector>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class GraphicBuffer;
-
-// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
-// HWC display and layer. When updating a display target or a layer buffer,
-// we have the option to send the buffer handle over or to request the HAL to
-// retrieve it from its cache. The latter is cheaper since it eliminates the
-// overhead to transfer the handle over the trasport layer, and the overhead
-// for the HAL to clone and retain the handle.
-//
-// To be able to find out whether a buffer is already in the HAL's cache, we
-// use HWComposerBufferCache to mirror the cache in SF.
-class HWComposerBufferCache {
-public:
- HWComposerBufferCache();
-
- // Given a buffer queue slot and buffer, return the HWC cache slot and
- // buffer to be sent to HWC.
- //
- // outBuffer is set to buffer when buffer is not in the HWC cache;
- // otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
- uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
-
-private:
- // a vector as we expect "slot" to be in the range of [0, 63] (that is,
- // less than BufferQueue::NUM_BUFFER_SLOTS).
- std::vector<sp<GraphicBuffer>> mBuffers;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
deleted file mode 100644
index fe7944f..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_HWCOMPOSER_HWC1_H
-#define ANDROID_SF_HWCOMPOSER_HWC1_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <hardware/hwcomposer_defs.h>
-
-#include <system/graphics.h>
-
-#include <ui/Fence.h>
-
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
- const struct timespec *request,
- struct timespec *remain);
-
-struct hwc_composer_device_1;
-struct hwc_display_contents_1;
-struct hwc_layer_1;
-struct hwc_procs;
-struct framebuffer_device_t;
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Fence;
-class FloatRect;
-class GraphicBuffer;
-class NativeHandle;
-class Region;
-class String8;
-class SurfaceFlinger;
-
-class HWComposer
-{
-public:
- class EventHandler {
- friend class HWComposer;
- virtual void onVSyncReceived(
- HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0;
- virtual void onHotplugReceived(HWComposer* composer, int disp, bool connected) = 0;
- virtual void onInvalidateReceived(HWComposer* composer) = 0;
- protected:
- virtual ~EventHandler() {}
- };
-
- enum {
- NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
- MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES,
- VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL,
- };
-
- HWComposer(
- const sp<SurfaceFlinger>& flinger,
- EventHandler& handler);
-
- ~HWComposer();
-
- status_t initCheck() const;
-
- // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to
- // be used with createWorkList (and all other methods requiring an ID
- // below).
- // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are
- // always valid.
- // Returns -1 if an ID cannot be allocated
- int32_t allocateDisplayId();
-
- // Recycles the given virtual display ID and frees the associated worklist.
- // IDs below NUM_BUILTIN_DISPLAYS are not recycled.
- status_t freeDisplayId(int32_t id);
-
-
- // Asks the HAL what it can do
- status_t prepare();
-
- // commits the list
- status_t commit();
-
- // set power mode
- status_t setPowerMode(int disp, int mode);
-
- // set active config
- status_t setActiveConfig(int disp, int mode);
-
- // reset state when an external, non-virtual display is disconnected
- void disconnectDisplay(int disp);
-
- // create a work list for numLayers layer. sets HWC_GEOMETRY_CHANGED.
- status_t createWorkList(int32_t id, size_t numLayers);
-
- bool supportsFramebufferTarget() const;
-
- // does this display have layers handled by HWC
- bool hasHwcComposition(int32_t id) const;
-
- // does this display have layers handled by GLES
- bool hasGlesComposition(int32_t id) const;
-
- // get the releaseFence file descriptor for a display's framebuffer layer.
- // the release fence is only valid after commit()
- sp<Fence> getAndResetReleaseFence(int32_t id);
-
- // needed forward declarations
- class LayerListIterator;
-
- // return the visual id to be used to find a suitable EGLConfig for
- // *ALL* displays.
- int getVisualID() const;
-
- // Forwarding to FB HAL for pre-HWC-1.1 code (see FramebufferSurface).
- int fbPost(int32_t id, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf);
- int fbCompositionComplete();
- void fbDump(String8& result);
-
- // Set the output buffer and acquire fence for a virtual display.
- // Returns INVALID_OPERATION if id is not a virtual display.
- status_t setOutputBuffer(int32_t id, const sp<Fence>& acquireFence,
- const sp<GraphicBuffer>& buf);
-
- // Get the retire fence for the last committed frame. This fence will
- // signal when the h/w composer is completely finished with the frame.
- // For physical displays, it is no longer being displayed. For virtual
- // displays, writes to the output buffer are complete.
- sp<Fence> getLastRetireFence(int32_t id) const;
-
- status_t setCursorPositionAsync(int32_t id, const Rect &pos);
-
- /*
- * Interface to hardware composer's layers functionality.
- * This abstracts the HAL interface to layers which can evolve in
- * incompatible ways from one release to another.
- * The idea is that we could extend this interface as we add
- * features to h/w composer.
- */
- class HWCLayerInterface {
- protected:
- virtual ~HWCLayerInterface() { }
- public:
- virtual int32_t getCompositionType() const = 0;
- virtual uint32_t getHints() const = 0;
- virtual sp<Fence> getAndResetReleaseFence() = 0;
- virtual void setDefaultState() = 0;
- virtual void setSkip(bool skip) = 0;
- virtual void setIsCursorLayerHint(bool isCursor = true) = 0;
- virtual void setBlending(uint32_t blending) = 0;
- virtual void setTransform(uint32_t transform) = 0;
- virtual void setFrame(const Rect& frame) = 0;
- virtual void setCrop(const FloatRect& crop) = 0;
- virtual void setVisibleRegionScreen(const Region& reg) = 0;
- virtual void setSurfaceDamage(const Region& reg) = 0;
- virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0;
- virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0;
- virtual void setAcquireFenceFd(int fenceFd) = 0;
- virtual void setPlaneAlpha(uint8_t alpha) = 0;
- virtual void onDisplayed() = 0;
- };
-
- /*
- * Interface used to implement an iterator to a list
- * of HWCLayer.
- */
- class HWCLayer : public HWCLayerInterface {
- friend class LayerListIterator;
- // select the layer at the given index
- virtual status_t setLayer(size_t index) = 0;
- virtual HWCLayer* dup() = 0;
- static HWCLayer* copy(HWCLayer *rhs) {
- return rhs ? rhs->dup() : nullptr;
- }
- protected:
- virtual ~HWCLayer() { }
- };
-
- /*
- * Iterator through a HWCLayer list.
- * This behaves more or less like a forward iterator.
- */
- class LayerListIterator {
- friend class HWComposer;
- HWCLayer* const mLayerList;
- size_t mIndex;
-
- LayerListIterator() : mLayerList(nullptr), mIndex(0) { }
-
- LayerListIterator(HWCLayer* layer, size_t index)
- : mLayerList(layer), mIndex(index) { }
-
- // we don't allow assignment, because we don't need it for now
- LayerListIterator& operator = (const LayerListIterator& rhs);
-
- public:
- // copy operators
- LayerListIterator(const LayerListIterator& rhs)
- : mLayerList(HWCLayer::copy(rhs.mLayerList)), mIndex(rhs.mIndex) {
- }
-
- ~LayerListIterator() { delete mLayerList; }
-
- // pre-increment
- LayerListIterator& operator++() {
- mLayerList->setLayer(++mIndex);
- return *this;
- }
-
- // dereference
- HWCLayerInterface& operator * () { return *mLayerList; }
- HWCLayerInterface* operator -> () { return mLayerList; }
-
- // comparison
- bool operator == (const LayerListIterator& rhs) const {
- return mIndex == rhs.mIndex;
- }
- bool operator != (const LayerListIterator& rhs) const {
- return !operator==(rhs);
- }
- };
-
- // Returns an iterator to the beginning of the layer list
- LayerListIterator begin(int32_t id);
-
- // Returns an iterator to the end of the layer list
- LayerListIterator end(int32_t id);
-
-
- // Events handling ---------------------------------------------------------
-
- enum {
- EVENT_VSYNC = HWC_EVENT_VSYNC
- };
-
- void eventControl(int disp, int event, int enabled);
-
- struct DisplayConfig {
- uint32_t width;
- uint32_t height;
- float xdpi;
- float ydpi;
- nsecs_t refresh;
- android_color_mode_t colorMode;
- bool operator==(const DisplayConfig& rhs) const {
- return width == rhs.width &&
- height == rhs.height &&
- xdpi == rhs.xdpi &&
- ydpi == rhs.ydpi &&
- refresh == rhs.refresh &&
- colorMode == rhs.colorMode;
- }
- };
-
- // Query display parameters. Pass in a display index (e.g.
- // HWC_DISPLAY_PRIMARY).
- nsecs_t getRefreshTimestamp(int disp) const;
- sp<Fence> getDisplayFence(int disp) const;
- uint32_t getFormat(int disp) const;
- bool isConnected(int disp) const;
-
- // These return the values for the current config of a given display index.
- // To get the values for all configs, use getConfigs below.
- uint32_t getWidth(int disp) const;
- uint32_t getHeight(int disp) const;
- float getDpiX(int disp) const;
- float getDpiY(int disp) const;
- nsecs_t getRefreshPeriod(int disp) const;
- android_color_mode_t getColorMode(int disp) const;
-
- const Vector<DisplayConfig>& getConfigs(int disp) const;
- size_t getCurrentConfig(int disp) const;
-
- status_t setVirtualDisplayProperties(int32_t id, uint32_t w, uint32_t h,
- uint32_t format);
-
- // this class is only used to fake the VSync event on systems that don't
- // have it.
- class VSyncThread : public Thread {
- HWComposer& mHwc;
- mutable Mutex mLock;
- Condition mCondition;
- bool mEnabled;
- mutable nsecs_t mNextFakeVSync;
- nsecs_t mRefreshPeriod;
- virtual void onFirstRef();
- virtual bool threadLoop();
- public:
- VSyncThread(HWComposer& hwc);
- void setEnabled(bool enabled);
- };
-
- friend class VSyncThread;
-
- // for debugging ----------------------------------------------------------
- void dump(String8& out) const;
-
-private:
- void loadHwcModule();
- int loadFbHalModule();
-
- LayerListIterator getLayerIterator(int32_t id, size_t index);
-
- struct cb_context;
-
- static void hook_invalidate(const struct hwc_procs* procs);
- static void hook_vsync(const struct hwc_procs* procs, int disp,
- int64_t timestamp);
- static void hook_hotplug(const struct hwc_procs* procs, int disp,
- int connected);
-
- inline void invalidate();
- inline void vsync(int disp, int64_t timestamp);
- inline void hotplug(int disp, int connected);
-
- status_t queryDisplayProperties(int disp);
-
- status_t setFramebufferTarget(int32_t id,
- const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf);
-
- struct DisplayData {
- DisplayData();
- ~DisplayData();
- Vector<DisplayConfig> configs;
- size_t currentConfig;
- uint32_t format; // pixel format from FB hal, for pre-hwc-1.1
- bool connected;
- bool hasFbComp;
- bool hasOvComp;
- size_t capacity;
- hwc_display_contents_1* list;
- hwc_layer_1* framebufferTarget;
- buffer_handle_t fbTargetHandle;
- sp<Fence> lastRetireFence; // signals when the last set op retires
- sp<Fence> lastDisplayFence; // signals when the last set op takes
- // effect on screen
- buffer_handle_t outbufHandle;
- sp<Fence> outbufAcquireFence;
-
- // protected by mEventControlLock
- int32_t events;
-
- // We need to hold "copies" of these for memory management purposes. The
- // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
- // internally doesn't copy the memory unless one of the copies is
- // modified.
- Vector<Region> visibleRegions;
- Vector<Region> surfaceDamageRegions;
- };
-
- sp<SurfaceFlinger> mFlinger;
- framebuffer_device_t* mFbDev;
- struct hwc_composer_device_1* mHwc;
- // invariant: mLists[0] != nullptr iff mHwc != nullptr
- // mLists[i>0] can be nullptr. that display is to be ignored
- struct hwc_display_contents_1* mLists[MAX_HWC_DISPLAYS];
- DisplayData mDisplayData[MAX_HWC_DISPLAYS];
- // protect mDisplayData from races between prepare and dump
- mutable Mutex mDisplayLock;
- size_t mNumDisplays;
-
- cb_context* mCBContext;
- EventHandler& mEventHandler;
- size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
- sp<VSyncThread> mVSyncThread;
- bool mDebugForceFakeVSync;
- BitSet32 mAllocatedDisplayIDs;
-
- // protected by mLock
- mutable Mutex mLock;
- mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
-
- // thread-safe
- mutable Mutex mEventControlLock;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 12bbae2..039db73 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -38,7 +38,7 @@
PowerAdvisor::PowerAdvisor() = default;
-void PowerAdvisor::setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) {
+void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
if (expected) {
mExpensiveDisplays.insert(displayId);
} else {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 573a1a9..5aa1f22 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -22,10 +22,12 @@
#undef HWC2_INCLUDE_STRINGIFICATION
#undef HWC2_USE_CPP11
+#include <unordered_set>
+
#include <android/hardware/power/1.3/IPower.h>
#include <utils/StrongPointer.h>
-#include <unordered_set>
+#include "DisplayIdentification.h"
namespace android {
namespace Hwc2 {
@@ -34,7 +36,7 @@
public:
virtual ~PowerAdvisor();
- virtual void setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) = 0;
+ virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
};
namespace impl {
@@ -48,12 +50,12 @@
PowerAdvisor();
~PowerAdvisor() override;
- void setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) override;
+ void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
private:
sp<V1_3::IPower> getPowerHal();
- std::unordered_set<hwc2_display_t> mExpensiveDisplays;
+ std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
bool mReconnectPowerHal = false;
};
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 9a2817d..613dc77 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -32,50 +32,54 @@
// ---------------------------------------------------------------------------
#define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
- mDisplayName.string(), ##__VA_ARGS__)
+ mDisplayName.c_str(), ##__VA_ARGS__)
#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
- mDisplayName.string(), ##__VA_ARGS__)
+ mDisplayName.c_str(), ##__VA_ARGS__)
#define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
- mDisplayName.string(), ##__VA_ARGS__)
+ mDisplayName.c_str(), ##__VA_ARGS__)
-static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
+static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
switch (type) {
- case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
- case DisplaySurface::COMPOSITION_GLES: return "GLES";
- case DisplaySurface::COMPOSITION_HWC: return "HWC";
- case DisplaySurface::COMPOSITION_MIXED: return "MIXED";
+ case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
+ return "UNKNOWN";
+ case compositionengine::DisplaySurface::COMPOSITION_GLES:
+ return "GLES";
+ case compositionengine::DisplaySurface::COMPOSITION_HWC:
+ return "HWC";
+ case compositionengine::DisplaySurface::COMPOSITION_MIXED:
+ return "MIXED";
default: return "<INVALID>";
}
}
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
- const sp<IGraphicBufferProducer>& sink,
- const sp<IGraphicBufferProducer>& bqProducer,
- const sp<IGraphicBufferConsumer>& bqConsumer,
- const String8& name)
-: ConsumerBase(bqConsumer),
- mHwc(hwc),
- mDisplayId(dispId),
- mDisplayName(name),
- mSource{},
- mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
- mOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
- mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
- mProducerSlotSource(0),
- mProducerBuffers(),
- mQueueBufferOutput(),
- mSinkBufferWidth(0),
- mSinkBufferHeight(0),
- mCompositionType(COMPOSITION_UNKNOWN),
- mFbFence(Fence::NO_FENCE),
- mOutputFence(Fence::NO_FENCE),
- mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mDbgState(DBG_STATE_IDLE),
- mDbgLastCompositionType(COMPOSITION_UNKNOWN),
- mMustRecompose(false),
- mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv)
-{
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
+ const std::optional<DisplayId>& displayId,
+ const sp<IGraphicBufferProducer>& sink,
+ const sp<IGraphicBufferProducer>& bqProducer,
+ const sp<IGraphicBufferConsumer>& bqConsumer,
+ const std::string& name)
+ : ConsumerBase(bqConsumer),
+ mHwc(hwc),
+ mDisplayId(displayId),
+ mDisplayName(name),
+ mSource{},
+ mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+ mOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+ mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
+ mProducerSlotSource(0),
+ mProducerBuffers(),
+ mQueueBufferOutput(),
+ mSinkBufferWidth(0),
+ mSinkBufferHeight(0),
+ mCompositionType(COMPOSITION_UNKNOWN),
+ mFbFence(Fence::NO_FENCE),
+ mOutputFence(Fence::NO_FENCE),
+ mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
+ mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
+ mDbgState(DBG_STATE_IDLE),
+ mDbgLastCompositionType(COMPOSITION_UNKNOWN),
+ mMustRecompose(false),
+ mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) {
mSource[SOURCE_SINK] = sink;
mSource[SOURCE_SCRATCH] = bqProducer;
@@ -102,7 +106,7 @@
}
mOutputFormat = mDefaultOutputFormat;
- ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string());
+ ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.c_str());
mConsumer->setConsumerName(ConsumerBase::mName);
mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
@@ -116,8 +120,9 @@
}
status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return NO_ERROR;
+ }
mMustRecompose = mustRecompose;
@@ -129,8 +134,9 @@
}
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return NO_ERROR;
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
"Unexpected prepareFrame() in %s state", dbgStateStr());
@@ -177,8 +183,9 @@
}
status_t VirtualDisplaySurface::advanceFrame() {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return NO_ERROR;
+ }
if (mCompositionType == COMPOSITION_HWC) {
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
@@ -211,32 +218,32 @@
// At this point we know the output buffer acquire fence,
// so update HWC state with it.
- mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
+ mHwc.setOutputBuffer(*mDisplayId, mOutputFence, outBuffer);
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer,
- &hwcSlot, &hwcBuffer);
+ mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer);
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(mDisplayId, hwcSlot, mFbFence,
- hwcBuffer, ui::Dataspace::UNKNOWN);
+ result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ ui::Dataspace::UNKNOWN);
}
return result;
}
void VirtualDisplaySurface::onFrameCommitted() {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return;
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
"Unexpected onFrameCommitted() in %s state", dbgStateStr());
mDbgState = DBG_STATE_IDLE;
- sp<Fence> retireFence = mHwc.getPresentFence(mDisplayId);
+ sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId);
if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
@@ -291,8 +298,9 @@
status_t VirtualDisplaySurface::requestBuffer(int pslot,
sp<GraphicBuffer>* outBuf) {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
"Unexpected requestBuffer pslot=%d in %s state",
@@ -313,7 +321,7 @@
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
- LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
+ LOG_FATAL_IF(!mDisplayId);
status_t result =
mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -359,7 +367,7 @@
PixelFormat format, uint64_t usage,
uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
- if (mDisplayId < 0) {
+ if (!mDisplayId) {
return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
outTimestamps);
}
@@ -446,8 +454,9 @@
status_t VirtualDisplaySurface::queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output) {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
"Unexpected queueBuffer(pslot=%d) in %s state", pslot,
@@ -504,8 +513,9 @@
status_t VirtualDisplaySurface::cancelBuffer(int pslot,
const sp<Fence>& fence) {
- if (mDisplayId < 0)
+ if (!mDisplayId) {
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
"Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
@@ -616,6 +626,8 @@
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
+ LOG_FATAL_IF(!mDisplayId);
+
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot),
@@ -633,8 +645,8 @@
// until after GLES calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
- result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
- mProducerBuffers[mOutputProducerSlot]);
+ result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
+ mProducerBuffers[mOutputProducerSlot]);
return result;
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 5c8acea..d6543d1 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -17,12 +17,16 @@
#ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
+#include <optional>
+#include <string>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
+#include "DisplayIdentification.h"
+
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
@@ -69,15 +73,14 @@
* the HWC output buffer. When HWC composition is complete, the scratch buffer
* is released and the output buffer is queued to the sink.
*/
-class VirtualDisplaySurface : public DisplaySurface,
+class VirtualDisplaySurface : public compositionengine::DisplaySurface,
public BnGraphicBufferProducer,
private ConsumerBase {
public:
- VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
- const sp<IGraphicBufferProducer>& sink,
- const sp<IGraphicBufferProducer>& bqProducer,
- const sp<IGraphicBufferConsumer>& bqConsumer,
- const String8& name);
+ VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId,
+ const sp<IGraphicBufferProducer>& sink,
+ const sp<IGraphicBufferProducer>& bqProducer,
+ const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
//
// DisplaySurface interface
@@ -152,8 +155,8 @@
// Immutable after construction
//
HWComposer& mHwc;
- const int32_t mDisplayId;
- const String8 mDisplayName;
+ const std::optional<DisplayId> mDisplayId;
+ const std::string mDisplayName;
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
@@ -250,7 +253,7 @@
bool mMustRecompose;
- HWComposerBufferCache mHwcBufferCache;
+ compositionengine::impl::HwcBufferCache mHwcBufferCache;
bool mForceHwcCopy;
};
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
deleted file mode 100644
index bc271c8..0000000
--- a/services/surfaceflinger/EventThread.cpp
+++ /dev/null
@@ -1,413 +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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <pthread.h>
-#include <sched.h>
-#include <sys/types.h>
-#include <chrono>
-#include <cstdint>
-
-#include <cutils/compiler.h>
-#include <cutils/sched_policy.h>
-
-#include <gui/DisplayEventReceiver.h>
-
-#include <utils/Errors.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include "EventThread.h"
-
-using namespace std::chrono_literals;
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-EventThread::~EventThread() = default;
-
-namespace impl {
-
-EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
- InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
- : mVSyncSource(src),
- mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
- mInterceptVSyncsCallback(interceptVSyncsCallback) {
- for (auto& event : mVSyncEvent) {
- event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- event.header.id = 0;
- event.header.timestamp = 0;
- event.vsync.count = 0;
- }
-
- mThread = std::thread(&EventThread::threadMain, this);
-
- pthread_setname_np(mThread.native_handle(), threadName);
-
- pid_t tid = pthread_gettid_np(mThread.native_handle());
-
- // Use SCHED_FIFO to minimize jitter
- constexpr int EVENT_THREAD_PRIORITY = 2;
- struct sched_param param = {0};
- param.sched_priority = EVENT_THREAD_PRIORITY;
- if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO for EventThread");
- }
-
- set_sched_policy(tid, SP_FOREGROUND);
-}
-
-EventThread::~EventThread() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mKeepRunning = false;
- mCondition.notify_all();
- }
- mThread.join();
-}
-
-void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
- std::lock_guard<std::mutex> lock(mMutex);
- mVSyncSource->setPhaseOffset(phaseOffset);
-}
-
-sp<BnDisplayEventConnection> EventThread::createEventConnection() const {
- return new Connection(const_cast<EventThread*>(this));
-}
-
-status_t EventThread::registerDisplayEventConnection(
- const sp<EventThread::Connection>& connection) {
- std::lock_guard<std::mutex> lock(mMutex);
- mDisplayEventConnections.add(connection);
- mCondition.notify_all();
- return NO_ERROR;
-}
-
-void EventThread::removeDisplayEventConnectionLocked(const wp<EventThread::Connection>& connection) {
- mDisplayEventConnections.remove(connection);
-}
-
-void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection>& connection) {
- if (int32_t(count) >= 0) { // server must protect against bad params
- std::lock_guard<std::mutex> lock(mMutex);
- const int32_t new_count = (count == 0) ? -1 : count;
- if (connection->count != new_count) {
- connection->count = new_count;
- mCondition.notify_all();
- }
- }
-}
-
-void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
- std::lock_guard<std::mutex> lock(mMutex);
-
- if (mResyncWithRateLimitCallback) {
- mResyncWithRateLimitCallback();
- }
-
- if (connection->count < 0) {
- connection->count = 0;
- mCondition.notify_all();
- }
-}
-
-void EventThread::onScreenReleased() {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mUseSoftwareVSync) {
- // disable reliance on h/w vsync
- mUseSoftwareVSync = true;
- mCondition.notify_all();
- }
-}
-
-void EventThread::onScreenAcquired() {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mUseSoftwareVSync) {
- // resume use of h/w vsync
- mUseSoftwareVSync = false;
- mCondition.notify_all();
- }
-}
-
-void EventThread::onVSyncEvent(nsecs_t timestamp) {
- std::lock_guard<std::mutex> lock(mMutex);
- mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[0].header.id = 0;
- mVSyncEvent[0].header.timestamp = timestamp;
- mVSyncEvent[0].vsync.count++;
- mCondition.notify_all();
-}
-
-void EventThread::onHotplugReceived(int type, bool connected) {
- ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
- "received hotplug event for an invalid display (id=%d)", type);
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- DisplayEventReceiver::Event event;
- event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG;
- event.header.id = type;
- event.header.timestamp = systemTime();
- event.hotplug.connected = connected;
- mPendingEvents.add(event);
- mCondition.notify_all();
- }
-}
-
-void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
- std::unique_lock<std::mutex> lock(mMutex);
- while (mKeepRunning) {
- DisplayEventReceiver::Event event;
- Vector<sp<EventThread::Connection> > signalConnections;
- signalConnections = waitForEventLocked(&lock, &event);
-
- // dispatch events to listeners...
- const size_t count = signalConnections.size();
- for (size_t i = 0; i < count; i++) {
- const sp<Connection>& conn(signalConnections[i]);
- // now see if we still need to report this event
- status_t err = conn->postEvent(event);
- if (err == -EAGAIN || err == -EWOULDBLOCK) {
- // The destination doesn't accept events anymore, it's probably
- // full. For now, we just drop the events on the floor.
- // FIXME: Note that some events cannot be dropped and would have
- // to be re-sent later.
- // Right-now we don't have the ability to do this.
- ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type,
- conn.get());
- } else if (err < 0) {
- // handle any other error on the pipe as fatal. the only
- // reasonable thing to do is to clean-up this connection.
- // The most common error we'll get here is -EPIPE.
- removeDisplayEventConnectionLocked(signalConnections[i]);
- }
- }
- }
-}
-
-// This will return when (1) a vsync event has been received, and (2) there was
-// at least one connection interested in receiving it when we started waiting.
-Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked(
- std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) {
- Vector<sp<EventThread::Connection> > signalConnections;
-
- while (signalConnections.isEmpty() && mKeepRunning) {
- bool eventPending = false;
- bool waitForVSync = false;
-
- size_t vsyncCount = 0;
- nsecs_t timestamp = 0;
- for (int32_t i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; i++) {
- timestamp = mVSyncEvent[i].header.timestamp;
- if (timestamp) {
- // we have a vsync event to dispatch
- if (mInterceptVSyncsCallback) {
- mInterceptVSyncsCallback(timestamp);
- }
- *event = mVSyncEvent[i];
- mVSyncEvent[i].header.timestamp = 0;
- vsyncCount = mVSyncEvent[i].vsync.count;
- break;
- }
- }
-
- if (!timestamp) {
- // no vsync event, see if there are some other event
- eventPending = !mPendingEvents.isEmpty();
- if (eventPending) {
- // we have some other event to dispatch
- *event = mPendingEvents[0];
- mPendingEvents.removeAt(0);
- }
- }
-
- // find out connections waiting for events
- size_t count = mDisplayEventConnections.size();
- for (size_t i = 0; i < count;) {
- sp<Connection> connection(mDisplayEventConnections[i].promote());
- if (connection != nullptr) {
- bool added = false;
- if (connection->count >= 0) {
- // we need vsync events because at least
- // one connection is waiting for it
- waitForVSync = true;
- if (timestamp) {
- // we consume the event only if it's time
- // (ie: we received a vsync event)
- if (connection->count == 0) {
- // fired this time around
- connection->count = -1;
- signalConnections.add(connection);
- added = true;
- } else if (connection->count == 1 ||
- (vsyncCount % connection->count) == 0) {
- // continuous event, and time to report it
- signalConnections.add(connection);
- added = true;
- }
- }
- }
-
- if (eventPending && !timestamp && !added) {
- // we don't have a vsync event to process
- // (timestamp==0), but we have some pending
- // messages.
- signalConnections.add(connection);
- }
- ++i;
- } else {
- // we couldn't promote this reference, the connection has
- // died, so clean-up!
- mDisplayEventConnections.removeAt(i);
- --count;
- }
- }
-
- // Here we figure out if we need to enable or disable vsyncs
- if (timestamp && !waitForVSync) {
- // we received a VSYNC but we have no clients
- // don't report it, and disable VSYNC events
- disableVSyncLocked();
- } else if (!timestamp && waitForVSync) {
- // we have at least one client, so we want vsync enabled
- // (TODO: this function is called right after we finish
- // notifying clients of a vsync, so this call will be made
- // at the vsync rate, e.g. 60fps. If we can accurately
- // track the current state we could avoid making this call
- // so often.)
- enableVSyncLocked();
- }
-
- // note: !timestamp implies signalConnections.isEmpty(), because we
- // don't populate signalConnections if there's no vsync pending
- if (!timestamp && !eventPending) {
- // wait for something to happen
- if (waitForVSync) {
- // This is where we spend most of our time, waiting
- // for vsync events and new client registrations.
- //
- // If the screen is off, we can't use h/w vsync, so we
- // use a 16ms timeout instead. It doesn't need to be
- // precise, we just need to keep feeding our clients.
- //
- // We don't want to stall if there's a driver bug, so we
- // use a (long) timeout when waiting for h/w vsync, and
- // generate fake events when necessary.
- bool softwareSync = mUseSoftwareVSync;
- auto timeout = softwareSync ? 16ms : 1000ms;
- if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) {
- if (!softwareSync) {
- ALOGW("Timed out waiting for hw vsync; faking it");
- }
- // FIXME: how do we decide which display id the fake
- // vsync came from ?
- mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
- mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- mVSyncEvent[0].vsync.count++;
- }
- } else {
- // Nobody is interested in vsync, so we just want to sleep.
- // h/w vsync should be disabled, so this will wait until we
- // get a new connection, or an existing connection becomes
- // interested in receiving vsync again.
- mCondition.wait(*lock);
- }
- }
- }
-
- // here we're guaranteed to have a timestamp and some connections to signal
- // (The connections might have dropped out of mDisplayEventConnections
- // while we were asleep, but we'll still have strong references to them.)
- return signalConnections;
-}
-
-void EventThread::enableVSyncLocked() {
- if (!mUseSoftwareVSync) {
- // never enable h/w VSYNC when screen is off
- if (!mVsyncEnabled) {
- mVsyncEnabled = true;
- mVSyncSource->setCallback(this);
- mVSyncSource->setVSyncEnabled(true);
- }
- }
- mDebugVsyncEnabled = true;
-}
-
-void EventThread::disableVSyncLocked() {
- if (mVsyncEnabled) {
- mVsyncEnabled = false;
- mVSyncSource->setVSyncEnabled(false);
- mDebugVsyncEnabled = false;
- }
-}
-
-void EventThread::dump(String8& result) const {
- std::lock_guard<std::mutex> lock(mMutex);
- result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled");
- result.appendFormat(" soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled");
- result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n",
- mDisplayEventConnections.size(),
- mVSyncEvent[DisplayDevice::DISPLAY_PRIMARY].vsync.count);
- for (size_t i = 0; i < mDisplayEventConnections.size(); i++) {
- sp<Connection> connection = mDisplayEventConnections.itemAt(i).promote();
- result.appendFormat(" %p: count=%d\n", connection.get(),
- connection != nullptr ? connection->count : 0);
- }
-}
-
-// ---------------------------------------------------------------------------
-
-EventThread::Connection::Connection(EventThread* eventThread)
- : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {}
-
-EventThread::Connection::~Connection() {
- // do nothing here -- clean-up will happen automatically
- // when the main thread wakes up
-}
-
-void EventThread::Connection::onFirstRef() {
- // NOTE: mEventThread doesn't hold a strong reference on us
- mEventThread->registerDisplayEventConnection(this);
-}
-
-status_t EventThread::Connection::stealReceiveChannel(gui::BitTube* outChannel) {
- outChannel->setReceiveFd(mChannel.moveReceiveFd());
- return NO_ERROR;
-}
-
-status_t EventThread::Connection::setVsyncRate(uint32_t count) {
- mEventThread->setVsyncRate(count, this);
- return NO_ERROR;
-}
-
-void EventThread::Connection::requestNextVsync() {
- mEventThread->requestNextVsync(this);
-}
-
-status_t EventThread::Connection::postEvent(const DisplayEventReceiver::Event& event) {
- ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
- return size < 0 ? status_t(size) : status_t(NO_ERROR);
-}
-
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
deleted file mode 100644
index 9c13ed2..0000000
--- a/services/surfaceflinger/EventThread.h
+++ /dev/null
@@ -1,171 +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.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
-#include <android-base/thread_annotations.h>
-
-#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
-#include <private/gui/BitTube.h>
-
-#include <utils/Errors.h>
-#include <utils/SortedVector.h>
-
-#include "DisplayDevice.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class EventThreadTest;
-class SurfaceFlinger;
-class String8;
-
-// ---------------------------------------------------------------------------
-
-class VSyncSource {
-public:
- class Callback {
- public:
- virtual ~Callback() {}
- virtual void onVSyncEvent(nsecs_t when) = 0;
- };
-
- virtual ~VSyncSource() {}
- virtual void setVSyncEnabled(bool enable) = 0;
- virtual void setCallback(Callback* callback) = 0;
- virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
-};
-
-class EventThread {
-public:
- virtual ~EventThread();
-
- virtual sp<BnDisplayEventConnection> createEventConnection() const = 0;
-
- // called before the screen is turned off from main thread
- virtual void onScreenReleased() = 0;
-
- // called after the screen is turned on from main thread
- virtual void onScreenAcquired() = 0;
-
- // called when receiving a hotplug event
- virtual void onHotplugReceived(int type, bool connected) = 0;
-
- virtual void dump(String8& result) const = 0;
-
- virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
-};
-
-namespace impl {
-
-class EventThread : public android::EventThread, private VSyncSource::Callback {
- class Connection : public BnDisplayEventConnection {
- public:
- explicit Connection(EventThread* eventThread);
- virtual ~Connection();
-
- virtual status_t postEvent(const DisplayEventReceiver::Event& event);
-
- // count >= 1 : continuous event. count is the vsync rate
- // count == 0 : one-shot event that has not fired
- // count ==-1 : one-shot event that fired this round / disabled
- int32_t count;
-
- private:
- virtual void onFirstRef();
- status_t stealReceiveChannel(gui::BitTube* outChannel) override;
- status_t setVsyncRate(uint32_t count) override;
- void requestNextVsync() override; // asynchronous
- EventThread* const mEventThread;
- gui::BitTube mChannel;
- };
-
-public:
- using ResyncWithRateLimitCallback = std::function<void()>;
- using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
-
- EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
- InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
- ~EventThread();
-
- sp<BnDisplayEventConnection> createEventConnection() const override;
- status_t registerDisplayEventConnection(const sp<Connection>& connection);
-
- void setVsyncRate(uint32_t count, const sp<Connection>& connection);
- void requestNextVsync(const sp<Connection>& connection);
-
- // called before the screen is turned off from main thread
- void onScreenReleased() override;
-
- // called after the screen is turned on from main thread
- void onScreenAcquired() override;
-
- // called when receiving a hotplug event
- void onHotplugReceived(int type, bool connected) override;
-
- void dump(String8& result) const override;
-
- void setPhaseOffset(nsecs_t phaseOffset) override;
-
-private:
- friend EventThreadTest;
-
- void threadMain();
- Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
- DisplayEventReceiver::Event* event)
- REQUIRES(mMutex);
-
- void removeDisplayEventConnectionLocked(const wp<Connection>& connection) REQUIRES(mMutex);
- void enableVSyncLocked() REQUIRES(mMutex);
- void disableVSyncLocked() REQUIRES(mMutex);
-
- // Implements VSyncSource::Callback
- void onVSyncEvent(nsecs_t timestamp) override;
-
- // constants
- VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
- const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
- const InterceptVSyncsCallback mInterceptVSyncsCallback;
-
- std::thread mThread;
- mutable std::mutex mMutex;
- mutable std::condition_variable mCondition;
-
- // protected by mLock
- SortedVector<wp<Connection>> mDisplayEventConnections GUARDED_BY(mMutex);
- Vector<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
- DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES] GUARDED_BY(
- mMutex);
- bool mUseSoftwareVSync GUARDED_BY(mMutex) = false;
- bool mVsyncEnabled GUARDED_BY(mMutex) = false;
- bool mKeepRunning GUARDED_BY(mMutex) = true;
-
- // for debugging
- bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
-};
-
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 1539873..f4cc49b 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -19,6 +19,7 @@
#include <inttypes.h>
+#include <android-base/stringprintf.h>
#include <android/log.h>
#include <utils/String8.h>
@@ -230,17 +231,17 @@
mFrameRecords[idx].actualPresentTime < INT64_MAX;
}
-void FrameTracker::dumpStats(String8& result) const {
+void FrameTracker::dumpStats(std::string& result) const {
Mutex::Autolock lock(mMutex);
processFencesLocked();
const size_t o = mOffset;
for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
const size_t index = (o+i) % NUM_FRAME_RECORDS;
- result.appendFormat("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
- mFrameRecords[index].desiredPresentTime,
- mFrameRecords[index].actualPresentTime,
- mFrameRecords[index].frameReadyTime);
+ base::StringAppendF(&result, "%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
+ mFrameRecords[index].desiredPresentTime,
+ mFrameRecords[index].actualPresentTime,
+ mFrameRecords[index].frameReadyTime);
}
result.append("\n");
}
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index b4a9fd6..555dcc1 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -90,7 +90,7 @@
void logAndResetStats(const String8& name);
// dumpStats dump appends the current frame display time history to the result string.
- void dumpStats(String8& result) const;
+ void dumpStats(std::string& result) const;
private:
struct FrameRecord {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 72f1fc4..07fe03e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -24,115 +24,98 @@
#include <stdlib.h>
#include <sys/types.h>
#include <algorithm>
+#include <mutex>
+#include <android-base/stringprintf.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <cutils/compiler.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
-
+#include <gui/BufferItem.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/DebugUtils.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/NativeHandle.h>
#include <utils/StopWatch.h>
#include <utils/Trace.h>
-#include <ui/DebugUtils.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/PixelFormat.h>
-
-#include <gui/BufferItem.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-
#include "BufferLayer.h"
+#include "ColorLayer.h"
#include "Colorizer.h"
#include "DisplayDevice.h"
+#include "DisplayHardware/HWComposer.h"
#include "Layer.h"
+#include "LayerProtoHelper.h"
#include "LayerRejecter.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
-#include "clz.h"
-
-#include "DisplayHardware/HWComposer.h"
-
-#include "RenderEngine/RenderEngine.h"
-
-#include <mutex>
-#include "LayerProtoHelper.h"
+#include "TimeStats/TimeStats.h"
#define DEBUG_RESIZE 0
namespace android {
-LayerBE::LayerBE()
- : mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2) {
-}
+using base::StringAppendF;
+std::atomic<int32_t> Layer::sSequence{1};
-int32_t Layer::sSequence = 1;
-
-Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
- uint32_t h, uint32_t flags)
- : contentDirty(false),
- sequence(uint32_t(android_atomic_inc(&sSequence))),
- mFlinger(flinger),
- mPremultipliedAlpha(true),
- mName(name),
- mTransactionFlags(0),
- mPendingStateMutex(),
- mPendingStates(),
- mQueuedFrames(0),
- mSidebandStreamChanged(false),
- mActiveBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mCurrentTransform(0),
- mOverrideScalingMode(-1),
- mCurrentOpacity(true),
- mCurrentFrameNumber(0),
- mFrameLatencyNeeded(false),
- mNeedsFiltering(false),
- mProtectedByApp(false),
- mClientRef(client),
- mPotentialCursor(false),
- mQueueItemLock(),
- mQueueItemCondition(),
- mQueueItems(),
- mLastFrameNumberReceived(0),
- mAutoRefresh(false),
- mFreezeGeometryUpdates(false),
- mCurrentChildren(LayerVector::StateSet::Current),
- mDrawingChildren(LayerVector::StateSet::Drawing) {
+Layer::Layer(const LayerCreationArgs& args)
+ : mFlinger(args.flinger), mName(args.name), mClientRef(args.client) {
mCurrentCrop.makeInvalid();
uint32_t layerFlags = 0;
- if (flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
- if (flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
- if (flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
+ if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
+ if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
+ if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
- mName = name;
mTransactionName = String8("TX - ") + mName;
- mCurrentState.active.w = w;
- mCurrentState.active.h = h;
+ mCurrentState.active_legacy.w = args.w;
+ mCurrentState.active_legacy.h = args.h;
mCurrentState.flags = layerFlags;
- mCurrentState.active.transform.set(0, 0);
- mCurrentState.crop.makeInvalid();
- mCurrentState.finalCrop.makeInvalid();
- mCurrentState.requestedFinalCrop = mCurrentState.finalCrop;
- mCurrentState.requestedCrop = mCurrentState.crop;
+ mCurrentState.active_legacy.transform.set(0, 0);
+ mCurrentState.crop_legacy.makeInvalid();
+ mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy;
mCurrentState.z = 0;
mCurrentState.color.a = 1.0f;
mCurrentState.layerStack = 0;
mCurrentState.sequence = 0;
- mCurrentState.requested = mCurrentState.active;
- mCurrentState.appId = 0;
- mCurrentState.type = 0;
+ mCurrentState.requested_legacy = mCurrentState.active_legacy;
+ mCurrentState.active.w = UINT32_MAX;
+ mCurrentState.active.h = UINT32_MAX;
+ mCurrentState.active.transform.set(0, 0);
+ mCurrentState.transform = 0;
+ mCurrentState.transformToDisplayInverse = false;
+ mCurrentState.crop.makeInvalid();
+ mCurrentState.acquireFence = new Fence(-1);
+ mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
+ mCurrentState.hdrMetadata.validTypes = 0;
+ mCurrentState.surfaceDamageRegion.clear();
+ mCurrentState.cornerRadius = 0.0f;
+ mCurrentState.api = -1;
+ mCurrentState.hasColorTransform = false;
+ mCurrentState.colorSpaceAgnostic = false;
+ mCurrentState.metadata = args.metadata;
// drawing state & current state are identical
mDrawingState = mCurrentState;
CompositorTiming compositorTiming;
- flinger->getCompositorTiming(&compositorTiming);
+ args.flinger->getCompositorTiming(&compositorTiming);
mFrameEventHistory.initializeCompositorTiming(compositorTiming);
mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
+
+ mFlinger->onLayerCreated();
}
Layer::~Layer() {
@@ -141,13 +124,9 @@
c->detachLayer(this);
}
- for (auto& point : mRemoteSyncPoints) {
- point->setTransactionApplied();
- }
- for (auto& point : mLocalSyncPoints) {
- point->setFrameAvailable();
- }
mFrameTracker.logAndResetStats(mName);
+
+ mFlinger->onLayerDestroyed();
}
// ---------------------------------------------------------------------------
@@ -162,32 +141,48 @@
void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
void Layer::onRemovedFromCurrentState() {
+ mRemovedFromCurrentState = true;
+
// the layer is removed from SF mCurrentState to mLayersPendingRemoval
-
- mPendingRemoval = true;
-
if (mCurrentState.zOrderRelativeOf != nullptr) {
sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote();
if (strongRelative != nullptr) {
strongRelative->removeZOrderRelative(this);
mFlinger->setTransactionFlags(eTraversalNeeded);
}
- mCurrentState.zOrderRelativeOf = nullptr;
+ setZOrderRelativeOf(nullptr);
+ }
+
+ // Since we are no longer reachable from CurrentState SurfaceFlinger
+ // will no longer invoke doTransaction for us, and so we will
+ // never finish applying transactions. We signal the sync point
+ // now so that another layer will not become indefinitely
+ // blocked.
+ for (auto& point: mRemoteSyncPoints) {
+ point->setTransactionApplied();
+ }
+ mRemoteSyncPoints.clear();
+
+ {
+ Mutex::Autolock syncLock(mLocalSyncPointMutex);
+ for (auto& point : mLocalSyncPoints) {
+ point->setFrameAvailable();
+ }
+ mLocalSyncPoints.clear();
}
for (const auto& child : mCurrentChildren) {
child->onRemovedFromCurrentState();
}
+
+ mFlinger->markLayerPendingRemovalLocked(this);
}
-void Layer::onRemoved() {
- // the layer is removed from SF mLayersPendingRemoval
- abandon();
-
- destroyAllHwcLayers();
+void Layer::addToCurrentState() {
+ mRemovedFromCurrentState = false;
for (const auto& child : mCurrentChildren) {
- child->onRemoved();
+ child->addToCurrentState();
}
}
@@ -212,44 +207,18 @@
// h/w composer set-up
// ---------------------------------------------------------------------------
-bool Layer::createHwcLayer(HWComposer* hwc, int32_t hwcId) {
- LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(hwcId) != 0,
- "Already have a layer for hwcId %d", hwcId);
- HWC2::Layer* layer = hwc->createLayer(hwcId);
- if (!layer) {
- return false;
- }
- LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[hwcId];
- hwcInfo.hwc = hwc;
- hwcInfo.layer = layer;
- layer->setLayerDestroyedListener(
- [this, hwcId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(hwcId); });
- return true;
+bool Layer::hasHwcLayer(const sp<const DisplayDevice>& displayDevice) {
+ auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ LOG_FATAL_IF(!outputLayer);
+ return outputLayer->getState().hwc && (*outputLayer->getState().hwc).hwcLayer != nullptr;
}
-bool Layer::destroyHwcLayer(int32_t hwcId) {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- return false;
+HWC2::Layer* Layer::getHwcLayer(const sp<const DisplayDevice>& displayDevice) {
+ auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ if (!outputLayer || !outputLayer->getState().hwc) {
+ return nullptr;
}
- auto& hwcInfo = getBE().mHwcLayers[hwcId];
- LOG_ALWAYS_FATAL_IF(hwcInfo.layer == nullptr, "Attempt to destroy null layer");
- LOG_ALWAYS_FATAL_IF(hwcInfo.hwc == nullptr, "Missing HWComposer");
- hwcInfo.hwc->destroyLayer(hwcId, hwcInfo.layer);
- // The layer destroyed listener should have cleared the entry from
- // mHwcLayers. Verify that.
- LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(hwcId) != 0,
- "Stale layer entry in getBE().mHwcLayers");
- return true;
-}
-
-void Layer::destroyAllHwcLayers() {
- size_t numLayers = getBE().mHwcLayers.size();
- for (size_t i = 0; i < numLayers; ++i) {
- LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.empty(), "destroyAllHwcLayers failed");
- destroyHwcLayer(getBE().mHwcLayers.begin()->first);
- }
- LOG_ALWAYS_FATAL_IF(!getBE().mHwcLayers.empty(),
- "All hardware composer layers should have been destroyed");
+ return (*outputLayer->getState().hwc).hwcLayer.get();
}
Rect Layer::getContentCrop() const {
@@ -259,9 +228,9 @@
if (!mCurrentCrop.isEmpty()) {
// if the buffer crop is defined, we use that
crop = mCurrentCrop;
- } else if (getBE().compositionInfo.mBuffer != nullptr) {
+ } else if (mActiveBuffer != nullptr) {
// otherwise we use the whole buffer
- crop = getBE().compositionInfo.mBuffer->getBounds();
+ crop = mActiveBuffer->getBounds();
} else {
// if we don't have a buffer yet, we use an empty/invalid crop
crop.makeInvalid();
@@ -287,412 +256,224 @@
return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
}
-Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
- const Layer::State& s(getDrawingState());
- Rect win(s.active.w, s.active.h);
-
- if (!s.crop.isEmpty()) {
- win.intersect(s.crop, &win);
+Rect Layer::getScreenBounds(bool reduceTransparentRegion) const {
+ if (!reduceTransparentRegion) {
+ return Rect{mScreenBounds};
}
- Transform t = getTransform();
- win = t.transform(win);
-
- if (!s.finalCrop.isEmpty()) {
- win.intersect(s.finalCrop, &win);
- }
-
- const sp<Layer>& p = mDrawingParent.promote();
- // Now we need to calculate the parent bounds, so we can clip ourselves to those.
- // When calculating the parent bounds for purposes of clipping,
- // we don't need to constrain the parent to its transparent region.
- // The transparent region is an optimization based on the
- // buffer contents of the layer, but does not affect the space allocated to
- // it by policy, and thus children should be allowed to extend into the
- // parent's transparent region. In fact one of the main uses, is to reduce
- // buffer allocation size in cases where a child window sits behind a main window
- // (by marking the hole in the parent window as a transparent region)
- if (p != nullptr) {
- Rect bounds = p->computeScreenBounds(false);
- bounds.intersect(win, &win);
- }
-
- if (reduceTransparentRegion) {
- auto const screenTransparentRegion = t.transform(s.activeTransparentRegion);
- win = reduce(win, screenTransparentRegion);
- }
-
- return win;
+ FloatRect bounds = getBounds();
+ ui::Transform t = getTransform();
+ // Transform to screen space.
+ bounds = t.transform(bounds);
+ return Rect{bounds};
}
-FloatRect Layer::computeBounds() const {
- const Layer::State& s(getDrawingState());
- return computeBounds(s.activeTransparentRegion);
+FloatRect Layer::getBounds() const {
+ const State& s(getDrawingState());
+ return getBounds(getActiveTransparentRegion(s));
}
-FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
- const Layer::State& s(getDrawingState());
- Rect win(s.active.w, s.active.h);
-
- if (!s.crop.isEmpty()) {
- win.intersect(s.crop, &win);
- }
-
- const auto& p = mDrawingParent.promote();
- FloatRect floatWin = win.toFloatRect();
- FloatRect parentBounds = floatWin;
- if (p != nullptr) {
- // We pass an empty Region here for reasons mirroring that of the case described in
- // the computeScreenBounds reduceTransparentRegion=false case.
- parentBounds = p->computeBounds(Region());
- }
-
- Transform t = s.active.transform;
-
-
- if (p != nullptr || !s.finalCrop.isEmpty()) {
- floatWin = t.transform(floatWin);
- floatWin = floatWin.intersect(parentBounds);
-
- if (!s.finalCrop.isEmpty()) {
- floatWin = floatWin.intersect(s.finalCrop.toFloatRect());
- }
- floatWin = t.inverse().transform(floatWin);
- }
-
- // subtract the transparent region and snap to the bounds
- return reduce(floatWin, activeTransparentRegion);
+FloatRect Layer::getBounds(const Region& activeTransparentRegion) const {
+ // Subtract the transparent region and snap to the bounds.
+ return reduce(mBounds, activeTransparentRegion);
}
-Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const {
- // the crop is the area of the window that gets cropped, but not
- // scaled in any ways.
+ui::Transform Layer::getBufferScaleTransform() const {
+ // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
+ // it isFixedSize) then there may be additional scaling not accounted
+ // for in the layer transform.
+ if (!isFixedSize() || !mActiveBuffer) {
+ return {};
+ }
+
+ // If the layer is a buffer state layer, the active width and height
+ // could be infinite. In that case, return the effective transform.
+ const uint32_t activeWidth = getActiveWidth(getDrawingState());
+ const uint32_t activeHeight = getActiveHeight(getDrawingState());
+ if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) {
+ return {};
+ }
+
+ int bufferWidth = mActiveBuffer->getWidth();
+ int bufferHeight = mActiveBuffer->getHeight();
+
+ if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+
+ float sx = activeWidth / static_cast<float>(bufferWidth);
+ float sy = activeHeight / static_cast<float>(bufferHeight);
+
+ ui::Transform extraParentScaling;
+ extraParentScaling.set(sx, 0, 0, sy);
+ return extraParentScaling;
+}
+
+ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
+ // We need to mirror this scaling to child surfaces or we will break the contract where WM can
+ // treat child surfaces as pixels in the parent surface.
+ if (!isFixedSize() || !mActiveBuffer) {
+ return mEffectiveTransform;
+ }
+ return mEffectiveTransform * bufferScaleTransform;
+}
+
+FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
+ // We need the pre scaled layer bounds when computing child bounds to make sure the child is
+ // cropped to its parent layer after any buffer transform scaling is applied.
+ if (!isFixedSize() || !mActiveBuffer) {
+ return mBounds;
+ }
+ return bufferScaleTransform.inverse().transform(mBounds);
+}
+
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
const State& s(getDrawingState());
- // apply the projection's clipping to the window crop in
- // layerstack space, and convert-back to layer space.
- // if there are no window scaling involved, this operation will map to full
- // pixels in the buffer.
- // FIXME: the 3 lines below can produce slightly incorrect clipping when we have
- // a viewport clipping and a window transform. we should use floating point to fix this.
+ // Calculate effective layer transform
+ mEffectiveTransform = parentTransform * getActiveTransform(s);
- Rect activeCrop(s.active.w, s.active.h);
- if (!s.crop.isEmpty()) {
- activeCrop.intersect(s.crop, &activeCrop);
- }
+ // Transform parent bounds to layer space
+ parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
- Transform t = getTransform();
- activeCrop = t.transform(activeCrop);
- if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
- activeCrop.clear();
- }
- if (!s.finalCrop.isEmpty()) {
- if (!activeCrop.intersect(s.finalCrop, &activeCrop)) {
- activeCrop.clear();
- }
- }
+ // Calculate source bounds
+ mSourceBounds = computeSourceBounds(parentBounds);
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- auto parentCrop = p->computeInitialCrop(hw);
- activeCrop.intersect(parentCrop, &activeCrop);
+ // Calculate bounds by croping diplay frame with layer crop and parent bounds
+ FloatRect bounds = mSourceBounds;
+ const Rect layerCrop = getCrop(s);
+ if (!layerCrop.isEmpty()) {
+ bounds = mSourceBounds.intersect(layerCrop.toFloatRect());
}
+ bounds = bounds.intersect(parentBounds);
- return activeCrop;
+ mBounds = bounds;
+ mScreenBounds = mEffectiveTransform.transform(mBounds);
+
+ // Add any buffer scaling to the layer's children.
+ ui::Transform bufferScaleTransform = getBufferScaleTransform();
+ for (const sp<Layer>& child : mDrawingChildren) {
+ child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
+ getTransformWithScale(bufferScaleTransform));
+ }
}
-FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
- // the content crop is the area of the content that gets scaled to the
- // layer's size. This is in buffer space.
- FloatRect crop = getContentCrop().toFloatRect();
-
- // In addition there is a WM-specified crop we pull from our drawing state.
- const State& s(getDrawingState());
-
- // Screen space to make reduction to parent crop clearer.
- Rect activeCrop = computeInitialCrop(hw);
- Transform t = getTransform();
- // Back to layer space to work with the content crop.
- activeCrop = t.inverse().transform(activeCrop);
-
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) {
- activeCrop.clear();
+Rect Layer::getCroppedBufferSize(const State& s) const {
+ Rect size = getBufferSize(s);
+ Rect crop = getCrop(s);
+ if (!crop.isEmpty() && size.isValid()) {
+ size.intersect(crop, &size);
+ } else if (!crop.isEmpty()) {
+ size = crop;
}
-
- // subtract the transparent region and snap to the bounds
- activeCrop = reduce(activeCrop, s.activeTransparentRegion);
-
- // Transform the window crop to match the buffer coordinate system,
- // which means using the inverse of the current transform set on the
- // SurfaceFlingerConsumer.
- uint32_t invTransform = mCurrentTransform;
- if (getTransformToDisplayInverse()) {
- /*
- * the code below applies the primary display's inverse transform to the
- * buffer
- */
- uint32_t invTransformOrient = DisplayDevice::getPrimaryDisplayOrientationTransform();
- // calculate the inverse transform
- if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
- // and apply to the current transform
- invTransform = (Transform(invTransformOrient) * Transform(invTransform)).getOrientation();
- }
-
- int winWidth = s.active.w;
- int winHeight = s.active.h;
- if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- // If the activeCrop has been rotate the ends are rotated but not
- // the space itself so when transforming ends back we can't rely on
- // 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 & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0;
- bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0;
- if (is_h_flipped == is_v_flipped) {
- invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
- winWidth = s.active.h;
- winHeight = s.active.w;
- }
- const Rect winCrop = activeCrop.transform(invTransform, s.active.w, s.active.h);
-
- // below, crop is intersected with winCrop expressed in crop's coordinate space
- float xScale = crop.getWidth() / float(winWidth);
- 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;
-
- crop.left += insetL;
- crop.top += insetT;
- crop.right -= insetR;
- crop.bottom -= insetB;
-
- return crop;
+ return size;
}
-void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z)
-{
- const auto hwcId = displayDevice->getHwcDisplayId();
- if (!hasHwcLayer(hwcId)) {
- return;
- }
- auto& hwcInfo = getBE().mHwcLayers[hwcId];
+void Layer::setupRoundedCornersCropCoordinates(Rect win,
+ const FloatRect& roundedCornersCrop) const {
+ // Translate win by the rounded corners rect coordinates, to have all values in
+ // layer coordinate space.
+ win.left -= roundedCornersCrop.left;
+ win.right -= roundedCornersCrop.left;
+ win.top -= roundedCornersCrop.top;
+ win.bottom -= roundedCornersCrop.top;
- // enable this layer
- hwcInfo.forceClientComposition = false;
+ renderengine::Mesh::VertexArray<vec2> cropCoords(
+ getCompositionLayer()->editState().reMesh.getCropCoordArray<vec2>());
+ cropCoords[0] = vec2(win.left, win.top);
+ cropCoords[1] = vec2(win.left, win.top + win.getHeight());
+ cropCoords[2] = vec2(win.right, win.top + win.getHeight());
+ cropCoords[3] = vec2(win.right, win.top);
+}
- if (isSecure() && !displayDevice->isSecure()) {
- hwcInfo.forceClientComposition = true;
- }
-
- auto& hwcLayer = hwcInfo.layer;
-
- // this gives us only the "orientation" component of the transform
- const State& s(getDrawingState());
+void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+ const auto& drawingState{getDrawingState()};
+ auto alpha = static_cast<float>(getAlpha());
auto blendMode = HWC2::BlendMode::None;
- if (!isOpaque(s) || getAlpha() != 1.0f) {
+ if (!isOpaque(drawingState) || alpha != 1.0f) {
blendMode =
mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
}
- auto error = hwcLayer->setBlendMode(blendMode);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set blend mode %s:"
- " %s (%d)",
- mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- // apply the layer's transform, followed by the display's global transform
- // here we're guaranteed that the layer's transform preserves rects
- Region activeTransparentRegion(s.activeTransparentRegion);
- Transform t = getTransform();
- if (!s.crop.isEmpty()) {
- Rect activeCrop(s.crop);
- activeCrop = t.transform(activeCrop);
- if (!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) {
- activeCrop.clear();
- }
- activeCrop = t.inverse().transform(activeCrop, true);
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) {
- activeCrop.clear();
- }
- // mark regions outside the crop as transparent
- activeTransparentRegion.orSelf(Rect(0, 0, s.active.w, activeCrop.top));
- activeTransparentRegion.orSelf(Rect(0, activeCrop.bottom, s.active.w, s.active.h));
- activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
- activeTransparentRegion.orSelf(
- Rect(activeCrop.right, activeCrop.top, s.active.w, activeCrop.bottom));
- }
-
- // computeBounds returns a FloatRect to provide more accuracy during the
- // transformation. We then round upon constructing 'frame'.
- Rect frame{t.transform(computeBounds(activeTransparentRegion))};
- if (!s.finalCrop.isEmpty()) {
- if (!frame.intersect(s.finalCrop, &frame)) {
- frame.clear();
- }
- }
- if (!frame.intersect(displayDevice->getViewport(), &frame)) {
- frame.clear();
- }
- const Transform& tr(displayDevice->getTransform());
- Rect transformedFrame = tr.transform(frame);
- error = hwcLayer->setDisplayFrame(transformedFrame);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(),
- transformedFrame.left, transformedFrame.top, transformedFrame.right,
- transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
- } else {
- hwcInfo.displayFrame = transformedFrame;
- }
-
- FloatRect sourceCrop = computeCrop(displayDevice);
- error = hwcLayer->setSourceCrop(sourceCrop);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
- "%s (%d)",
- mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom,
- to_string(error).c_str(), static_cast<int32_t>(error));
- } else {
- hwcInfo.sourceCrop = sourceCrop;
- }
-
- float alpha = static_cast<float>(getAlpha());
- error = hwcLayer->setPlaneAlpha(alpha);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set plane alpha %.3f: "
- "%s (%d)",
- mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = hwcLayer->setZOrder(z);
- ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z,
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- int type = s.type;
- int appId = s.appId;
+ int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
sp<Layer> parent = mDrawingParent.promote();
if (parent.get()) {
auto& parentState = parent->getDrawingState();
- if (parentState.type >= 0 || parentState.appId >= 0) {
- type = parentState.type;
- appId = parentState.appId;
+ const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
+ if (parentType >= 0 || parentAppId >= 0) {
+ type = parentType;
+ appId = parentAppId;
}
}
- error = hwcLayer->setInfo(type, appId);
- ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(),
- static_cast<int32_t>(error));
+ compositionState.geomLayerTransform = getTransform();
+ compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
+ compositionState.geomBufferSize = getBufferSize(drawingState);
+ compositionState.geomContentCrop = getContentCrop();
+ compositionState.geomCrop = getCrop(drawingState);
+ compositionState.geomBufferTransform = mCurrentTransform;
+ compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+ compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState);
+ compositionState.geomLayerBounds = mBounds;
+ compositionState.geomUsesSourceCrop = usesSourceCrop();
+ compositionState.isSecure = isSecure();
- /*
- * Transformations are applied in this order:
- * 1) buffer orientation/flip/mirror
- * 2) state transformation (window manager)
- * 3) layer orientation (screen orientation)
- * (NOTE: the matrices are multiplied in reverse order)
- */
+ compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+ compositionState.alpha = alpha;
+ compositionState.type = type;
+ compositionState.appId = appId;
+}
- const Transform bufferOrientation(mCurrentTransform);
- Transform transform(tr * t * bufferOrientation);
-
- if (getTransformToDisplayInverse()) {
- /*
- * the code below applies the primary display's inverse transform to the
- * buffer
- */
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
- // calculate the inverse transform
- if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
-
- /*
- * Here we cancel out the orientation component of the WM transform.
- * The scaling and translate components are already included in our bounds
- * computation so it's enough to just omit it in the composition.
- * See comment in onDraw with ref to b/36727915 for why.
- */
- transform = Transform(invTransform) * tr * bufferOrientation;
- }
-
- // this gives us only the "orientation" component of the transform
- const uint32_t orientation = transform.getOrientation();
- if (orientation & Transform::ROT_INVALID) {
- // we can only handle simple transformation
- hwcInfo.forceClientComposition = true;
- } else {
- auto transform = static_cast<HWC2::Transform>(orientation);
- hwcInfo.transform = transform;
- auto error = hwcLayer->setTransform(transform);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set transform %s: "
- "%s (%d)",
- mName.string(), to_string(transform).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
+void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
+ bool includeGeometry) const {
+ if (includeGeometry) {
+ latchGeometry(compositionState);
}
}
-void Layer::forceClientComposition(int32_t hwcId) {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- ALOGE("forceClientComposition: no HWC layer found (%d)", hwcId);
- return;
- }
-
- getBE().mHwcLayers[hwcId].forceClientComposition = true;
+const char* Layer::getDebugName() const {
+ return mName.string();
}
-bool Layer::getForceClientComposition(int32_t hwcId) {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- ALOGE("getForceClientComposition: no HWC layer found (%d)", hwcId);
- return false;
- }
-
- return getBE().mHwcLayers[hwcId].forceClientComposition;
+void Layer::forceClientComposition(const sp<DisplayDevice>& display) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer);
+ outputLayer->editState().forceClientComposition = true;
}
-void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) {
- auto hwcId = displayDevice->getHwcDisplayId();
- if (getBE().mHwcLayers.count(hwcId) == 0 ||
- getCompositionType(hwcId) != HWC2::Composition::Cursor) {
+bool Layer::getForceClientComposition(const sp<DisplayDevice>& display) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer);
+ return outputLayer->getState().forceClientComposition;
+}
+
+void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer);
+
+ if (!outputLayer->getState().hwc ||
+ (*outputLayer->getState().hwc).hwcCompositionType !=
+ Hwc2::IComposerClient::Composition::CURSOR) {
return;
}
// This gives us only the "orientation" component of the transform
- const State& s(getCurrentState());
+ const State& s(getDrawingState());
// Apply the layer's transform, followed by the display's global transform
// Here we're guaranteed that the layer's transform preserves rects
- Rect win(s.active.w, s.active.h);
- if (!s.crop.isEmpty()) {
- win.intersect(s.crop, &win);
- }
+ Rect win = getCroppedBufferSize(s);
// Subtract the transparent region and snap to the bounds
- Rect bounds = reduce(win, s.activeTransparentRegion);
+ Rect bounds = reduce(win, getActiveTransparentRegion(s));
Rect frame(getTransform().transform(bounds));
- frame.intersect(displayDevice->getViewport(), &frame);
- if (!s.finalCrop.isEmpty()) {
- frame.intersect(s.finalCrop, &frame);
- }
- auto& displayTransform(displayDevice->getTransform());
+ frame.intersect(display->getViewport(), &frame);
+ auto& displayTransform = display->getTransform();
auto position = displayTransform.transform(frame);
- auto error = getBE().mHwcLayers[hwcId].layer->setCursorPosition(position.left,
- position.top);
+ auto error =
+ (*outputLayer->getState().hwc).hwcLayer->setCursorPosition(position.left, position.top);
ALOGE_IF(error != HWC2::Error::None,
"[%s] Failed to set cursor position "
"to (%d, %d): %s (%d)",
@@ -704,80 +485,103 @@
// drawing...
// ---------------------------------------------------------------------------
-void Layer::draw(const RenderArea& renderArea, const Region& clip) const {
- onDraw(renderArea, clip, false);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ Region& clearRegion, const bool supportProtectedContent,
+ renderengine::LayerSettings& layer) {
+ return prepareClientLayer(renderArea, clip, false, clearRegion, supportProtectedContent, layer);
}
-void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) const {
- onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
+bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+ Region& clearRegion, const bool supportProtectedContent,
+ renderengine::LayerSettings& layer) {
+ return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
+ clearRegion, supportProtectedContent, layer);
}
-void Layer::draw(const RenderArea& renderArea) const {
- onDraw(renderArea, Region(renderArea.getBounds()), false);
+bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
+ bool useIdentityTransform, Region& /*clearRegion*/,
+ const bool /*supportProtectedContent*/,
+ renderengine::LayerSettings& layer) {
+ FloatRect bounds = getBounds();
+ half alpha = getAlpha();
+ layer.geometry.boundaries = bounds;
+ if (useIdentityTransform) {
+ layer.geometry.positionTransform = mat4();
+ } else {
+ const ui::Transform transform = getTransform();
+ mat4 m;
+ m[0][0] = transform[0][0];
+ m[0][1] = transform[0][1];
+ m[0][3] = transform[0][2];
+ m[1][0] = transform[1][0];
+ m[1][1] = transform[1][1];
+ m[1][3] = transform[1][2];
+ m[3][0] = transform[2][0];
+ m[3][1] = transform[2][1];
+ m[3][3] = transform[2][2];
+ layer.geometry.positionTransform = m;
+ }
+
+ if (hasColorTransform()) {
+ layer.colorTransform = getColorTransform();
+ }
+
+ const auto roundedCornerState = getRoundedCornerState();
+ layer.geometry.roundedCornersRadius = roundedCornerState.radius;
+ layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+ layer.alpha = alpha;
+ layer.sourceDataspace = mCurrentDataSpace;
+ return true;
}
void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
float alpha) const {
auto& engine(mFlinger->getRenderEngine());
- computeGeometry(renderArea, getBE().mMesh, false);
+ computeGeometry(renderArea, getCompositionLayer()->editState().reMesh, false);
engine.setupFillWithColor(red, green, blue, alpha);
- engine.drawMesh(getBE().mMesh);
+ engine.drawMesh(getCompositionLayer()->getState().reMesh);
}
void Layer::clearWithOpenGL(const RenderArea& renderArea) const {
clearWithOpenGL(renderArea, 0, 0, 0, 0);
}
-void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc) {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- ALOGE("setCompositionType called without a valid HWC layer");
- return;
- }
- auto& hwcInfo = getBE().mHwcLayers[hwcId];
- auto& hwcLayer = hwcInfo.layer;
- ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(),
- static_cast<int>(callIntoHwc));
- if (hwcInfo.compositionType != type) {
+void Layer::setCompositionType(const sp<const DisplayDevice>& display,
+ Hwc2::IComposerClient::Composition type) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer);
+ LOG_FATAL_IF(!outputLayer->getState().hwc);
+ auto& compositionState = outputLayer->editState();
+
+ ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", ((*compositionState.hwc).hwcLayer)->getId(),
+ toString(type).c_str(), 1);
+ if ((*compositionState.hwc).hwcCompositionType != type) {
ALOGV(" actually setting");
- hwcInfo.compositionType = type;
- if (callIntoHwc) {
- auto error = hwcLayer->setCompositionType(type);
- ALOGE_IF(error != HWC2::Error::None,
- "[%s] Failed to set "
- "composition type %s: %s (%d)",
- mName.string(), to_string(type).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- }
+ (*compositionState.hwc).hwcCompositionType = type;
+
+ auto error = (*compositionState.hwc)
+ .hwcLayer->setCompositionType(static_cast<HWC2::Composition>(type));
+ ALOGE_IF(error != HWC2::Error::None,
+ "[%s] Failed to set "
+ "composition type %s: %s (%d)",
+ mName.string(), toString(type).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
-HWC2::Composition Layer::getCompositionType(int32_t hwcId) const {
- if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
- // If we're querying the composition type for a display that does not
- // have a HWC counterpart, then it will always be Client
- return HWC2::Composition::Client;
- }
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- ALOGE("getCompositionType called with an invalid HWC layer");
- return HWC2::Composition::Invalid;
- }
- return getBE().mHwcLayers.at(hwcId).compositionType;
+Hwc2::IComposerClient::Composition Layer::getCompositionType(
+ const sp<const DisplayDevice>& display) const {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ LOG_FATAL_IF(!outputLayer);
+ return outputLayer->getState().hwc ? (*outputLayer->getState().hwc).hwcCompositionType
+ : Hwc2::IComposerClient::Composition::CLIENT;
}
-void Layer::setClearClientTarget(int32_t hwcId, bool clear) {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- ALOGE("setClearClientTarget called without a valid HWC layer");
- return;
- }
- getBE().mHwcLayers[hwcId].clearClientTarget = clear;
-}
-
-bool Layer::getClearClientTarget(int32_t hwcId) const {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- ALOGE("getClearClientTarget called without a valid HWC layer");
- return false;
- }
- return getBE().mHwcLayers.at(hwcId).clearClientTarget;
+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) {
@@ -786,6 +590,9 @@
// relevant frame
return false;
}
+ if (isRemovedFromCurrentState()) {
+ return false;
+ }
Mutex::Autolock lock(mLocalSyncPointMutex);
mLocalSyncPoints.push_back(point);
@@ -796,34 +603,18 @@
// local state
// ----------------------------------------------------------------------------
-static void boundPoint(vec2* point, const Rect& crop) {
- if (point->x < crop.left) {
- point->x = crop.left;
- }
- if (point->x > crop.right) {
- point->x = crop.right;
- }
- if (point->y < crop.top) {
- point->y = crop.top;
- }
- if (point->y > crop.bottom) {
- point->y = crop.bottom;
- }
-}
-
-void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh,
+void Layer::computeGeometry(const RenderArea& renderArea,
+ renderengine::Mesh& mesh,
bool useIdentityTransform) const {
- const Layer::State& s(getDrawingState());
- const Transform renderAreaTransform(renderArea.getTransform());
- const uint32_t height = renderArea.getHeight();
- FloatRect win = computeBounds();
+ const ui::Transform renderAreaTransform(renderArea.getTransform());
+ FloatRect win = getBounds();
vec2 lt = vec2(win.left, win.top);
vec2 lb = vec2(win.left, win.bottom);
vec2 rb = vec2(win.right, win.bottom);
vec2 rt = vec2(win.right, win.top);
- Transform layerTransform = getTransform();
+ ui::Transform layerTransform = getTransform();
if (!useIdentityTransform) {
lt = layerTransform.transform(lt);
lb = layerTransform.transform(lb);
@@ -831,25 +622,15 @@
rt = layerTransform.transform(rt);
}
- if (!s.finalCrop.isEmpty()) {
- boundPoint(<, s.finalCrop);
- boundPoint(&lb, s.finalCrop);
- boundPoint(&rb, s.finalCrop);
- boundPoint(&rt, s.finalCrop);
- }
-
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ renderengine::Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
position[0] = renderAreaTransform.transform(lt);
position[1] = renderAreaTransform.transform(lb);
position[2] = renderAreaTransform.transform(rb);
position[3] = renderAreaTransform.transform(rt);
- for (size_t i = 0; i < 4; i++) {
- position[i].y = height - position[i].y;
- }
}
bool Layer::isSecure() const {
- const Layer::State& s(mDrawingState);
+ const State& s(mDrawingState);
return (s.flags & layer_state_t::eLayerSecure);
}
@@ -885,22 +666,24 @@
// If this transaction is waiting on the receipt of a frame, generate a sync
// point and send it to the remote layer.
- if (mCurrentState.barrierLayer != nullptr) {
- sp<Layer> barrierLayer = mCurrentState.barrierLayer.promote();
+ // We don't allow installing sync points after we are removed from the current state
+ // as we won't be able to signal our end.
+ if (mCurrentState.barrierLayer_legacy != nullptr && !isRemovedFromCurrentState()) {
+ sp<Layer> barrierLayer = mCurrentState.barrierLayer_legacy.promote();
if (barrierLayer == nullptr) {
ALOGE("[%s] Unable to promote barrier Layer.", mName.string());
// If we can't promote the layer we are intended to wait on,
// then it is expired or otherwise invalid. Allow this transaction
// to be applied as per normal (no synchronization).
- mCurrentState.barrierLayer = nullptr;
+ mCurrentState.barrierLayer_legacy = nullptr;
} else {
- auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber);
+ auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy);
if (barrierLayer->addSyncPoint(syncPoint)) {
mRemoteSyncPoints.push_back(std::move(syncPoint));
} else {
// We already missed the frame we're supposed to synchronize
// on, so go ahead and apply the state update
- mCurrentState.barrierLayer = nullptr;
+ mCurrentState.barrierLayer_legacy = nullptr;
}
}
@@ -922,7 +705,7 @@
bool Layer::applyPendingStates(State* stateToCommit) {
bool stateUpdateAvailable = false;
while (!mPendingStates.empty()) {
- if (mPendingStates[0].barrierLayer != nullptr) {
+ if (mPendingStates[0].barrierLayer_legacy != nullptr) {
if (mRemoteSyncPoints.empty()) {
// If we don't have a sync point for this, apply it anyway. It
// will be visually wrong, but it should keep us from getting
@@ -933,7 +716,8 @@
continue;
}
- if (mRemoteSyncPoints.front()->getFrameNumber() != mPendingStates[0].frameNumber) {
+ if (mRemoteSyncPoints.front()->getFrameNumber() !=
+ mPendingStates[0].frameNumber_legacy) {
ALOGE("[%s] Unexpected sync point frame number found", mName.string());
// Signal our end of the sync point and then dispose of it
@@ -970,18 +754,11 @@
return stateUpdateAvailable;
}
-uint32_t Layer::doTransaction(uint32_t flags) {
- ATRACE_CALL();
+uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) {
+ const State& s(getDrawingState());
- pushPendingState();
- Layer::State c = getCurrentState();
- if (!applyPendingStates(&c)) {
- return 0;
- }
-
- const Layer::State& s(getDrawingState());
-
- const bool sizeChanged = (c.requested.w != s.requested.w) || (c.requested.h != s.requested.h);
+ const bool sizeChanged = (stateToCommit->requested_legacy.w != s.requested_legacy.w) ||
+ (stateToCommit->requested_legacy.h != s.requested_legacy.h);
if (sizeChanged) {
// the size changed, we need to ask our client to request a new buffer
@@ -991,16 +768,15 @@
" requested={ wh={%4u,%4u} }}\n"
" drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
" requested={ wh={%4u,%4u} }}\n",
- this, getName().string(), mCurrentTransform,
- getEffectiveScalingMode(), c.active.w, c.active.h, c.crop.left, c.crop.top,
- c.crop.right, c.crop.bottom, c.crop.getWidth(), c.crop.getHeight(), c.requested.w,
- c.requested.h, s.active.w, s.active.h, s.crop.left, s.crop.top, s.crop.right,
- s.crop.bottom, s.crop.getWidth(), s.crop.getHeight(), s.requested.w,
- s.requested.h);
-
- // record the new size, form this point on, when the client request
- // a buffer, it'll get the new size.
- setDefaultBufferSize(c.requested.w, c.requested.h);
+ this, getName().string(), mCurrentTransform, getEffectiveScalingMode(),
+ stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
+ stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
+ stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
+ stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(),
+ stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h,
+ s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top,
+ s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(),
+ s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
}
// Don't let Layer::doTransaction update the drawing state
@@ -1021,10 +797,12 @@
// resizePending state is to avoid applying the state of the new buffer
// to the old buffer. However in the state where we don't have an old buffer
// there is no such concern but we may still be being used as a parent layer.
- const bool resizePending = ((c.requested.w != c.active.w) || (c.requested.h != c.active.h)) &&
- (getBE().compositionInfo.mBuffer != nullptr);
+ const bool resizePending =
+ ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) ||
+ (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) &&
+ (mActiveBuffer != nullptr);
if (!isFixedSize()) {
- if (resizePending && getBE().compositionInfo.hwc.sidebandStream == nullptr) {
+ if (resizePending && mSidebandStream == nullptr) {
flags |= eDontUpdateGeometryState;
}
}
@@ -1033,7 +811,7 @@
// latching configuration. See Layer.h for a detailed discussion of
// how geometry latching is controlled.
if (!(flags & eDontUpdateGeometryState)) {
- Layer::State& editCurrentState(getCurrentState());
+ State& editCurrentState(getCurrentState());
// If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize
// mode, which causes attributes which normally latch regardless of scaling mode,
@@ -1045,21 +823,46 @@
// being stored in the same data structure while having different latching rules.
// b/38182305
//
- // Careful that "c" and editCurrentState may not begin as equivalent due to
+ // Careful that "stateToCommit" and editCurrentState may not begin as equivalent due to
// applyPendingStates in the presence of deferred transactions.
if (mFreezeGeometryUpdates) {
- float tx = c.active.transform.tx();
- float ty = c.active.transform.ty();
- c.active = c.requested;
- c.active.transform.set(tx, ty);
- editCurrentState.active = c.active;
+ float tx = stateToCommit->active_legacy.transform.tx();
+ float ty = stateToCommit->active_legacy.transform.ty();
+ stateToCommit->active_legacy = stateToCommit->requested_legacy;
+ stateToCommit->active_legacy.transform.set(tx, ty);
+ editCurrentState.active_legacy = stateToCommit->active_legacy;
} else {
- editCurrentState.active = editCurrentState.requested;
- c.active = c.requested;
+ editCurrentState.active_legacy = editCurrentState.requested_legacy;
+ stateToCommit->active_legacy = stateToCommit->requested_legacy;
}
}
- if (s.active != c.active) {
+ return flags;
+}
+
+uint32_t Layer::doTransaction(uint32_t flags) {
+ ATRACE_CALL();
+
+ if (mLayerDetached) {
+ return flags;
+ }
+
+ if (mChildrenChanged) {
+ flags |= eVisibleRegion;
+ mChildrenChanged = false;
+ }
+
+ pushPendingState();
+ State c = getCurrentState();
+ if (!applyPendingStates(&c)) {
+ return flags;
+ }
+
+ flags = doTransactionResize(flags, &c);
+
+ const State& s(getDrawingState());
+
+ if (getActiveGeometry(c) != getActiveGeometry(s)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
}
@@ -1070,19 +873,18 @@
this->contentDirty = true;
// we may use linear filtering, if the matrix scales us
- const uint8_t type = c.active.transform.getType();
- mNeedsFiltering = (!c.active.transform.preserveRects() || (type >= Transform::SCALE));
+ const uint8_t type = getActiveTransform(c).getType();
+ mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE);
}
- // If the layer is hidden, signal and clear out all local sync points so
- // that transactions for layers depending on this layer's frames becoming
- // visible are not blocked
- if (c.flags & layer_state_t::eLayerHidden) {
- clearSyncPoints();
+ if (mCurrentState.inputInfoChanged) {
+ flags |= eInputInfoChanged;
+ mCurrentState.inputInfoChanged = false;
}
// Commit the transaction
commitTransaction(c);
+ mCurrentState.callbackHandles = {};
return flags;
}
@@ -1091,28 +893,29 @@
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
- return android_atomic_and(~flags, &mTransactionFlags) & flags;
+ return mTransactionFlags.fetch_and(~flags) & flags;
}
uint32_t Layer::setTransactionFlags(uint32_t flags) {
- return android_atomic_or(flags, &mTransactionFlags);
+ return mTransactionFlags.fetch_or(flags);
}
bool Layer::setPosition(float x, float y, bool immediate) {
- if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y)
+ if (mCurrentState.requested_legacy.transform.tx() == x &&
+ mCurrentState.requested_legacy.transform.ty() == y)
return false;
mCurrentState.sequence++;
// We update the requested and active position simultaneously because
// we want to apply the position portion of the transform matrix immediately,
// but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
- mCurrentState.requested.transform.set(x, y);
+ mCurrentState.requested_legacy.transform.set(x, y);
if (immediate && !mFreezeGeometryUpdates) {
// Here we directly update the active state
// unlike other setters, because we store it within
// the transform, but use different latching rules.
// b/38182305
- mCurrentState.active.transform.set(x, y);
+ mCurrentState.active_legacy.transform.set(x, y);
}
mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
@@ -1160,7 +963,7 @@
if (strongRelative != nullptr) {
strongRelative->removeZOrderRelative(this);
}
- mCurrentState.zOrderRelativeOf = nullptr;
+ setZOrderRelativeOf(nullptr);
}
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1180,6 +983,13 @@
setTransactionFlags(eTransactionNeeded);
}
+void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) {
+ mCurrentState.zOrderRelativeOf = relativeOf;
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+}
+
bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
if (handle == nullptr) {
@@ -1191,7 +1001,7 @@
}
if (mCurrentState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
- mCurrentState.zOrderRelativeOf == relative) {
+ mCurrentState.zOrderRelativeOf == relative) {
return false;
}
@@ -1203,7 +1013,7 @@
if (oldZOrderRelativeOf != nullptr) {
oldZOrderRelativeOf->removeZOrderRelative(this);
}
- mCurrentState.zOrderRelativeOf = relative;
+ setZOrderRelativeOf(relative);
relative->addZOrderRelative(this);
setTransactionFlags(eTransactionNeeded);
@@ -1212,11 +1022,16 @@
}
bool Layer::setSize(uint32_t w, uint32_t h) {
- if (mCurrentState.requested.w == w && mCurrentState.requested.h == h) return false;
- mCurrentState.requested.w = w;
- mCurrentState.requested.h = h;
+ if (mCurrentState.requested_legacy.w == w && mCurrentState.requested_legacy.h == h)
+ return false;
+ mCurrentState.requested_legacy.w = w;
+ mCurrentState.requested_legacy.h = h;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+
+ // record the new size, from this point on, when the client request
+ // a buffer, it'll get the new size.
+ setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h);
return true;
}
bool Layer::setAlpha(float alpha) {
@@ -1228,15 +1043,48 @@
return true;
}
-bool Layer::setColor(const half3& color) {
- if (color.r == mCurrentState.color.r && color.g == mCurrentState.color.g &&
- color.b == mCurrentState.color.b)
+bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) {
+ if (!mCurrentState.bgColorLayer && alpha == 0) {
return false;
+ }
+ mCurrentState.sequence++;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ if (!mCurrentState.bgColorLayer && alpha != 0) {
+ // create background color layer if one does not yet exist
+ uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
+ const String8& name = mName + "BackgroundColorLayer";
+ mCurrentState.bgColorLayer = new ColorLayer(
+ LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata()));
+
+ // add to child list
+ addChild(mCurrentState.bgColorLayer);
+ mFlinger->mLayersAdded = true;
+ // set up SF to handle added color layer
+ if (isRemovedFromCurrentState()) {
+ mCurrentState.bgColorLayer->onRemovedFromCurrentState();
+ }
+ mFlinger->setTransactionFlags(eTransactionNeeded);
+ } else if (mCurrentState.bgColorLayer && alpha == 0) {
+ mCurrentState.bgColorLayer->reparent(nullptr);
+ mCurrentState.bgColorLayer = nullptr;
+ return true;
+ }
+
+ mCurrentState.bgColorLayer->setColor(color);
+ mCurrentState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min());
+ mCurrentState.bgColorLayer->setAlpha(alpha);
+ mCurrentState.bgColorLayer->setDataspace(dataspace);
+
+ return true;
+}
+
+bool Layer::setCornerRadius(float cornerRadius) {
+ if (mCurrentState.cornerRadius == cornerRadius) return false;
mCurrentState.sequence++;
- mCurrentState.color.r = color.r;
- mCurrentState.color.g = color.g;
- mCurrentState.color.b = color.b;
+ mCurrentState.cornerRadius = cornerRadius;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1244,7 +1092,7 @@
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
- Transform t;
+ ui::Transform t;
t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
@@ -1252,13 +1100,15 @@
return false;
}
mCurrentState.sequence++;
- mCurrentState.requested.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+ mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx,
+ matrix.dsdy);
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
+
bool Layer::setTransparentRegionHint(const Region& transparent) {
- mCurrentState.requestedTransparentRegion = transparent;
+ mCurrentState.requestedTransparentRegion_legacy = transparent;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1273,26 +1123,12 @@
return true;
}
-bool Layer::setCrop(const Rect& crop, bool immediate) {
- if (mCurrentState.requestedCrop == crop) return false;
+bool Layer::setCrop_legacy(const Rect& crop, bool immediate) {
+ if (mCurrentState.requestedCrop_legacy == crop) return false;
mCurrentState.sequence++;
- mCurrentState.requestedCrop = crop;
+ mCurrentState.requestedCrop_legacy = crop;
if (immediate && !mFreezeGeometryUpdates) {
- mCurrentState.crop = crop;
- }
- mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
-
- mCurrentState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFinalCrop(const Rect& crop, bool immediate) {
- if (mCurrentState.requestedFinalCrop == crop) return false;
- mCurrentState.sequence++;
- mCurrentState.requestedFinalCrop = crop;
- if (immediate && !mFreezeGeometryUpdates) {
- mCurrentState.finalCrop = crop;
+ mCurrentState.crop_legacy = crop;
}
mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
@@ -1308,11 +1144,12 @@
return true;
}
-void Layer::setInfo(int32_t type, int32_t appId) {
- mCurrentState.appId = appId;
- mCurrentState.type = type;
+bool Layer::setMetadata(const LayerMetadata& data) {
+ if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
+ mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+ return true;
}
bool Layer::setLayerStack(uint32_t layerStack) {
@@ -1324,6 +1161,17 @@
return true;
}
+bool Layer::setColorSpaceAgnostic(const bool agnostic) {
+ if (mCurrentState.colorSpaceAgnostic == agnostic) {
+ return false;
+ }
+ mCurrentState.sequence++;
+ mCurrentState.colorSpaceAgnostic = agnostic;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
uint32_t Layer::getLayerStack() const {
auto p = mDrawingParent.promote();
if (p == nullptr) {
@@ -1332,30 +1180,29 @@
return p->getLayerStack();
}
-void Layer::deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
- mCurrentState.barrierLayer = barrierLayer;
- mCurrentState.frameNumber = frameNumber;
+void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
+ mCurrentState.barrierLayer_legacy = barrierLayer;
+ mCurrentState.frameNumber_legacy = frameNumber;
// We don't set eTransactionNeeded, because just receiving a deferral
// request without any other state updates shouldn't actually induce a delay
mCurrentState.modified = true;
pushPendingState();
- mCurrentState.barrierLayer = nullptr;
- mCurrentState.frameNumber = 0;
+ mCurrentState.barrierLayer_legacy = nullptr;
+ mCurrentState.frameNumber_legacy = 0;
mCurrentState.modified = false;
}
-void Layer::deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber) {
+void Layer::deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, uint64_t frameNumber) {
sp<Handle> handle = static_cast<Handle*>(barrierHandle.get());
- deferTransactionUntil(handle->owner.promote(), frameNumber);
+ deferTransactionUntil_legacy(handle->owner.promote(), frameNumber);
}
-
// ----------------------------------------------------------------------------
// pageflip handling...
// ----------------------------------------------------------------------------
bool Layer::isHiddenByPolicy() const {
- const Layer::State& s(mDrawingState);
+ const State& s(mDrawingState);
const auto& parent = mDrawingParent.promote();
if (parent != nullptr && parent->isHiddenByPolicy()) {
return true;
@@ -1376,15 +1223,18 @@
return usage;
}
-void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const {
+void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const {
uint32_t orientation = 0;
- if (!mFlinger->mDebugDisableTransformHint) {
+ // Disable setting transform hint if the debug flag is set or if the
+ // getTransformToDisplayInverse flag is set and the client wants to submit buffers
+ // in one orientation.
+ if (!mFlinger->mDebugDisableTransformHint && !getTransformToDisplayInverse()) {
// The transform hint is used to improve performance, but we can
// only have a single transform hint, it cannot
// apply to all displays.
- const Transform& planeTransform(hw->getTransform());
+ const ui::Transform& planeTransform = display->getTransform();
orientation = planeTransform.getOrientation();
- if (orientation & Transform::ROT_INVALID) {
+ if (orientation & ui::Transform::ROT_INVALID) {
orientation = 0;
}
}
@@ -1395,34 +1245,34 @@
// debugging
// ----------------------------------------------------------------------------
+// TODO(marissaw): add new layer state info to layer debugging
LayerDebugInfo Layer::getLayerDebugInfo() const {
LayerDebugInfo info;
- const Layer::State& ds = getDrawingState();
+ const State& ds = getDrawingState();
info.mName = getName();
sp<Layer> parent = getParent();
info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
- info.mType = String8(getTypeId());
- info.mTransparentRegion = ds.activeTransparentRegion;
+ info.mType = std::string(getTypeId());
+ info.mTransparentRegion = ds.activeTransparentRegion_legacy;
info.mVisibleRegion = visibleRegion;
info.mSurfaceDamageRegion = surfaceDamageRegion;
info.mLayerStack = getLayerStack();
- info.mX = ds.active.transform.tx();
- info.mY = ds.active.transform.ty();
+ info.mX = ds.active_legacy.transform.tx();
+ info.mY = ds.active_legacy.transform.ty();
info.mZ = ds.z;
- info.mWidth = ds.active.w;
- info.mHeight = ds.active.h;
- info.mCrop = ds.crop;
- info.mFinalCrop = ds.finalCrop;
+ info.mWidth = ds.active_legacy.w;
+ info.mHeight = ds.active_legacy.h;
+ info.mCrop = ds.crop_legacy;
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace);
- info.mMatrix[0][0] = ds.active.transform[0][0];
- info.mMatrix[0][1] = ds.active.transform[0][1];
- info.mMatrix[1][0] = ds.active.transform[1][0];
- info.mMatrix[1][1] = ds.active.transform[1][1];
+ info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
+ info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
+ info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
+ info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
{
- sp<const GraphicBuffer> buffer = getBE().compositionInfo.mBuffer;
+ sp<const GraphicBuffer> buffer = mActiveBuffer;
if (buffer != 0) {
info.mActiveBufferWidth = buffer->getWidth();
info.mActiveBufferHeight = buffer->getHeight();
@@ -1442,54 +1292,65 @@
return info;
}
-void Layer::miniDumpHeader(String8& result) {
- result.append("----------------------------------------");
- result.append("---------------------------------------\n");
+void Layer::miniDumpHeader(std::string& result) {
+ result.append("-------------------------------");
+ result.append("-------------------------------");
+ result.append("-----------------------------\n");
result.append(" Layer name\n");
result.append(" Z | ");
result.append(" Comp Type | ");
+ result.append(" Transform | ");
result.append(" Disp Frame (LTRB) | ");
result.append(" Source Crop (LTRB)\n");
- result.append("----------------------------------------");
- result.append("---------------------------------------\n");
+ result.append("-------------------------------");
+ result.append("-------------------------------");
+ result.append("-----------------------------\n");
}
-void Layer::miniDump(String8& result, int32_t hwcId) const {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
+void Layer::miniDump(std::string& result, const sp<DisplayDevice>& displayDevice) const {
+ auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ if (!outputLayer) {
return;
}
- String8 name;
+ std::string name;
if (mName.length() > 77) {
std::string shortened;
shortened.append(mName.string(), 36);
shortened.append("[...]");
shortened.append(mName.string() + (mName.length() - 36), 36);
- name = shortened.c_str();
+ name = shortened;
} else {
- name = mName;
+ name = std::string(mName.string(), mName.size());
}
- result.appendFormat(" %s\n", name.string());
+ StringAppendF(&result, " %s\n", name.c_str());
- const Layer::State& layerState(getDrawingState());
- const LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers.at(hwcId);
+ const State& layerState(getDrawingState());
+ const auto& compositionState = outputLayer->getState();
+
if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
- result.appendFormat(" rel %6d | ", layerState.z);
+ StringAppendF(&result, " rel %6d | ", layerState.z);
} else {
- result.appendFormat(" %10d | ", layerState.z);
+ StringAppendF(&result, " %10d | ", layerState.z);
}
- result.appendFormat("%10s | ", to_string(getCompositionType(hwcId)).c_str());
- const Rect& frame = hwcInfo.displayFrame;
- result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
- const FloatRect& crop = hwcInfo.sourceCrop;
- result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right, crop.bottom);
+ StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
+ StringAppendF(&result, "%10s | ",
+ toString(getCompositionLayer() ? compositionState.bufferTransform
+ : static_cast<Hwc2::Transform>(0))
+ .c_str());
+ const Rect& frame = compositionState.displayFrame;
+ StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
+ const FloatRect& crop = compositionState.sourceCrop;
+ StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right,
+ crop.bottom);
- result.append("- - - - - - - - - - - - - - - - - - - - ");
- result.append("- - - - - - - - - - - - - - - - - - - -\n");
+ result.append("- - - - - - - - - - - - - - - -");
+ result.append("- - - - - - - - - - - - - - - -");
+ result.append("- - - - - - - - - - - - - - -\n");
}
-void Layer::dumpFrameStats(String8& result) const {
+void Layer::dumpFrameStats(std::string& result) const {
mFrameTracker.dumpStats(result);
}
@@ -1505,8 +1366,8 @@
mFrameTracker.getStats(outStats);
}
-void Layer::dumpFrameEvents(String8& result) {
- result.appendFormat("- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this);
+void Layer::dumpFrameEvents(std::string& result) {
+ StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this);
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.checkFencesForCompletion();
mFrameEventHistory.dump(result);
@@ -1515,14 +1376,14 @@
void Layer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.onDisconnect();
- mTimeStats.onDisconnect(getName().c_str());
+ mFlinger->mTimeStats->onDestroy(getSequence());
}
void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
- mTimeStats.setPostTime(getName().c_str(), newTimestamps->frameNumber,
- newTimestamps->postedTime);
+ mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
+ getName().c_str(), newTimestamps->postedTime);
}
Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1551,11 +1412,17 @@
}
void Layer::addChild(const sp<Layer>& layer) {
+ mChildrenChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+
mCurrentChildren.add(layer);
layer->setParent(this);
}
ssize_t Layer::removeChild(const sp<Layer>& layer) {
+ mChildrenChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+
layer->setParent(nullptr);
return mCurrentChildren.remove(layer);
}
@@ -1573,13 +1440,11 @@
return false;
}
+ if (attachChildren()) {
+ setTransactionFlags(eTransactionNeeded);
+ }
for (const sp<Layer>& child : mCurrentChildren) {
newParent->addChild(child);
-
- sp<Client> client(child->mClientRef.promote());
- if (client != nullptr) {
- client->updateParent(newParent);
- }
}
mCurrentChildren.clear();
@@ -1589,34 +1454,62 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
+ child->computeBounds(newParent->mBounds,
+ newParent->getTransformWithScale(
+ newParent->getBufferScaleTransform()));
}
}
bool Layer::reparent(const sp<IBinder>& newParentHandle) {
- if (newParentHandle == nullptr) {
+ bool callSetTransactionFlags = false;
+
+ // While layers are detached, we allow most operations
+ // and simply halt performing the actual transaction. However
+ // for reparent != null we would enter the mRemovedFromCurrentState
+ // state, regardless of whether doTransaction was called, and
+ // so we need to prevent the update here.
+ if (mLayerDetached && newParentHandle == nullptr) {
return false;
}
- auto handle = static_cast<Handle*>(newParentHandle.get());
- sp<Layer> newParent = handle->owner.promote();
- if (newParent == nullptr) {
- ALOGE("Unable to promote Layer handle");
- return false;
+ sp<Layer> newParent;
+ if (newParentHandle != nullptr) {
+ auto handle = static_cast<Handle*>(newParentHandle.get());
+ newParent = handle->owner.promote();
+ if (newParent == nullptr) {
+ ALOGE("Unable to promote Layer handle");
+ return false;
+ }
+ if (newParent == this) {
+ ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str());
+ return false;
+ }
}
sp<Layer> parent = getParent();
if (parent != nullptr) {
parent->removeChild(this);
}
- newParent->addChild(this);
- sp<Client> client(mClientRef.promote());
- sp<Client> newParentClient(newParent->mClientRef.promote());
+ if (newParentHandle != nullptr) {
+ newParent->addChild(this);
+ if (!newParent->isRemovedFromCurrentState()) {
+ addToCurrentState();
+ } else {
+ onRemovedFromCurrentState();
+ }
- if (client != newParentClient) {
- client->updateParent(newParent);
+ if (mLayerDetached) {
+ mLayerDetached = false;
+ callSetTransactionFlags = true;
+ }
+ } else {
+ onRemovedFromCurrentState();
}
+ if (callSetTransactionFlags || attachChildren()) {
+ setTransactionFlags(eTransactionNeeded);
+ }
return true;
}
@@ -1625,7 +1518,7 @@
sp<Client> parentClient = mClientRef.promote();
sp<Client> client(child->mClientRef.promote());
if (client != nullptr && parentClient != client) {
- client->detachLayer(child.get());
+ child->mLayerDetached = true;
child->detachChildren();
}
}
@@ -1633,6 +1526,53 @@
return true;
}
+bool Layer::attachChildren() {
+ bool changed = false;
+ for (const sp<Layer>& child : mCurrentChildren) {
+ sp<Client> parentClient = mClientRef.promote();
+ sp<Client> client(child->mClientRef.promote());
+ if (client != nullptr && parentClient != client) {
+ if (child->mLayerDetached) {
+ child->mLayerDetached = false;
+ changed = true;
+ }
+ changed |= child->attachChildren();
+ }
+ }
+
+ return changed;
+}
+
+bool Layer::setColorTransform(const mat4& matrix) {
+ static const mat4 identityMatrix = mat4();
+
+ if (mCurrentState.colorTransform == matrix) {
+ return false;
+ }
+ ++mCurrentState.sequence;
+ mCurrentState.colorTransform = matrix;
+ mCurrentState.hasColorTransform = matrix != identityMatrix;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+mat4 Layer::getColorTransform() const {
+ mat4 colorTransform = mat4(getDrawingState().colorTransform);
+ if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
+ colorTransform = parent->getColorTransform() * colorTransform;
+ }
+ return colorTransform;
+}
+
+bool Layer::hasColorTransform() const {
+ bool hasColorTransform = getDrawingState().hasColorTransform;
+ if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
+ hasColorTransform = hasColorTransform || parent->hasColorTransform();
+ }
+ return hasColorTransform;
+}
+
bool Layer::isLegacyDataSpace() const {
// return true when no higher bits are set
return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK |
@@ -1643,18 +1583,6 @@
mCurrentParent = layer;
}
-void Layer::clearSyncPoints() {
- for (const auto& child : mCurrentChildren) {
- child->clearSyncPoints();
- }
-
- Mutex::Autolock lock(mLocalSyncPointMutex);
- for (auto& point : mLocalSyncPoints) {
- point->setFrameAvailable();
- }
- mLocalSyncPoints.clear();
-}
-
int32_t Layer::getZ() const {
return mDrawingState.z;
}
@@ -1843,35 +1771,8 @@
traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
}
-Transform Layer::getTransform() const {
- Transform t;
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- t = p->getTransform();
-
- // If the parent is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
- // it isFixedSize) then there may be additional scaling not accounted
- // for in the transform. We need to mirror this scaling in child surfaces
- // or we will break the contract where WM can treat child surfaces as
- // pixels in the parent surface.
- if (p->isFixedSize() && p->getBE().compositionInfo.mBuffer != nullptr) {
- int bufferWidth;
- int bufferHeight;
- if ((p->mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
- bufferWidth = p->getBE().compositionInfo.mBuffer->getWidth();
- bufferHeight = p->getBE().compositionInfo.mBuffer->getHeight();
- } else {
- bufferHeight = p->getBE().compositionInfo.mBuffer->getWidth();
- bufferWidth = p->getBE().compositionInfo.mBuffer->getHeight();
- }
- float sx = p->getDrawingState().active.w / static_cast<float>(bufferWidth);
- float sy = p->getDrawingState().active.h / static_cast<float>(bufferHeight);
- Transform extraParentScaling;
- extraParentScaling.set(sx, 0, 0, sy);
- t = t * extraParentScaling;
- }
- }
- return t * getDrawingState().active.transform;
+ui::Transform Layer::getTransform() const {
+ return mEffectiveTransform;
}
half Layer::getAlpha() const {
@@ -1886,6 +1787,32 @@
return half4(color.r, color.g, color.b, getAlpha());
}
+Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+ return getRoundedCornerStateInternal(mSourceBounds);
+}
+
+Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect bounds) const {
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
+ if (parentState.radius > 0) {
+ ui::Transform t = getActiveTransform(getDrawingState());
+ t = t.inverse();
+ parentState.cropRect = t.transform(parentState.cropRect);
+ // The rounded corners shader only accepts 1 corner radius for performance reasons,
+ // 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;
+ return parentState;
+ }
+ }
+ const float radius = getDrawingState().cornerRadius;
+ return radius > 0
+ ? RoundedCornerState(bounds.intersect(getCrop(getDrawingState()).toFloatRect()), radius)
+ : RoundedCornerState();
+}
+
void Layer::commitChildList() {
for (size_t i = 0; i < mCurrentChildren.size(); i++) {
const auto& child = mCurrentChildren[i];
@@ -1895,13 +1822,36 @@
mDrawingParent = mCurrentParent;
}
+static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
+ if (weakBinderHandle == nullptr) {
+ return nullptr;
+ }
+ sp<IBinder> binderHandle = weakBinderHandle.promote();
+ if (binderHandle == nullptr) {
+ return nullptr;
+ }
+ sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
+ if (handle == nullptr) {
+ return nullptr;
+ }
+ return handle->owner;
+}
+
+void Layer::setInputInfo(const InputWindowInfo& info) {
+ mCurrentState.inputInfo = info;
+ mCurrentState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
+ mCurrentState.modified = true;
+ mCurrentState.inputInfoChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+}
+
void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
- Transform requestedTransform = state.active.transform;
- Transform transform = getTransform();
+ ui::Transform requestedTransform = state.active_legacy.transform;
+ ui::Transform transform = getTransform();
layerInfo->set_id(sequence);
layerInfo->set_name(getName().c_str());
@@ -1918,28 +1868,28 @@
}
}
- LayerProtoHelper::writeToProto(state.activeTransparentRegion,
- layerInfo->mutable_transparent_region());
- LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region());
- LayerProtoHelper::writeToProto(surfaceDamageRegion, layerInfo->mutable_damage_region());
+ LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
+ [&]() { return layerInfo->mutable_transparent_region(); });
+ LayerProtoHelper::writeToProto(visibleRegion,
+ [&]() { return layerInfo->mutable_visible_region(); });
+ LayerProtoHelper::writeToProto(surfaceDamageRegion,
+ [&]() { return layerInfo->mutable_damage_region(); });
layerInfo->set_layer_stack(getLayerStack());
layerInfo->set_z(state.z);
- PositionProto* position = layerInfo->mutable_position();
- position->set_x(transform.tx());
- position->set_y(transform.ty());
+ LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+ [&]() { return layerInfo->mutable_position(); });
- PositionProto* requestedPosition = layerInfo->mutable_requested_position();
- requestedPosition->set_x(requestedTransform.tx());
- requestedPosition->set_y(requestedTransform.ty());
+ LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+ return layerInfo->mutable_requested_position();
+ });
- SizeProto* size = layerInfo->mutable_size();
- size->set_w(state.active.w);
- size->set_h(state.active.h);
+ LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+ [&]() { return layerInfo->mutable_size(); });
- LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop());
- LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop());
+ LayerProtoHelper::writeToProto(state.crop_legacy, [&]() { return layerInfo->mutable_crop(); });
+ layerInfo->set_corner_radius(getRoundedCornerState().radius);
layerInfo->set_is_opaque(isOpaque(state));
layerInfo->set_invalidate(contentDirty);
@@ -1948,8 +1898,9 @@
layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace)));
layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
- LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color());
- LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color());
+ LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
+ LayerProtoHelper::writeToProto(state.color,
+ [&]() { return layerInfo->mutable_requested_color(); });
layerInfo->set_flags(state.flags);
LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
@@ -1958,53 +1909,74 @@
auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
if (parent != nullptr) {
layerInfo->set_parent(parent->sequence);
+ } else {
+ layerInfo->set_parent(-1);
}
auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
if (zOrderRelativeOf != nullptr) {
layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
+ } else {
+ layerInfo->set_z_order_relative_of(-1);
}
- // XXX getBE().compositionInfo.mBuffer is not protected
- auto buffer = getBE().compositionInfo.mBuffer;
+ auto buffer = mActiveBuffer;
if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer());
+ LayerProtoHelper::writeToProto(buffer,
+ [&]() { return layerInfo->mutable_active_buffer(); });
+ LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform),
+ layerInfo->mutable_buffer_transform());
}
layerInfo->set_queued_frames(getQueuedFrameCount());
layerInfo->set_refresh_pending(isBufferLatched());
- layerInfo->set_window_type(state.type);
- layerInfo->set_app_id(state.appId);
layerInfo->set_curr_frame(mCurrentFrameNumber);
+ layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
for (const auto& pendingState : mPendingStates) {
- auto barrierLayer = pendingState.barrierLayer.promote();
+ auto barrierLayer = pendingState.barrierLayer_legacy.promote();
if (barrierLayer != nullptr) {
BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
barrierLayerProto->set_id(barrierLayer->sequence);
- barrierLayerProto->set_frame_number(pendingState.frameNumber);
+ barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
}
}
+
+ auto protoMap = layerInfo->mutable_metadata();
+ for (const auto& entry : state.metadata.mMap) {
+ (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+ }
+ LayerProtoHelper::writeToProto(mEffectiveTransform, layerInfo->mutable_effective_transform());
+ LayerProtoHelper::writeToProto(mSourceBounds,
+ [&]() { return layerInfo->mutable_source_bounds(); });
+ LayerProtoHelper::writeToProto(mScreenBounds,
+ [&]() { return layerInfo->mutable_screen_bounds(); });
+ LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
}
-void Layer::writeToProto(LayerProto* layerInfo, int32_t hwcId) {
- if (!hasHwcLayer(hwcId)) {
+void Layer::writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice) {
+ auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ if (!outputLayer) {
return;
}
+
writeToProto(layerInfo, LayerVector::StateSet::Drawing);
- const auto& hwcInfo = getBE().mHwcLayers.at(hwcId);
+ const auto& compositionState = outputLayer->getState();
- const Rect& frame = hwcInfo.displayFrame;
- LayerProtoHelper::writeToProto(frame, layerInfo->mutable_hwc_frame());
+ const Rect& frame = compositionState.displayFrame;
+ LayerProtoHelper::writeToProto(frame, [&]() { return layerInfo->mutable_hwc_frame(); });
- const FloatRect& crop = hwcInfo.sourceCrop;
- LayerProtoHelper::writeToProto(crop, layerInfo->mutable_hwc_crop());
+ const FloatRect& crop = compositionState.sourceCrop;
+ LayerProtoHelper::writeToProto(crop, [&]() { return layerInfo->mutable_hwc_crop(); });
- const int32_t transform = static_cast<int32_t>(hwcInfo.transform);
+ const int32_t transform =
+ getCompositionLayer() ? static_cast<int32_t>(compositionState.bufferTransform) : 0;
layerInfo->set_hwc_transform(transform);
- const int32_t compositionType = static_cast<int32_t>(hwcInfo.compositionType);
+ const int32_t compositionType =
+ static_cast<int32_t>(compositionState.hwc ? (*compositionState.hwc).hwcCompositionType
+ : Hwc2::IComposerClient::Composition::CLIENT);
layerInfo->set_hwc_composition_type(compositionType);
if (std::strcmp(getTypeId(), "BufferLayer") == 0 &&
@@ -2015,6 +1987,79 @@
}
}
+bool Layer::isRemovedFromCurrentState() const {
+ return mRemovedFromCurrentState;
+}
+
+InputWindowInfo Layer::fillInputInfo() {
+ InputWindowInfo info = mDrawingState.inputInfo;
+
+ if (info.displayId == ADISPLAY_ID_NONE) {
+ info.displayId = mDrawingState.layerStack;
+ }
+
+ ui::Transform t = getTransform();
+ const float xScale = t.sx();
+ const float yScale = t.sy();
+ if (xScale != 1.0f || yScale != 1.0f) {
+ info.windowXScale *= 1.0f / xScale;
+ info.windowYScale *= 1.0f / yScale;
+ info.touchableRegion.scaleSelf(xScale, 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.
+ Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+ ? getBufferSize(getDrawingState())
+ : info.touchableRegion.getBounds();
+ if (!layerBounds.isValid()) {
+ layerBounds = getCroppedBufferSize(getDrawingState());
+ }
+ layerBounds = t.transform(layerBounds);
+ layerBounds.inset(info.surfaceInset, info.surfaceInset, info.surfaceInset, info.surfaceInset);
+
+ // Input coordinate should match the layer bounds.
+ info.frameLeft = layerBounds.left;
+ info.frameTop = layerBounds.top;
+ info.frameRight = layerBounds.right;
+ info.frameBottom = layerBounds.bottom;
+
+ // 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();
+
+ auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+ if (info.replaceTouchableRegionWithCrop) {
+ if (cropLayer == nullptr) {
+ info.touchableRegion = Region(Rect{mScreenBounds});
+ } else {
+ info.touchableRegion = Region(Rect{cropLayer->mScreenBounds});
+ }
+ } else if (cropLayer != nullptr) {
+ info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
+ }
+
+ return info;
+}
+
+bool Layer::hasInput() const {
+ return mDrawingState.inputInfo.token != nullptr;
+}
+
+std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const {
+ return nullptr;
+}
+
+bool Layer::canReceiveInput() const {
+ return isVisible();
+}
+
+compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
+ const sp<const DisplayDevice>& display) const {
+ return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionLayer().get());
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 239f397..b7cfc16 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -19,40 +19,40 @@
#include <sys/types.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
+#include <compositionengine/LayerFE.h>
+#include <gui/BufferQueue.h>
+#include <gui/ISurfaceComposerClient.h>
+#include <gui/LayerState.h>
+#include <input/InputWindow.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <math/vec4.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
#include <ui/FloatRect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
+#include <ui/Transform.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
-#include <gui/ISurfaceComposerClient.h>
-#include <gui/LayerState.h>
-#include <gui/BufferQueue.h>
-
-#include <list>
#include <cstdint>
+#include <list>
+#include <optional>
+#include <vector>
#include "Client.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
-#include "TimeStats/TimeStats.h"
-#include "Transform.h"
+#include "TransactionCompletedThread.h"
-#include <layerproto/LayerProtoHeader.h>
+#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWComposerBufferCache.h"
#include "RenderArea.h"
-#include "RenderEngine/Mesh.h"
-#include "RenderEngine/Texture.h"
-
-#include <math/vec4.h>
-#include <vector>
using namespace android::surfaceflinger;
@@ -66,7 +66,12 @@
class GraphicBuffer;
class SurfaceFlinger;
class LayerDebugInfo;
-class LayerBE;
+
+namespace compositionengine {
+class Layer;
+class OutputLayer;
+struct LayerFECompositionState;
+}
namespace impl {
class SurfaceInterceptor;
@@ -74,77 +79,26 @@
// ---------------------------------------------------------------------------
-struct CompositionInfo {
- HWC2::Composition compositionType;
- sp<GraphicBuffer> mBuffer = nullptr;
- int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
- struct {
- HWComposer* hwc;
- sp<Fence> fence;
- HWC2::BlendMode blendMode;
- Rect displayFrame;
- float alpha;
- FloatRect sourceCrop;
- HWC2::Transform transform;
- int z;
- int type;
- int appId;
- Region visibleRegion;
- Region surfaceDamage;
- sp<NativeHandle> sidebandStream;
- android_dataspace dataspace;
- hwc_color_t color;
- } hwc;
- struct {
- RE::RenderEngine* renderEngine;
- Mesh* mesh;
- } renderEngine;
+struct LayerCreationArgs {
+ LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
+ uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
+ : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags),
+ metadata(std::move(metadata)) {}
+
+ SurfaceFlinger* flinger;
+ const sp<Client>& client;
+ const String8& name;
+ uint32_t w;
+ uint32_t h;
+ uint32_t flags;
+ LayerMetadata metadata;
};
-class LayerBE {
-public:
- LayerBE();
-
- // The mesh used to draw the layer in GLES composition mode
- Mesh mMesh;
-
- // HWC items, accessed from the main thread
- struct HWCInfo {
- HWCInfo()
- : hwc(nullptr),
- layer(nullptr),
- forceClientComposition(false),
- compositionType(HWC2::Composition::Invalid),
- clearClientTarget(false),
- transform(HWC2::Transform::None) {}
-
- HWComposer* hwc;
- HWC2::Layer* layer;
- bool forceClientComposition;
- HWC2::Composition compositionType;
- bool clearClientTarget;
- Rect displayFrame;
- FloatRect sourceCrop;
- HWComposerBufferCache bufferCache;
- HWC2::Transform transform;
- };
-
- // A layer can be attached to multiple displays when operating in mirror mode
- // (a.k.a: when several displays are attached with equal layerStack). In this
- // case we need to keep track. In non-mirror mode, a layer will have only one
- // HWCInfo. This map key is a display layerStack.
- std::unordered_map<int32_t, HWCInfo> mHwcLayers;
-
- CompositionInfo compositionInfo;
-};
-
-class Layer : public virtual RefBase {
- static int32_t sSequence;
+class Layer : public virtual compositionengine::LayerFE {
+ static std::atomic<int32_t> sSequence;
public:
- LayerBE& getBE() { return mBE; }
- LayerBE& getBE() const { return mBE; }
- mutable bool contentDirty;
+ mutable bool contentDirty{false};
// regions below are in window-manager space
Region visibleRegion;
Region coveredRegion;
@@ -154,17 +108,18 @@
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
// the same.
- int32_t sequence;
+ int32_t sequence{sSequence++};
enum { // flags for doTransaction()
eDontUpdateGeometryState = 0x00000001,
eVisibleRegion = 0x00000002,
+ eInputInfoChanged = 0x00000004
};
struct Geometry {
uint32_t w;
uint32_t h;
- Transform transform;
+ ui::Transform transform;
inline bool operator==(const Geometry& rhs) const {
return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) &&
@@ -173,9 +128,20 @@
inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
};
+ struct RoundedCornerState {
+ RoundedCornerState() = default;
+ RoundedCornerState(FloatRect cropRect, float radius)
+ : cropRect(cropRect), radius(radius) {}
+
+ // Rounded rectangle in local layer coordinate space.
+ FloatRect cropRect = FloatRect();
+ // Radius of the rounded rectangle.
+ float radius = 0.0f;
+ };
+
struct State {
- Geometry active;
- Geometry requested;
+ Geometry active_legacy;
+ Geometry requested_legacy;
int32_t z;
// The identifier of the layer stack this layer belongs to. A layer can
@@ -191,26 +157,21 @@
bool modified;
// Crop is expressed in layer space coordinate.
- Rect crop;
- Rect requestedCrop;
-
- // finalCrop is expressed in display space coordinate.
- Rect finalCrop;
- Rect requestedFinalCrop;
+ Rect crop_legacy;
+ Rect requestedCrop_legacy;
// If set, defers this state update until the identified Layer
// receives a frame with the given frameNumber
- wp<Layer> barrierLayer;
- uint64_t frameNumber;
+ wp<Layer> barrierLayer_legacy;
+ uint64_t frameNumber_legacy;
// the transparentRegion hint is a bit special, it's latched only
// when we receive a buffer -- this is because it's "content"
// dependent.
- Region activeTransparentRegion;
- Region requestedTransparentRegion;
+ Region activeTransparentRegion_legacy;
+ Region requestedTransparentRegion_legacy;
- int32_t appId;
- int32_t type;
+ LayerMetadata metadata;
// If non-null, a Surface this Surface's Z-order is interpreted relative to.
wp<Layer> zOrderRelativeOf;
@@ -219,13 +180,50 @@
SortedVector<wp<Layer>> zOrderRelatives;
half4 color;
+ float cornerRadius;
+
+ bool inputInfoChanged;
+ InputWindowInfo inputInfo;
+ wp<Layer> touchableRegionCrop;
+
+ // dataspace is only used by BufferStateLayer and ColorLayer
+ ui::Dataspace dataspace;
+
+ // The fields below this point are only used by BufferStateLayer
+ Geometry active;
+
+ uint32_t transform;
+ bool transformToDisplayInverse;
+
+ Rect crop;
+ Region transparentRegionHint;
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> acquireFence;
+ HdrMetadata hdrMetadata;
+ Region surfaceDamageRegion;
+ int32_t api;
+
+ sp<NativeHandle> sidebandStream;
+ mat4 colorTransform;
+ bool hasColorTransform;
+
+ // pointer to background color layer that, if set, appears below the buffer state layer
+ // and the buffer state layer's children. Z order will be set to
+ // INT_MIN
+ sp<Layer> bgColorLayer;
+
+ // The deque of callback handles for this frame. The back of the deque contains the most
+ // recent callback handle.
+ std::deque<sp<CallbackHandle>> callbackHandles;
+ bool colorSpaceAgnostic;
};
- Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w,
- uint32_t h, uint32_t flags);
+ explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+ bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
// ------------------------------------------------------------------------
// Geometry setting functions.
@@ -254,11 +252,12 @@
// also the rendered size of the layer prior to any transformations. Parent
// or local matrix transformations will not affect the size of the buffer,
// but may affect it's on-screen size or clipping.
- bool setSize(uint32_t w, uint32_t h);
+ virtual bool setSize(uint32_t w, uint32_t h);
// Set a 2x2 transformation matrix on the layer. This transform
// will be applied after parent transforms, but before any final
// producer specified transform.
- bool setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms);
+ virtual bool setMatrix(const layer_state_t::matrix22_t& matrix,
+ bool allowNonRectPreservingTransforms);
// This second set of geometry attributes are controlled by
// setGeometryAppliesWithResize, and their default mode is to be
@@ -268,32 +267,63 @@
// setPosition operates in parent buffer space (pre parent-transform) or display
// space for top-level layers.
- bool setPosition(float x, float y, bool immediate);
+ virtual bool setPosition(float x, float y, bool immediate);
// Buffer space
- bool setCrop(const Rect& crop, bool immediate);
- // Parent buffer space/display space
- bool setFinalCrop(const Rect& crop, bool immediate);
+ virtual bool setCrop_legacy(const Rect& crop, bool immediate);
// TODO(b/38182121): Could we eliminate the various latching modes by
// using the layer hierarchy?
// -----------------------------------------------------------------------
- bool setLayer(int32_t z);
- bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
+ virtual bool setLayer(int32_t z);
+ virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
- bool setAlpha(float alpha);
- bool setColor(const half3& color);
- bool setTransparentRegionHint(const Region& transparent);
- bool setFlags(uint8_t flags, uint8_t mask);
- bool setLayerStack(uint32_t layerStack);
- uint32_t getLayerStack() const;
- void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
- void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
- bool setOverrideScalingMode(int32_t overrideScalingMode);
- void setInfo(int32_t type, int32_t appId);
- bool reparentChildren(const sp<IBinder>& layer);
- void setChildrenDrawingParent(const sp<Layer>& layer);
- bool reparent(const sp<IBinder>& newParentHandle);
- bool detachChildren();
+ virtual bool setAlpha(float alpha);
+ virtual bool setColor(const half3& /*color*/) { return false; };
+
+ // Set rounded corner radius for this layer and its children.
+ //
+ // We only support 1 radius per layer in the hierarchy, where parent layers have precedence.
+ // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
+ // from which we inferred the rounded corner radius.
+ virtual bool setCornerRadius(float cornerRadius);
+ virtual bool setTransparentRegionHint(const Region& transparent);
+ virtual bool setFlags(uint8_t flags, uint8_t mask);
+ virtual bool setLayerStack(uint32_t layerStack);
+ virtual uint32_t getLayerStack() const;
+ virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle,
+ uint64_t frameNumber);
+ virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
+ virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
+ virtual bool setMetadata(const LayerMetadata& data);
+ virtual bool reparentChildren(const sp<IBinder>& layer);
+ virtual void setChildrenDrawingParent(const sp<Layer>& layer);
+ virtual bool reparent(const sp<IBinder>& newParentHandle);
+ virtual bool detachChildren();
+ bool attachChildren();
+ bool isLayerDetached() const { return mLayerDetached; }
+ virtual bool setColorTransform(const mat4& matrix);
+ virtual mat4 getColorTransform() const;
+ virtual bool hasColorTransform() const;
+ virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
+
+ // Used only to set BufferStateLayer state
+ virtual bool setTransform(uint32_t /*transform*/) { return false; };
+ virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
+ virtual bool setCrop(const Rect& /*crop*/) { return false; };
+ virtual bool setFrame(const Rect& /*frame*/) { return false; };
+ virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/) { return false; };
+ virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
+ virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
+ virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
+ virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
+ virtual bool setApi(int32_t /*api*/) { return false; };
+ virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
+ virtual bool setTransactionCompletedListeners(
+ const std::vector<sp<CallbackHandle>>& /*handles*/) {
+ return false;
+ };
+ virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
+ virtual bool setColorSpaceAgnostic(const bool agnostic);
ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
@@ -304,22 +334,42 @@
// visually.
bool isLegacyDataSpace() const;
+ virtual std::shared_ptr<compositionengine::Layer> getCompositionLayer() const;
+
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
// one empty rect.
virtual void useSurfaceDamage() {}
virtual void useEmptyDamage() {}
+ uint32_t getTransactionFlags() const { return mTransactionFlags; }
uint32_t getTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
+ // Deprecated, please use compositionengine::Output::belongsInOutput()
+ // instead.
+ // TODO(lpique): Move the remaining callers (screencap) to the new function.
bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
}
- void computeGeometry(const RenderArea& renderArea, Mesh& mesh, bool useIdentityTransform) const;
- FloatRect computeBounds(const Region& activeTransparentRegion) const;
- FloatRect computeBounds() const;
+ void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
+ bool useIdentityTransform) const;
+ FloatRect getBounds(const Region& activeTransparentRegion) const;
+ FloatRect getBounds() const;
+
+ // Compute bounds for the layer and cache the results.
+ void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+
+ // Returns the buffer scale transform if a scaling mode is set.
+ ui::Transform getBufferScaleTransform() const;
+
+ // Get effective layer transform, taking into account all its parent transform with any
+ // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
+ ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
+
+ // Returns the bounds of the layer without any buffer scaling.
+ FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
int32_t getSequence() const { return sequence; }
@@ -356,69 +406,106 @@
bool isHiddenByPolicy() const;
/*
+ * Returns whether this layer can receive input.
+ */
+ virtual bool canReceiveInput() const;
+
+ /*
+ * isProtected - true if the layer may contain protected content in the
+ * GRALLOC_USAGE_PROTECTED sense.
+ */
+ virtual bool isProtected() const { return false; }
+
+ /*
* isFixedSize - true if content has a fixed size
*/
virtual bool isFixedSize() const { return true; }
+ /*
+ * usesSourceCrop - true if content should use a source crop
+ */
+ virtual bool usesSourceCrop() const { return false; }
+
// Most layers aren't created from the main thread, and therefore need to
// grab the SF state lock to access HWC, but ContainerLayer does, so we need
// to avoid grabbing the lock again to avoid deadlock
virtual bool isCreatedFromMainThread() const { return false; }
-
- bool isPendingRemoval() const { return mPendingRemoval; }
+ bool isRemovedFromCurrentState() const;
void writeToProto(LayerProto* layerInfo,
LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing);
- void writeToProto(LayerProto* layerInfo, int32_t hwcId);
+ void writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice);
+
+ 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; }
+ virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
+ virtual ui::Transform getActiveTransform(const Layer::State& s) const {
+ return s.active_legacy.transform;
+ }
+ virtual Region getActiveTransparentRegion(const Layer::State& s) const {
+ return s.activeTransparentRegion_legacy;
+ }
+ virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+
+ virtual void setPostTime(nsecs_t /*postTime*/) {}
+ virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {}
protected:
+ virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
+ bool useIdentityTransform, Region& clearRegion,
+ const bool supportProtectedContent,
+ renderengine::LayerSettings& layer);
+
+public:
/*
- * onDraw - draws the surface.
+ * compositionengine::LayerFE overrides
*/
- virtual void onDraw(const RenderArea& renderArea, const Region& clip,
- bool useIdentityTransform) const = 0;
+ void latchCompositionState(compositionengine::LayerFECompositionState&,
+ bool includeGeometry) const override;
+ void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ const char* getDebugName() const override;
+
+protected:
+ void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
public:
virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
virtual bool isHdrY410() const { return false; }
- void setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z);
- void forceClientComposition(int32_t hwcId);
- bool getForceClientComposition(int32_t hwcId);
- virtual void setPerFrameData(const sp<const DisplayDevice>& displayDevice) = 0;
+ void forceClientComposition(const sp<DisplayDevice>& display);
+ bool getForceClientComposition(const sp<DisplayDevice>& display);
+ virtual void setPerFrameData(const sp<const DisplayDevice>& display,
+ const ui::Transform& transform, const Rect& viewport,
+ int32_t supportedPerFrameMetadata,
+ const ui::Dataspace targetDataspace) = 0;
// callIntoHwc exists so we can update our local state and call
// acceptDisplayChanges without unnecessarily updating the device's state
- void setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc = true);
- HWC2::Composition getCompositionType(int32_t hwcId) const;
- void setClearClientTarget(int32_t hwcId, bool clear);
- bool getClearClientTarget(int32_t hwcId) const;
- void updateCursorPosition(const sp<const DisplayDevice>& hw);
+ void setCompositionType(const sp<const DisplayDevice>& display,
+ Hwc2::IComposerClient::Composition type);
+ Hwc2::IComposerClient::Composition getCompositionType(
+ const sp<const DisplayDevice>& display) const;
+ bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
+ void updateCursorPosition(const sp<const DisplayDevice>& display);
- /*
- * called after page-flip
- */
- virtual void onLayerDisplayed(const sp<Fence>& releaseFence);
-
- virtual void abandon() {}
-
- virtual bool shouldPresentNow(const DispSync& /*dispSync*/) const { return false; }
+ virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
virtual void setTransformHint(uint32_t /*orientation*/) const { }
/*
* called before composition.
* returns true if the layer has pending updates.
*/
- virtual bool onPreComposition(nsecs_t /*refreshStartTime*/) { return true; }
+ virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
*/
- virtual bool onPostComposition(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+ virtual bool onPostComposition(const std::optional<DisplayId>& /*displayId*/,
+ const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const std::shared_ptr<FenceTime>& /*presentFence*/,
const CompositorTiming& /*compositorTiming*/) {
return false;
@@ -427,14 +514,16 @@
// If a buffer was replaced this frame, release the former buffer
virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
-
/*
- * draw - performs some global clipping optimizations
- * and calls onDraw().
+ * prepareClientLayer - populates a renderengine::LayerSettings to passed to
+ * RenderEngine::drawLayers. Returns true if the layer can be used, and
+ * false otherwise.
*/
- void draw(const RenderArea& renderArea, const Region& clip) const;
- void draw(const RenderArea& renderArea, bool useIdentityTransform) const;
- void draw(const RenderArea& renderArea) const;
+ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
+ const bool supportProtectedContent, renderengine::LayerSettings& layer);
+ bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
+ Region& clearRegion, const bool supportProtectedContent,
+ renderengine::LayerSettings& layer);
/*
* doTransaction - process the transaction. This is a good place to figure
@@ -472,13 +561,12 @@
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- virtual Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
+ virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
return {};
}
virtual bool isBufferLatched() const { return false; }
- bool isPotentialCursor() const { return mPotentialCursor; }
/*
* called with the state lock from a binder thread when the layer is
* removed from the current list to the pending removal list
@@ -486,14 +574,13 @@
void onRemovedFromCurrentState();
/*
- * called with the state lock from the main thread when the layer is
- * removed from the pending removal list
+ * Called when the layer is added back to the current state list.
*/
- void onRemoved();
+ void addToCurrentState();
// Updates the transform hint in our SurfaceFlingerConsumer to match
// the current orientation of the display device.
- void updateTransformHint(const sp<const DisplayDevice>& hw) const;
+ void updateTransformHint(const sp<const DisplayDevice>& display) const;
/*
* returns the rectangle that crops the content of the layer and scales it
@@ -502,33 +589,18 @@
Rect getContentCrop() const;
/*
- * Returns if a frame is queued.
+ * Returns if a frame is ready
*/
- bool hasQueuedFrame() const {
- return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
- }
+ virtual bool hasReadyFrame() const { return false; }
- int32_t getQueuedFrameCount() const { return mQueuedFrames; }
+ virtual int32_t getQueuedFrameCount() const { return 0; }
// -----------------------------------------------------------------------
- bool createHwcLayer(HWComposer* hwc, int32_t hwcId);
- bool destroyHwcLayer(int32_t hwcId);
- void destroyAllHwcLayers();
-
- bool hasHwcLayer(int32_t hwcId) {
- return getBE().mHwcLayers.count(hwcId) > 0;
- }
-
- HWC2::Layer* getHwcLayer(int32_t hwcId) {
- if (getBE().mHwcLayers.count(hwcId) == 0) {
- return nullptr;
- }
- return getBE().mHwcLayers[hwcId].layer;
- }
+ bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice);
+ HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice);
// -----------------------------------------------------------------------
-
void clearWithOpenGL(const RenderArea& renderArea) const;
inline const State& getDrawingState() const { return mDrawingState; }
@@ -538,10 +610,10 @@
LayerDebugInfo getLayerDebugInfo() const;
/* always call base class first */
- static void miniDumpHeader(String8& result);
- void miniDump(String8& result, int32_t hwcId) const;
- void dumpFrameStats(String8& result) const;
- void dumpFrameEvents(String8& result);
+ static void miniDumpHeader(std::string& result);
+ void miniDump(std::string& result, const sp<DisplayDevice>& display) const;
+ void dumpFrameStats(std::string& result) const;
+ void dumpFrameEvents(std::string& result);
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
@@ -556,7 +628,7 @@
virtual bool getTransformToDisplayInverse() const { return false; }
- Transform getTransform() const;
+ ui::Transform getTransform() const;
// Returns the Alpha of the Surface, accounting for the Alpha
// of parent Surfaces in the hierarchy (alpha's will be multiplied
@@ -564,6 +636,13 @@
half getAlpha() const;
half4 getColor() 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.
+ // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+ // ignored.
+ RoundedCornerState getRoundedCornerState() const;
+
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -582,7 +661,7 @@
ssize_t removeChild(const sp<Layer>& layer);
sp<Layer> getParent() const { return mCurrentParent.promote(); }
bool hasParent() const { return getParent() != nullptr; }
- Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+ Rect getScreenBounds(bool reduceTransparentRegion = true) const;
bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
bool setChildRelativeLayer(const sp<Layer>& childLayer,
const sp<IBinder>& relativeToHandle, int32_t relativeZ);
@@ -591,7 +670,25 @@
// SurfaceFlinger to complete a transaction.
void commitChildList();
int32_t getZ() const;
- void pushPendingState();
+ virtual void pushPendingState();
+
+ /**
+ * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+ * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+ */
+ virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+
+ /**
+ * Returns the source bounds. If the bounds are not defined, it is inferred from the
+ * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+ * For the root layer, this is the display viewport size.
+ */
+ virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+ return parentBounds;
+ }
+
+ compositionengine::OutputLayer* findOutputLayerForDisplay(
+ const sp<const DisplayDevice>& display) const;
protected:
// constant
@@ -602,12 +699,12 @@
*/
class LayerCleaner {
sp<SurfaceFlinger> mFlinger;
- wp<Layer> mLayer;
+ sp<Layer> mLayer;
protected:
~LayerCleaner() {
// destroy client resources
- mFlinger->onLayerDestroyed(mLayer);
+ mFlinger->onHandleDestroyed(mLayer);
}
public:
@@ -617,21 +714,22 @@
friend class impl::SurfaceInterceptor;
- void commitTransaction(const State& stateToCommit);
+ // For unit tests
+ friend class TestableSurfaceFlinger;
+
+ virtual void commitTransaction(const State& stateToCommit);
uint32_t getEffectiveUsage(uint32_t usage) const;
- FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
- // Compute the initial crop as specified by parent layers and the
- // SurfaceControl for this layer. Does not include buffer crop from the
- // IGraphicBufferProducer client, as that should not affect child clipping.
- // Returns in screen space.
- Rect computeInitialCrop(const sp<const DisplayDevice>& hw) const;
+ /**
+ * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
+ * crop coordinates, transforming them into layer space.
+ */
+ void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
// drawing
void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b,
float alpha) const;
-
void setParent(const sp<Layer>& layer);
LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
@@ -673,9 +771,8 @@
bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
void popPendingState(State* stateToCommit);
- bool applyPendingStates(State* stateToCommit);
-
- void clearSyncPoints();
+ virtual bool applyPendingStates(State* stateToCommit);
+ virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
// Returns mCurrentScaling mode (originating from the
// Client) or mOverrideScalingMode mode (originating from
@@ -705,11 +802,17 @@
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
bool getPremultipledAlpha() const;
+ bool mPendingHWCDestroy{false};
+ void setInputInfo(const InputWindowInfo& info);
+
+ InputWindowInfo fillInputInfo();
+ bool hasInput() const;
+
protected:
// -----------------------------------------------------------------------
bool usingRelativeZ(LayerVector::StateSet stateSet);
- bool mPremultipliedAlpha;
+ bool mPremultipliedAlpha{true};
String8 mName;
String8 mTransactionName; // A cached version of "TX - " + mName for systraces
@@ -718,16 +821,12 @@
// these are protected by an external lock
State mCurrentState;
State mDrawingState;
- volatile int32_t mTransactionFlags;
+ std::atomic<uint32_t> mTransactionFlags{0};
// Accessed from main thread and binder threads
Mutex mPendingStateMutex;
Vector<State> mPendingStates;
- // thread-safe
- volatile int32_t mQueuedFrames;
- volatile int32_t mSidebandStreamChanged; // used like an atomic boolean
-
// Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
@@ -738,27 +837,29 @@
FenceTimeline mAcquireTimeline;
FenceTimeline mReleaseTimeline;
- TimeStats& mTimeStats = TimeStats::getInstance();
-
// main thread
- int mActiveBufferSlot;
- sp<GraphicBuffer> mActiveBuffer;
sp<NativeHandle> mSidebandStream;
+ // Active buffer fields
+ sp<GraphicBuffer> mActiveBuffer;
+ sp<Fence> mActiveBufferFence;
+ // False if the buffer and its contents have been previously used for GPU
+ // composition, true otherwise.
+ bool mIsActiveBufferUpdatedForGpu = true;
+
ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
Rect mCurrentCrop;
- uint32_t mCurrentTransform;
+ uint32_t mCurrentTransform{0};
// We encode unset as -1.
- int32_t mOverrideScalingMode;
- bool mCurrentOpacity;
- std::atomic<uint64_t> mCurrentFrameNumber;
- bool mFrameLatencyNeeded;
+ int32_t mOverrideScalingMode{-1};
+ std::atomic<uint64_t> mCurrentFrameNumber{0};
+ bool mFrameLatencyNeeded{false};
// Whether filtering is needed b/c of the drawingstate
- bool mNeedsFiltering;
+ bool mNeedsFiltering{false};
- bool mPendingRemoval = false;
+ std::atomic<bool> mRemovedFromCurrentState{false};
// page-flip thread (currently main thread)
- bool mProtectedByApp; // application requires protected path to external sink
+ bool mProtectedByApp{false}; // application requires protected path to external sink
// protected by mLock
mutable Mutex mLock;
@@ -766,25 +867,22 @@
const wp<Client> mClientRef;
// This layer can be a cursor on some displays.
- bool mPotentialCursor;
+ bool mPotentialCursor{false};
- // Local copy of the queued contents of the incoming BufferQueue
- mutable Mutex mQueueItemLock;
- Condition mQueueItemCondition;
- Vector<BufferItem> mQueueItems;
- std::atomic<uint64_t> mLastFrameNumberReceived;
- bool mAutoRefresh;
- bool mFreezeGeometryUpdates;
+ bool mFreezeGeometryUpdates{false};
// Child list about to be committed/used for editing.
- LayerVector mCurrentChildren;
+ LayerVector mCurrentChildren{LayerVector::StateSet::Current};
// Child list used for rendering.
- LayerVector mDrawingChildren;
+ LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
wp<Layer> mCurrentParent;
wp<Layer> mDrawingParent;
- mutable LayerBE mBE;
+ // Can only be accessed with the SF state lock held.
+ bool mLayerDetached{false};
+ // Can only be accessed with the SF state lock held.
+ bool mChildrenChanged{false};
private:
/**
@@ -801,10 +899,43 @@
const LayerVector::Visitor& visitor);
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
+ /**
+ * 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.
+ * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+ * bounds are constrained by its parent bounds.
+ */
+ Rect getCroppedBufferSize(const Layer::State& s) const;
+
+ RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
+
+ // Cached properties computed from drawing state
+ // Effective transform taking into account parent transforms and any parent scaling.
+ ui::Transform mEffectiveTransform;
+
+ // Bounds of the layer before any transformation is applied and before it has been cropped
+ // by its parents.
+ FloatRect mSourceBounds;
+
+ // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and
+ // its parent bounds.
+ FloatRect mBounds;
+
+ // Layer bounds in screen space.
+ FloatRect mScreenBounds;
+
+ void setZOrderRelativeOf(const wp<Layer>& relativeOf);
};
-// ---------------------------------------------------------------------------
+} // namespace android
-}; // 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 cc39550..c25c418 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -18,52 +18,105 @@
namespace android {
namespace surfaceflinger {
-void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) {
+
+void LayerProtoHelper::writePositionToProto(const float x, const float y,
+ std::function<PositionProto*()> getPositionProto) {
+ if (x != 0 || y != 0) {
+ // Use a lambda do avoid writing the object header when the object is empty
+ PositionProto* position = getPositionProto();
+ position->set_x(x);
+ position->set_y(y);
+ }
+}
+
+void LayerProtoHelper::writeSizeToProto(const uint32_t w, const uint32_t h,
+ std::function<SizeProto*()> getSizeProto) {
+ if (w != 0 || h != 0) {
+ // Use a lambda do avoid writing the object header when the object is empty
+ SizeProto* size = getSizeProto();
+ size->set_w(w);
+ size->set_h(h);
+ }
+}
+
+void LayerProtoHelper::writeToProto(const Region& region,
+ std::function<RegionProto*()> getRegionProto) {
+ if (region.isEmpty()) {
+ return;
+ }
+
Region::const_iterator head = region.begin();
Region::const_iterator const tail = region.end();
- uint64_t address = reinterpret_cast<uint64_t>(®ion);
- regionProto->set_id(address);
+ // Use a lambda do avoid writing the object header when the object is empty
+ RegionProto* regionProto = getRegionProto();
while (head != tail) {
- RectProto* rectProto = regionProto->add_rect();
- writeToProto(*head, rectProto);
+ std::function<RectProto*()> getProtoRect = [&]() { return regionProto->add_rect(); };
+ writeToProto(*head, getProtoRect);
head++;
}
}
-void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) {
- rectProto->set_left(rect.left);
- rectProto->set_top(rect.top);
- rectProto->set_bottom(rect.bottom);
- rectProto->set_right(rect.right);
+void LayerProtoHelper::writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto) {
+ if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
+ // Use a lambda do avoid writing the object header when the object is empty
+ RectProto* rectProto = getRectProto();
+ rectProto->set_left(rect.left);
+ rectProto->set_top(rect.top);
+ rectProto->set_bottom(rect.bottom);
+ rectProto->set_right(rect.right);
+ }
}
-void LayerProtoHelper::writeToProto(const FloatRect& rect, FloatRectProto* rectProto) {
- rectProto->set_left(rect.left);
- rectProto->set_top(rect.top);
- rectProto->set_bottom(rect.bottom);
- rectProto->set_right(rect.right);
+void LayerProtoHelper::writeToProto(const FloatRect& rect,
+ std::function<FloatRectProto*()> getFloatRectProto) {
+ if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
+ // Use a lambda do avoid writing the object header when the object is empty
+ FloatRectProto* rectProto = getFloatRectProto();
+ rectProto->set_left(rect.left);
+ rectProto->set_top(rect.top);
+ rectProto->set_bottom(rect.bottom);
+ rectProto->set_right(rect.right);
+ }
}
-void LayerProtoHelper::writeToProto(const half4 color, ColorProto* colorProto) {
- colorProto->set_r(color.r);
- colorProto->set_g(color.g);
- colorProto->set_b(color.b);
- colorProto->set_a(color.a);
+void LayerProtoHelper::writeToProto(const half4 color, std::function<ColorProto*()> getColorProto) {
+ if (color.r != 0 || color.g != 0 || color.b != 0 || color.a != 0) {
+ // Use a lambda do avoid writing the object header when the object is empty
+ ColorProto* colorProto = getColorProto();
+ colorProto->set_r(color.r);
+ colorProto->set_g(color.g);
+ colorProto->set_b(color.b);
+ colorProto->set_a(color.a);
+ }
}
-void LayerProtoHelper::writeToProto(const Transform& transform, TransformProto* transformProto) {
- transformProto->set_dsdx(transform[0][0]);
- transformProto->set_dtdx(transform[0][1]);
- transformProto->set_dsdy(transform[1][0]);
- transformProto->set_dtdy(transform[1][1]);
+void LayerProtoHelper::writeToProto(const ui::Transform& transform,
+ TransformProto* transformProto) {
+ const uint32_t type = transform.getType() | (transform.getOrientation() << 8);
+ transformProto->set_type(type);
+
+ // Rotations that are 90/180/270 have their own type so the transform matrix can be
+ // reconstructed later. All other rotation have the type UKNOWN so we need to save the transform
+ // values in that case.
+ if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) {
+ transformProto->set_dsdx(transform[0][0]);
+ transformProto->set_dtdx(transform[0][1]);
+ transformProto->set_dsdy(transform[1][0]);
+ transformProto->set_dtdy(transform[1][1]);
+ }
}
void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer,
- ActiveBufferProto* activeBufferProto) {
- activeBufferProto->set_width(buffer->getWidth());
- activeBufferProto->set_height(buffer->getHeight());
- activeBufferProto->set_stride(buffer->getStride());
- activeBufferProto->set_format(buffer->format);
+ std::function<ActiveBufferProto*()> getActiveBufferProto) {
+ if (buffer->getWidth() != 0 || buffer->getHeight() != 0 || buffer->getStride() != 0 ||
+ buffer->format != 0) {
+ // Use a lambda do avoid writing the object header when the object is empty
+ ActiveBufferProto* activeBufferProto = getActiveBufferProto();
+ activeBufferProto->set_width(buffer->getWidth());
+ activeBufferProto->set_height(buffer->getHeight());
+ activeBufferProto->set_stride(buffer->getStride());
+ activeBufferProto->set_format(buffer->format);
+ }
}
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 860da63..dca9a5e 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -16,24 +16,28 @@
#include <layerproto/LayerProtoHeader.h>
+#include <math/vec4.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-
-#include <Transform.h>
-
-#include <math/vec4.h>
+#include <ui/Transform.h>
namespace android {
namespace surfaceflinger {
class LayerProtoHelper {
public:
- static void writeToProto(const Rect& rect, RectProto* rectProto);
- static void writeToProto(const FloatRect& rect, FloatRectProto* rectProto);
- static void writeToProto(const Region& region, RegionProto* regionProto);
- static void writeToProto(const half4 color, ColorProto* colorProto);
- static void writeToProto(const Transform& transform, TransformProto* transformProto);
- static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto);
+ static void writePositionToProto(const float x, const float y,
+ std::function<PositionProto*()> getPositionProto);
+ static void writeSizeToProto(const uint32_t w, const uint32_t h,
+ std::function<SizeProto*()> getSizeProto);
+ static void writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto);
+ static void writeToProto(const FloatRect& rect,
+ std::function<FloatRectProto*()> getFloatRectProto);
+ static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto);
+ static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto);
+ static void writeToProto(const ui::Transform& transform, TransformProto* transformProto);
+ static void writeToProto(const sp<GraphicBuffer>& buffer,
+ std::function<ActiveBufferProto*()> getActiveBufferProto);
};
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index a5f0b98..72abea8 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -19,8 +19,6 @@
#include <gui/BufferItem.h>
#include <system/window.h>
-#include "clz.h"
-
#define DEBUG_RESIZE 0
namespace android {
@@ -31,6 +29,7 @@
bool stickySet,
const char* name,
int32_t overrideScalingMode,
+ bool transformToDisplayInverse,
bool& freezePositionUpdates)
: mFront(front),
mCurrent(current),
@@ -38,6 +37,7 @@
mStickyTransformSet(stickySet),
mName(name),
mOverrideScalingMode(overrideScalingMode),
+ mTransformToDisplayInverse(transformToDisplayInverse),
mFreezeGeometryUpdates(freezePositionUpdates) {}
bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
@@ -50,26 +50,34 @@
// check that we received a buffer of the right size
// (Take the buffer's orientation into account)
- if (item.mTransform & Transform::ROT_90) {
- swap(bufWidth, bufHeight);
+ if (item.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (mTransformToDisplayInverse) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
}
int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
- if (mFront.active != mFront.requested) {
- if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) {
+ if (mFront.active_legacy != mFront.requested_legacy) {
+ if (isFixedSize ||
+ (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) {
// Here we pretend the transaction happened by updating the
// current and drawing states. Drawing state is only accessed
// in this thread, no need to have it locked
- mFront.active = mFront.requested;
+ mFront.active_legacy = mFront.requested_legacy;
// We also need to update the current state so that
// we don't end-up overwriting the drawing state with
// this stale current state during the next transaction
//
// NOTE: We don't need to hold the transaction lock here
- // because State::active is only accessed from this thread.
- mCurrent.active = mFront.active;
+ // because State::active_legacy is only accessed from this thread.
+ mCurrent.active_legacy = mFront.active_legacy;
mCurrent.modified = true;
// recompute visible region
@@ -77,35 +85,32 @@
mFreezeGeometryUpdates = false;
- if (mFront.crop != mFront.requestedCrop) {
- mFront.crop = mFront.requestedCrop;
- mCurrent.crop = mFront.requestedCrop;
- mRecomputeVisibleRegions = true;
- }
- if (mFront.finalCrop != mFront.requestedFinalCrop) {
- mFront.finalCrop = mFront.requestedFinalCrop;
- mCurrent.finalCrop = mFront.requestedFinalCrop;
+ if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
+ mFront.crop_legacy = mFront.requestedCrop_legacy;
+ mCurrent.crop_legacy = mFront.requestedCrop_legacy;
mRecomputeVisibleRegions = true;
}
}
ALOGD_IF(DEBUG_RESIZE,
"[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) "
+ " drawing={ active_legacy ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} "
+ "(%4d,%4d) "
"}\n"
- " requested={ wh={%4u,%4u} }}\n",
- mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w,
- mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right,
- mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(),
- mFront.requested.w, mFront.requested.h);
+ " requested_legacy={ wh={%4u,%4u} }}\n",
+ mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode,
+ mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
+ mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
+ mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
+ mFront.requested_legacy.w, mFront.requested_legacy.h);
}
if (!isFixedSize && !mStickyTransformSet) {
- if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) {
+ if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) {
// reject this buffer
ALOGE("[%s] rejecting buffer: "
- "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
- mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h);
+ "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
+ mName, bufWidth, bufHeight, mFront.active_legacy.w, mFront.active_legacy.h);
return true;
}
}
@@ -118,16 +123,17 @@
// We latch the transparent region here, instead of above where we latch
// the rest of the geometry because it is only content but not necessarily
// resize dependent.
- if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) {
- mFront.activeTransparentRegion = mFront.requestedTransparentRegion;
+ if (!mFront.activeTransparentRegion_legacy.isTriviallyEqual(
+ mFront.requestedTransparentRegion_legacy)) {
+ mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
// We also need to update the current state so that
// we don't end-up overwriting the drawing state with
// this stale current state during the next transaction
//
// NOTE: We don't need to hold the transaction lock here
- // because State::active is only accessed from this thread.
- mCurrent.activeTransparentRegion = mFront.activeTransparentRegion;
+ // because State::active_legacy is only accessed from this thread.
+ mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy;
// recompute visible region
mRecomputeVisibleRegions = true;
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index 40972aa..63d51de 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -29,6 +29,7 @@
bool stickySet,
const char *name,
int32_t overrideScalingMode,
+ bool transformToDisplayInverse,
bool &freezePositionUpdates);
virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
@@ -40,6 +41,7 @@
bool mStickyTransformSet;
const char *mName;
int32_t mOverrideScalingMode;
+ bool mTransformToDisplayInverse;
bool &mFreezeGeometryUpdates;
};
} // namespace android
diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp
index 2a67955..a2d1feb 100644
--- a/services/surfaceflinger/LayerStats.cpp
+++ b/services/surfaceflinger/LayerStats.cpp
@@ -23,11 +23,13 @@
#include <android-base/stringprintf.h>
#include <log/log.h>
-#include <utils/String8.h>
#include <utils/Trace.h>
namespace android {
+using base::StringAppendF;
+using base::StringPrintf;
+
void LayerStats::enable() {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
@@ -57,33 +59,31 @@
}
void LayerStats::traverseLayerTreeStatsLocked(
- const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree,
+ const std::vector<LayerProtoParser::Layer*>& layerTree,
const LayerProtoParser::LayerGlobal& layerGlobal,
std::vector<std::string>* const outLayerShapeVec) {
for (const auto& layer : layerTree) {
if (!layer) continue;
traverseLayerTreeStatsLocked(layer->children, layerGlobal, outLayerShapeVec);
std::string key = "";
- base::StringAppendF(&key, ",%s", layer->type.c_str());
- base::StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType));
- base::StringAppendF(&key, ",%d", layer->isProtected);
- base::StringAppendF(&key, ",%s", layerTransform(layer->hwcTransform));
- base::StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format).c_str());
- base::StringAppendF(&key, ",%s", layer->dataspace.c_str());
- base::StringAppendF(&key, ",%s",
- destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0],
- true));
- base::StringAppendF(&key, ",%s",
- destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1],
- false));
- base::StringAppendF(&key, ",%s",
- destinationSize(layer->hwcFrame.right - layer->hwcFrame.left,
- layerGlobal.resolution[0], true));
- base::StringAppendF(&key, ",%s",
- destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top,
- layerGlobal.resolution[1], false));
- base::StringAppendF(&key, ",%s", scaleRatioWH(layer.get()).c_str());
- base::StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a)));
+ StringAppendF(&key, ",%s", layer->type.c_str());
+ StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType));
+ StringAppendF(&key, ",%d", layer->isProtected);
+ StringAppendF(&key, ",%s", layerTransform(layer->hwcTransform));
+ StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format).c_str());
+ StringAppendF(&key, ",%s", layer->dataspace.c_str());
+ StringAppendF(&key, ",%s",
+ destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0], true));
+ StringAppendF(&key, ",%s",
+ destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1], false));
+ StringAppendF(&key, ",%s",
+ destinationSize(layer->hwcFrame.right - layer->hwcFrame.left,
+ layerGlobal.resolution[0], true));
+ StringAppendF(&key, ",%s",
+ destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top,
+ layerGlobal.resolution[1], false));
+ StringAppendF(&key, ",%s", scaleRatioWH(layer).c_str());
+ StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a)));
outLayerShapeVec->push_back(key);
ALOGV("%s", key.c_str());
@@ -98,12 +98,12 @@
std::vector<std::string> layerShapeVec;
std::lock_guard<std::mutex> lock(mMutex);
- traverseLayerTreeStatsLocked(layerTree, layerGlobal, &layerShapeVec);
+ traverseLayerTreeStatsLocked(layerTree.topLevelLayers, layerGlobal, &layerShapeVec);
std::string layerShapeKey =
- base::StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()),
- layerGlobal.colorMode.c_str(), layerGlobal.colorTransform.c_str(),
- layerTransform(layerGlobal.globalTransform));
+ StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()),
+ layerGlobal.colorMode.c_str(), layerGlobal.colorTransform.c_str(),
+ layerTransform(layerGlobal.globalTransform));
ALOGV("%s", layerShapeKey.c_str());
std::sort(layerShapeVec.begin(), layerShapeVec.end(), std::greater<std::string>());
@@ -114,7 +114,7 @@
mLayerShapeStatsMap[layerShapeKey]++;
}
-void LayerStats::dump(String8& result) {
+void LayerStats::dump(std::string& result) {
ATRACE_CALL();
ALOGD("Dumping");
std::lock_guard<std::mutex> lock(mMutex);
@@ -122,7 +122,7 @@
result.append("LayerType,CompositionType,IsProtected,Transform,PixelFormat,Dataspace,");
result.append("DstX,DstY,DstWidth,DstHeight,WScale,HScale,Alpha\n");
for (auto& u : mLayerShapeStatsMap) {
- result.appendFormat("%u,%s\n", u.second, u.first.c_str());
+ StringAppendF(&result, "%u,%s\n", u.second, u.first.c_str());
}
}
diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h
index bd17d82..62b2688 100644
--- a/services/surfaceflinger/LayerStats.h
+++ b/services/surfaceflinger/LayerStats.h
@@ -24,7 +24,6 @@
using namespace android::surfaceflinger;
namespace android {
-class String8;
class LayerStats {
public:
@@ -33,12 +32,12 @@
void clear();
bool isEnabled();
void logLayerStats(const LayersProto& layersProto);
- void dump(String8& result);
+ void dump(std::string& result);
private:
// Traverse layer tree to get all visible layers' stats
void traverseLayerTreeStatsLocked(
- const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree,
+ const std::vector<LayerProtoParser::Layer*>& layerTree,
const LayerProtoParser::LayerGlobal& layerGlobal,
std::vector<std::string>* const outLayerShapeVec);
// Convert layer's top-left position into 8x8 percentage of the display
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 389fbd2..06e3d9c 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include "MessageQueue.h"
#include "MonitoredProducer.h"
-#include "SurfaceFlinger.h"
#include "Layer.h"
+#include "SurfaceFlinger.h"
+
+#include "Scheduler/MessageQueue.h"
namespace android {
diff --git a/services/surfaceflinger/NativeWindowSurface.cpp b/services/surfaceflinger/NativeWindowSurface.cpp
new file mode 100644
index 0000000..3fff928
--- /dev/null
+++ b/services/surfaceflinger/NativeWindowSurface.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 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 "NativeWindowSurface.h"
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+
+namespace android::surfaceflinger {
+
+NativeWindowSurface::~NativeWindowSurface() = default;
+
+namespace impl {
+
+std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+ const sp<IGraphicBufferProducer>& producer) {
+ class NativeWindowSurface final : public surfaceflinger::NativeWindowSurface {
+ public:
+ explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer)
+ : mSurface(new Surface(producer, /* controlledByApp */ false)) {}
+
+ ~NativeWindowSurface() override = default;
+
+ sp<ANativeWindow> getNativeWindow() const override { return mSurface; }
+
+ void preallocateBuffers() override { mSurface->allocateBuffers(); }
+
+ private:
+ sp<Surface> mSurface;
+ };
+
+ return std::make_unique<NativeWindowSurface>(producer);
+}
+
+} // namespace impl
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/NativeWindowSurface.h b/services/surfaceflinger/NativeWindowSurface.h
new file mode 100644
index 0000000..f34a45a
--- /dev/null
+++ b/services/surfaceflinger/NativeWindowSurface.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 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 <memory>
+
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+class IGraphicBufferProducer;
+
+namespace surfaceflinger {
+
+// A thin interface to abstract creating instances of Surface (gui/Surface.h) to
+// use as a NativeWindow.
+class NativeWindowSurface {
+public:
+ virtual ~NativeWindowSurface();
+
+ // Gets the NativeWindow to use for the surface.
+ virtual sp<ANativeWindow> getNativeWindow() const = 0;
+
+ // Indicates that the surface should allocate its buffers now.
+ virtual void preallocateBuffers() = 0;
+};
+
+namespace impl {
+
+std::unique_ptr<NativeWindowSurface> createNativeWindowSurface(const sp<IGraphicBufferProducer>&);
+
+} // namespace impl
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
new file mode 100644
index 0000000..718e996
--- /dev/null
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "RegionSamplingThread"
+
+#include "RegionSamplingThread.h"
+
+#include <cutils/properties.h>
+#include <gui/IRegionSamplingListener.h>
+#include <utils/Trace.h>
+#include <string>
+
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+using namespace std::chrono_literals;
+
+template <typename T>
+struct SpHash {
+ size_t operator()(const sp<T>& p) const { return std::hash<T*>()(p.get()); }
+};
+
+constexpr auto lumaSamplingStepTag = "LumaSamplingStep";
+enum class samplingStep {
+ noWorkNeeded,
+ idleTimerWaiting,
+ waitForZeroPhase,
+ waitForSamplePhase,
+ sample
+};
+
+constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingPeriod = 100ms;
+constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+// TODO: (b/127403193) duration to string conversion could probably be constexpr
+template <typename Rep, typename Per>
+inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
+ return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(t).count());
+}
+
+RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
+ char value[PROPERTY_VALUE_MAX] = {};
+
+ property_get("debug.sf.region_sampling_offset_ns", value,
+ toNsString(defaultRegionSamplingOffset).c_str());
+ int const samplingOffsetNsRaw = atoi(value);
+
+ property_get("debug.sf.region_sampling_period_ns", value,
+ toNsString(defaultRegionSamplingPeriod).c_str());
+ int const samplingPeriodNsRaw = atoi(value);
+
+ property_get("debug.sf.region_sampling_timer_timeout_ns", value,
+ toNsString(defaultRegionSamplingTimerTimeout).c_str());
+ int const samplingTimerTimeoutNsRaw = atoi(value);
+
+ if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
+ ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
+ mSamplingOffset = defaultRegionSamplingOffset;
+ mSamplingPeriod = defaultRegionSamplingPeriod;
+ mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
+ } else {
+ mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+ mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
+ mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
+ }
+}
+
+struct SamplingOffsetCallback : DispSync::Callback {
+ SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
+ std::chrono::nanoseconds targetSamplingOffset)
+ : mRegionSamplingThread(samplingThread),
+ mScheduler(scheduler),
+ mTargetSamplingOffset(targetSamplingOffset) {}
+
+ ~SamplingOffsetCallback() { stopVsyncListener(); }
+
+ SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
+ SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
+
+ void startVsyncListener() {
+ std::lock_guard lock(mMutex);
+ if (mVsyncListening) return;
+
+ mPhaseIntervalSetting = Phase::ZERO;
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.addEventListener("SamplingThreadDispSyncListener", 0, this, mLastCallbackTime);
+ });
+ mVsyncListening = true;
+ }
+
+ void stopVsyncListener() {
+ std::lock_guard lock(mMutex);
+ stopVsyncListenerLocked();
+ }
+
+private:
+ void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
+ if (!mVsyncListening) return;
+
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.removeEventListener(this, &mLastCallbackTime);
+ });
+ mVsyncListening = false;
+ }
+
+ void onDispSyncEvent(nsecs_t /* when */) final {
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+
+ if (mPhaseIntervalSetting == Phase::ZERO) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ mPhaseIntervalSetting = Phase::SAMPLING;
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.changePhaseOffset(this, mTargetSamplingOffset.count());
+ });
+ return;
+ }
+
+ if (mPhaseIntervalSetting == Phase::SAMPLING) {
+ mPhaseIntervalSetting = Phase::ZERO;
+ mScheduler.withPrimaryDispSync(
+ [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); });
+ stopVsyncListenerLocked();
+ lock.unlock();
+ mRegionSamplingThread.notifySamplingOffset();
+ return;
+ }
+ }
+
+ RegionSamplingThread& mRegionSamplingThread;
+ Scheduler& mScheduler;
+ const std::chrono::nanoseconds mTargetSamplingOffset;
+ mutable std::mutex mMutex;
+ nsecs_t mLastCallbackTime = 0;
+ enum class Phase {
+ ZERO,
+ SAMPLING
+ } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
+ = Phase::ZERO;
+ bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+};
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+ const TimingTunables& tunables)
+ : mFlinger(flinger),
+ mScheduler(scheduler),
+ mTunables(tunables),
+ mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
+ mTunables.mSamplingTimerTimeout),
+ [] {}, [this] { checkForStaleLuma(); }),
+ mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
+ tunables.mSamplingOffset)),
+ lastSampleTime(0ns) {
+ {
+ std::lock_guard threadLock(mThreadMutex);
+ mThread = std::thread([this]() { threadMain(); });
+ pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+ }
+ mIdleTimer.start();
+}
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
+ : RegionSamplingThread(flinger, scheduler,
+ TimingTunables{defaultRegionSamplingOffset,
+ defaultRegionSamplingPeriod,
+ defaultRegionSamplingTimerTimeout}) {}
+
+RegionSamplingThread::~RegionSamplingThread() {
+ mIdleTimer.stop();
+
+ {
+ std::lock_guard lock(mMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ std::lock_guard threadLock(mThreadMutex);
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ wp<Layer> stopLayer = stopLayerHandle != nullptr
+ ? static_cast<Layer::Handle*>(stopLayerHandle.get())->owner
+ : nullptr;
+
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
+}
+
+void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+void RegionSamplingThread::checkForStaleLuma() {
+ std::lock_guard lock(mMutex);
+
+ if (mDiscardedFrames) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
+ mDiscardedFrames = false;
+ mPhaseCallback->startVsyncListener();
+ }
+}
+
+void RegionSamplingThread::notifyNewContent() {
+ doSample();
+}
+
+void RegionSamplingThread::notifySamplingOffset() {
+ doSample();
+}
+
+void RegionSamplingThread::doSample() {
+ std::lock_guard lock(mMutex);
+ auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+ if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
+ mDiscardedFrames = true;
+ return;
+ }
+
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
+
+ mDiscardedFrames = false;
+ lastSampleTime = now;
+
+ mIdleTimer.reset();
+ mPhaseCallback->stopVsyncListener();
+
+ mSampleRequested = true;
+ mCondition.notify_one();
+}
+
+void RegionSamplingThread::binderDied(const wp<IBinder>& who) {
+ std::lock_guard lock(mMutex);
+ mDescriptors.erase(who);
+}
+
+namespace {
+// Using Rec. 709 primaries
+float getLuma(float r, float g, float b) {
+ constexpr auto rec709_red_primary = 0.2126f;
+ constexpr auto rec709_green_primary = 0.7152f;
+ constexpr auto rec709_blue_primary = 0.0722f;
+ return rec709_red_primary * r + rec709_green_primary * g + rec709_blue_primary * b;
+}
+
+float sampleArea(const uint32_t* data, int32_t stride, const Rect& area) {
+ std::array<int32_t, 256> brightnessBuckets = {};
+ const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2;
+
+ for (int32_t row = area.top; row < area.bottom; ++row) {
+ const uint32_t* rowBase = data + row * stride;
+ for (int32_t column = area.left; column < area.right; ++column) {
+ uint32_t pixel = rowBase[column];
+ const float r = (pixel & 0xFF) / 255.0f;
+ const float g = ((pixel >> 8) & 0xFF) / 255.0f;
+ const float b = ((pixel >> 16) & 0xFF) / 255.0f;
+ const uint8_t luma = std::round(getLuma(r, g, b) * 255.0f);
+ ++brightnessBuckets[luma];
+ if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f;
+ }
+ }
+
+ int32_t accumulated = 0;
+ size_t bucket = 0;
+ while (bucket++ < brightnessBuckets.size()) {
+ accumulated += brightnessBuckets[bucket];
+ if (accumulated > majoritySampleNum) break;
+ }
+
+ return bucket / 255.0f;
+}
+} // anonymous namespace
+
+std::vector<float> RegionSamplingThread::sampleBuffer(
+ const sp<GraphicBuffer>& buffer, const Point& leftTop,
+ const std::vector<RegionSamplingThread::Descriptor>& descriptors) {
+ void* data_raw = nullptr;
+ buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw);
+ std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw),
+ [&buffer](auto) { buffer->unlock(); });
+ if (!data) return {};
+
+ const int32_t stride = buffer->getStride();
+ std::vector<float> lumas(descriptors.size());
+ std::transform(descriptors.begin(), descriptors.end(), lumas.begin(),
+ [&](auto const& descriptor) {
+ return sampleArea(data.get(), stride, descriptor.area - leftTop);
+ });
+ return lumas;
+}
+
+void RegionSamplingThread::captureSample() {
+ ATRACE_CALL();
+
+ if (mDescriptors.empty()) {
+ return;
+ }
+
+ std::vector<RegionSamplingThread::Descriptor> descriptors;
+ Region sampleRegion;
+ for (const auto& [listener, descriptor] : mDescriptors) {
+ sampleRegion.orSelf(descriptor.area);
+ descriptors.emplace_back(descriptor);
+ }
+
+ const Rect sampledArea = sampleRegion.bounds();
+
+ sp<const DisplayDevice> device = mFlinger.getDefaultDisplayDevice();
+ DisplayRenderArea renderArea(device, sampledArea, sampledArea.getWidth(),
+ sampledArea.getHeight(), ui::Dataspace::V0_SRGB,
+ ui::Transform::ROT_0);
+
+ std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
+
+ auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
+ bool stopLayerFound = false;
+ auto filterVisitor = [&](Layer* layer) {
+ // We don't want to capture any layers beyond the stop layer
+ if (stopLayerFound) return;
+
+ // Likewise if we just found a stop layer, set the flag and abort
+ for (const auto& [area, stopLayer, listener] : descriptors) {
+ if (layer == stopLayer.promote().get()) {
+ stopLayerFound = true;
+ return;
+ }
+ }
+
+ // Compute the layer's position on the screen
+ const Rect bounds = Rect(layer->getBounds());
+ const ui::Transform transform = layer->getTransform();
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
+
+ // If this layer doesn't intersect with the larger sampledArea, skip capturing it
+ Rect ignore;
+ if (!transformed.intersect(sampledArea, &ignore)) return;
+
+ // If the layer doesn't intersect a sampling area, skip capturing it
+ bool intersectsAnyArea = false;
+ for (const auto& [area, stopLayer, listener] : descriptors) {
+ if (transformed.intersect(area, &ignore)) {
+ intersectsAnyArea = true;
+ listeners.insert(listener);
+ }
+ }
+ if (!intersectsAnyArea) return;
+
+ ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getName().string(), bounds.left,
+ bounds.top, bounds.right, bounds.bottom);
+ visitor(layer);
+ };
+ mFlinger.traverseLayersInDisplay(device, filterVisitor);
+ };
+
+ const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+
+ // When calling into SF, we post a message into the SF message queue (so the
+ // screen capture runs on the main thread). This message blocks until the
+ // screenshot is actually captured, but before the capture occurs, the main
+ // thread may perform a normal refresh cycle. At the end of this cycle, it
+ // can request another sample (because layers changed), which triggers a
+ // call into sampleNow. When sampleNow attempts to grab the mutex, we can
+ // deadlock.
+ //
+ // To avoid this, we drop the mutex while we call into SF.
+ mMutex.unlock();
+ mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false);
+ mMutex.lock();
+
+ std::vector<Descriptor> activeDescriptors;
+ for (const auto& descriptor : descriptors) {
+ if (listeners.count(descriptor.listener) != 0) {
+ activeDescriptors.emplace_back(descriptor);
+ }
+ }
+
+ ALOGV("Sampling %zu descriptors", activeDescriptors.size());
+ std::vector<float> lumas = sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors);
+
+ if (lumas.size() != activeDescriptors.size()) {
+ ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
+ activeDescriptors.size());
+ return;
+ }
+
+ for (size_t d = 0; d < activeDescriptors.size(); ++d) {
+ activeDescriptors[d].listener->onSampleCollected(lumas[d]);
+ }
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
+}
+
+void RegionSamplingThread::threadMain() {
+ std::lock_guard lock(mMutex);
+ while (mRunning) {
+ if (mSampleRequested) {
+ mSampleRequested = false;
+ captureSample();
+ }
+ mCondition.wait(mMutex,
+ [this]() REQUIRES(mMutex) { return mSampleRequested || !mRunning; });
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
new file mode 100644
index 0000000..d4e57bf
--- /dev/null
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 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 <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+#include "Scheduler/IdleTimer.h"
+
+namespace android {
+
+class GraphicBuffer;
+class IRegionSamplingListener;
+class Layer;
+class Scheduler;
+class SurfaceFlinger;
+struct SamplingOffsetCallback;
+
+class RegionSamplingThread : public IBinder::DeathRecipient {
+public:
+ struct TimingTunables {
+ // debug.sf.sampling_offset_ns
+ // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
+ // at which the sampling should start.
+ std::chrono::nanoseconds mSamplingOffset;
+ // debug.sf.sampling_period_ns
+ // This is the maximum amount of time the luma recieving client
+ // should have to wait for a new luma value after a frame is updated. The inverse of this is
+ // roughly the sampling rate. Sampling system rounds up sub-vsync sampling period to vsync
+ // period.
+ std::chrono::nanoseconds mSamplingPeriod;
+ // debug.sf.sampling_timer_timeout_ns
+ // This is the interval at which the luma sampling system will check that the luma clients
+ // have up to date information. It defaults to the mSamplingPeriod.
+ std::chrono::nanoseconds mSamplingTimerTimeout;
+ };
+ struct EnvironmentTimingTunables : TimingTunables {
+ EnvironmentTimingTunables();
+ };
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+ const TimingTunables& tunables);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+
+ ~RegionSamplingThread();
+
+ // Add a listener to receive luma notifications. The luma reported via listener will
+ // report the median luma for the layers under the stopLayerHandle, in the samplingArea region.
+ void addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener);
+ // Remove the listener to stop receiving median luma notifications.
+ void removeListener(const sp<IRegionSamplingListener>& listener);
+
+ // Notifies sampling engine that new content is available. This will trigger a sampling
+ // pass at some point in the future.
+ void notifyNewContent();
+
+ // Notifies the sampling engine that it has a good timing window in which to sample.
+ void notifySamplingOffset();
+
+private:
+ struct Descriptor {
+ Rect area = Rect::EMPTY_RECT;
+ wp<Layer> stopLayer;
+ sp<IRegionSamplingListener> listener;
+ };
+
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+ std::vector<float> sampleBuffer(
+ const sp<GraphicBuffer>& buffer, const Point& leftTop,
+ const std::vector<RegionSamplingThread::Descriptor>& descriptors);
+
+ void doSample();
+ void binderDied(const wp<IBinder>& who) override;
+ void checkForStaleLuma();
+
+ void captureSample() REQUIRES(mMutex);
+ void threadMain();
+
+ SurfaceFlinger& mFlinger;
+ Scheduler& mScheduler;
+ const TimingTunables mTunables;
+ scheduler::IdleTimer mIdleTimer;
+
+ std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
+
+ std::mutex mThreadMutex;
+ std::thread mThread GUARDED_BY(mThreadMutex);
+
+ std::mutex mMutex;
+ std::condition_variable_any mCondition;
+ bool mRunning GUARDED_BY(mMutex) = true;
+ bool mSampleRequested GUARDED_BY(mMutex) = false;
+
+ std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mMutex);
+ std::chrono::nanoseconds lastSampleTime GUARDED_BY(mMutex);
+ bool mDiscardedFrames GUARDED_BY(mMutex) = false;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index d980bd5..edc6442 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -1,11 +1,14 @@
#pragma once
-#include "Transform.h"
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
#include <functional>
namespace android {
+class DisplayDevice;
+
// RenderArea describes a rectangular area that layers can be rendered to.
//
// There is a logical render area and a physical render area. When a layer is
@@ -19,9 +22,11 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- Transform::orientation_flags rotation = Transform::ROT_0)
+ ui::Dataspace reqDataSpace,
+ ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: mReqWidth(reqWidth),
mReqHeight(reqHeight),
+ mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation) {}
@@ -40,7 +45,7 @@
// Returns the transform to be applied on layers to transform them into
// the logical render area.
- virtual const Transform& getTransform() const = 0;
+ virtual const ui::Transform& getTransform() const = 0;
// Returns the size of the logical render area. Layers are clipped to the
// logical render area.
@@ -60,21 +65,27 @@
virtual Rect getSourceCrop() const = 0;
// Returns the rotation of the source crop and the layers.
- Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
+ ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
// Returns the size of the physical render area.
int getReqWidth() const { return mReqWidth; };
int getReqHeight() const { return mReqHeight; };
+ // Returns the composition data space of the render area.
+ ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
+
// Returns the fill color of the physical render area. Regions not
// covered by any rendered layer should be filled with this color.
CaptureFill getCaptureFill() const { return mCaptureFill; };
+ virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
+
private:
const uint32_t mReqWidth;
const uint32_t mReqHeight;
+ const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
- const Transform::orientation_flags mRotationFlags;
+ const ui::Transform::orientation_flags mRotationFlags;
};
} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
deleted file mode 100644
index c218e4d..0000000
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 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 <string.h>
-
-#include <utils/TypeHelpers.h>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include "Description.h"
-
-namespace android {
-
-void Description::setPremultipliedAlpha(bool premultipliedAlpha) {
- mPremultipliedAlpha = premultipliedAlpha;
-}
-
-void Description::setOpaque(bool opaque) {
- mOpaque = opaque;
-}
-
-void Description::setTexture(const Texture& texture) {
- mTexture = texture;
- mTextureEnabled = true;
-}
-
-void Description::disableTexture() {
- mTextureEnabled = false;
-}
-
-void Description::setColor(const half4& color) {
- mColor = color;
-}
-
-void Description::setProjectionMatrix(const mat4& mtx) {
- mProjectionMatrix = mtx;
-}
-
-void Description::setColorMatrix(const mat4& mtx) {
- mColorMatrix = mtx;
-}
-
-void Description::setInputTransformMatrix(const mat3& matrix) {
- mInputTransformMatrix = matrix;
-}
-
-void Description::setOutputTransformMatrix(const mat4& matrix) {
- mOutputTransformMatrix = matrix;
-}
-
-bool Description::hasInputTransformMatrix() const {
- const mat3 identity;
- return mInputTransformMatrix != identity;
-}
-
-bool Description::hasOutputTransformMatrix() const {
- const mat4 identity;
- return mOutputTransformMatrix != identity;
-}
-
-bool Description::hasColorMatrix() const {
- const mat4 identity;
- return mColorMatrix != identity;
-}
-
-const mat4& Description::getColorMatrix() const {
- return mColorMatrix;
-}
-
-void Description::setY410BT2020(bool enable) {
- mY410BT2020 = enable;
-}
-
-void Description::setInputTransferFunction(TransferFunction transferFunction) {
- mInputTransferFunction = transferFunction;
-}
-
-void Description::setOutputTransferFunction(TransferFunction transferFunction) {
- mOutputTransferFunction = transferFunction;
-}
-
-void Description::setDisplayMaxLuminance(const float maxLuminance) {
- mDisplayMaxLuminance = maxLuminance;
-}
-
-} /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
deleted file mode 100644
index 6ebb340..0000000
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 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 <GLES2/gl2.h>
-#include "Texture.h"
-
-#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_
-#define SF_RENDER_ENGINE_DESCRIPTION_H_
-
-namespace android {
-
-class Program;
-
-/*
- * This holds the state of the rendering engine. This class is used
- * to generate a corresponding GLSL program and set the appropriate
- * uniform.
- *
- * Program and ProgramCache are friends and access the state directly
- */
-class Description {
-public:
- Description() = default;
- ~Description() = default;
-
- void setPremultipliedAlpha(bool premultipliedAlpha);
- void setOpaque(bool opaque);
- void setTexture(const Texture& texture);
- void disableTexture();
- void setColor(const half4& color);
- void setProjectionMatrix(const mat4& mtx);
- void setColorMatrix(const mat4& mtx);
- void setInputTransformMatrix(const mat3& matrix);
- void setOutputTransformMatrix(const mat4& matrix);
- bool hasInputTransformMatrix() const;
- bool hasOutputTransformMatrix() const;
- bool hasColorMatrix() const;
- const mat4& getColorMatrix() const;
-
- void setY410BT2020(bool enable);
-
- enum class TransferFunction : int {
- LINEAR,
- SRGB,
- ST2084,
- HLG, // Hybrid Log-Gamma for HDR.
- };
- void setInputTransferFunction(TransferFunction transferFunction);
- void setOutputTransferFunction(TransferFunction transferFunction);
- void setDisplayMaxLuminance(const float maxLuminance);
-
-private:
- friend class Program;
- friend class ProgramCache;
-
- // whether textures are premultiplied
- bool mPremultipliedAlpha = false;
- // whether this layer is marked as opaque
- bool mOpaque = true;
-
- // Texture this layer uses
- Texture mTexture;
- bool mTextureEnabled = false;
-
- // color used when texturing is disabled or when setting alpha.
- half4 mColor;
-
- // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
- bool mY410BT2020 = false;
-
- // transfer functions for the input/output
- TransferFunction mInputTransferFunction = TransferFunction::LINEAR;
- TransferFunction mOutputTransferFunction = TransferFunction::LINEAR;
-
- float mDisplayMaxLuminance;
-
- // projection matrix
- mat4 mProjectionMatrix;
- mat4 mColorMatrix;
- mat3 mInputTransformMatrix;
- mat4 mOutputTransformMatrix;
-};
-
-} /* namespace android */
-
-#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
deleted file mode 100644
index 744a70c..0000000
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright 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.
- */
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "RenderEngine"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/ColorSpace.h>
-#include <ui/DebugUtils.h>
-#include <ui/Rect.h>
-
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include <cutils/compiler.h>
-#include <gui/ISurfaceComposer.h>
-#include <math.h>
-
-#include "Description.h"
-#include "GLES20RenderEngine.h"
-#include "Mesh.h"
-#include "Program.h"
-#include "ProgramCache.h"
-#include "Texture.h"
-
-#include <fstream>
-#include <sstream>
-
-// ---------------------------------------------------------------------------
-bool checkGlError(const char* op, int lineNumber) {
- bool errorFound = false;
- GLint error = glGetError();
- while (error != GL_NO_ERROR) {
- errorFound = true;
- error = glGetError();
- ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
- }
- return errorFound;
-}
-
-static constexpr bool outputDebugPPMs = false;
-
-void writePPM(const char* basename, GLuint width, GLuint height) {
- ALOGV("writePPM #%s: %d x %d", basename, width, height);
-
- std::vector<GLubyte> pixels(width * height * 4);
- std::vector<GLubyte> outBuffer(width * height * 3);
-
- // TODO(courtneygo): We can now have float formats, need
- // to remove this code or update to support.
- // Make returned pixels fit in uint32_t, one byte per component
- glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
- if (checkGlError(__FUNCTION__, __LINE__)) {
- return;
- }
-
- std::string filename(basename);
- filename.append(".ppm");
- std::ofstream file(filename.c_str(), std::ios::binary);
- if (!file.is_open()) {
- ALOGE("Unable to open file: %s", filename.c_str());
- ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
- "surfaceflinger to write debug images");
- return;
- }
-
- file << "P6\n";
- file << width << "\n";
- file << height << "\n";
- file << 255 << "\n";
-
- auto ptr = reinterpret_cast<char*>(pixels.data());
- auto outPtr = reinterpret_cast<char*>(outBuffer.data());
- for (int y = height - 1; y >= 0; y--) {
- char* data = ptr + y * width * sizeof(uint32_t);
-
- for (GLuint x = 0; x < width; x++) {
- // Only copy R, G and B components
- outPtr[0] = data[0];
- outPtr[1] = data[1];
- outPtr[2] = data[2];
- data += sizeof(uint32_t);
- outPtr += 3;
- }
- }
- file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
-}
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace RE {
-namespace impl {
-// ---------------------------------------------------------------------------
-
-using ui::Dataspace;
-
-GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags)
- : RenderEngine(featureFlags),
- mVpWidth(0),
- mVpHeight(0),
- mPlatformHasWideColor((featureFlags & WIDE_COLOR_SUPPORT) != 0) {
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
-
- const uint16_t protTexData[] = {0};
- glGenTextures(1, &mProtectedTexName);
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-
- // mColorBlindnessCorrection = M;
-
- if (mPlatformHasWideColor) {
- ColorSpace srgb(ColorSpace::sRGB());
- ColorSpace displayP3(ColorSpace::DisplayP3());
- ColorSpace bt2020(ColorSpace::BT2020());
-
- // Compute sRGB to Display P3 transform matrix.
- // NOTE: For now, we are limiting output wide color space support to
- // Display-P3 only.
- mSrgbToDisplayP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform());
-
- // Compute Display P3 to sRGB transform matrix.
- mDisplayP3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform());
-
- // no chromatic adaptation needed since all color spaces use D65 for their white points.
- mSrgbToXyz = srgb.getRGBtoXYZ();
- mDisplayP3ToXyz = displayP3.getRGBtoXYZ();
- mBt2020ToXyz = bt2020.getRGBtoXYZ();
- mXyzToSrgb = mat4(srgb.getXYZtoRGB());
- mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
- mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
- }
-}
-
-GLES20RenderEngine::~GLES20RenderEngine() {}
-
-size_t GLES20RenderEngine::getMaxTextureSize() const {
- return mMaxTextureSize;
-}
-
-size_t GLES20RenderEngine::getMaxViewportDims() const {
- return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
- size_t hwh, bool yswap,
- Transform::orientation_flags rotation) {
- int32_t l = sourceCrop.left;
- int32_t r = sourceCrop.right;
-
- // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
- int32_t t = hwh - sourceCrop.top;
- int32_t b = hwh - sourceCrop.bottom;
-
- mat4 m;
- if (yswap) {
- m = mat4::ortho(l, r, t, b, 0, 1);
- } else {
- m = mat4::ortho(l, r, b, t, 0, 1);
- }
-
- // Apply custom rotation to the projection.
- float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
- switch (rotation) {
- case Transform::ROT_0:
- break;
- case Transform::ROT_90:
- m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
- break;
- case Transform::ROT_180:
- m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
- break;
- case Transform::ROT_270:
- m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
- break;
- default:
- break;
- }
-
- glViewport(0, 0, vpw, vph);
- mState.setProjectionMatrix(m);
- mVpWidth = vpw;
- mVpHeight = vph;
-}
-
-void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
- bool disableTexture, const half4& color) {
- mState.setPremultipliedAlpha(premultipliedAlpha);
- mState.setOpaque(opaque);
- mState.setColor(color);
-
- if (disableTexture) {
- mState.disableTexture();
- }
-
- if (color.a < 1.0f || !opaque) {
- glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-void GLES20RenderEngine::setSourceY410BT2020(bool enable) {
- mState.setY410BT2020(enable);
-}
-
-void GLES20RenderEngine::setSourceDataSpace(Dataspace source) {
- mDataSpace = source;
-}
-
-void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) {
- mOutputDataSpace = dataspace;
-}
-
-void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
- mState.setDisplayMaxLuminance(maxLuminance);
-}
-
-void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) {
- GLuint target = texture.getTextureTarget();
- glBindTexture(target, texture.getTextureName());
- GLenum filter = GL_NEAREST;
- if (texture.getFiltering()) {
- filter = GL_LINEAR;
- }
- glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
-
- mState.setTexture(texture);
-}
-
-void GLES20RenderEngine::setupLayerBlackedOut() {
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
- texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
- mState.setTexture(texture);
-}
-
-void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) {
- mState.setColorMatrix(colorTransform);
-}
-
-void GLES20RenderEngine::disableTexturing() {
- mState.disableTexture();
-}
-
-void GLES20RenderEngine::disableBlending() {
- glDisable(GL_BLEND);
-}
-
-void GLES20RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName,
- uint32_t* fbName, uint32_t* status) {
- GLuint tname, name;
- // turn our EGLImage into a texture
- glGenTextures(1, &tname);
- glBindTexture(GL_TEXTURE_2D, tname);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
-
- // create a Framebuffer Object to render into
- glGenFramebuffers(1, &name);
- glBindFramebuffer(GL_FRAMEBUFFER, name);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0);
-
- *status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- *texName = tname;
- *fbName = name;
-}
-
-void GLES20RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glDeleteFramebuffers(1, &fbName);
- glDeleteTextures(1, &texName);
-}
-
-void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
- mState.setPremultipliedAlpha(true);
- mState.setOpaque(false);
- mState.setColor(half4(r, g, b, a));
- mState.disableTexture();
- glDisable(GL_BLEND);
-}
-
-void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
- ATRACE_CALL();
- if (mesh.getTexCoordsSize()) {
- glEnableVertexAttribArray(Program::texCoords);
- glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getTexCoords());
- }
-
- glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getPositions());
-
- // By default, DISPLAY_P3 is the only supported wide color output. However,
- // when HDR content is present, hardware composer may be able to handle
- // BT2020 data space, in that case, the output data space is set to be
- // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
- // to respect this and convert non-HDR content to HDR format.
- if (mPlatformHasWideColor) {
- Description wideColorState = mState;
- Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
- Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
- Dataspace outputStandard = static_cast<Dataspace>(mOutputDataSpace &
- Dataspace::STANDARD_MASK);
- Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace &
- Dataspace::TRANSFER_MASK);
- bool needsXYZConversion = needsXYZTransformMatrix();
-
- if (needsXYZConversion) {
- // The supported input color spaces are standard RGB, Display P3 and BT2020.
- switch (inputStandard) {
- case Dataspace::STANDARD_DCI_P3:
- wideColorState.setInputTransformMatrix(mDisplayP3ToXyz);
- break;
- case Dataspace::STANDARD_BT2020:
- wideColorState.setInputTransformMatrix(mBt2020ToXyz);
- break;
- default:
- wideColorState.setInputTransformMatrix(mSrgbToXyz);
- break;
- }
-
- // The supported output color spaces are BT2020, Display P3 and standard RGB.
- switch (outputStandard) {
- case Dataspace::STANDARD_BT2020:
- wideColorState.setOutputTransformMatrix(mXyzToBt2020);
- break;
- case Dataspace::STANDARD_DCI_P3:
- wideColorState.setOutputTransformMatrix(mXyzToDisplayP3);
- break;
- default:
- wideColorState.setOutputTransformMatrix(mXyzToSrgb);
- break;
- }
- } else if (inputStandard != outputStandard) {
- // At this point, the input data space and output data space could be both
- // HDR data spaces, but they match each other, we do nothing in this case.
- // In addition to the case above, the input data space could be
- // - scRGB linear
- // - scRGB non-linear
- // - sRGB
- // - Display P3
- // The output data spaces could be
- // - sRGB
- // - Display P3
- if (outputStandard == Dataspace::STANDARD_BT709) {
- wideColorState.setOutputTransformMatrix(mDisplayP3ToSrgb);
- } else if (outputStandard == Dataspace::STANDARD_DCI_P3) {
- wideColorState.setOutputTransformMatrix(mSrgbToDisplayP3);
- }
- }
-
- // we need to convert the RGB value to linear space and convert it back when:
- // - there is a color matrix that is not an identity matrix, or
- // - there is an output transform matrix that is not an identity matrix, or
- // - the input transfer function doesn't match the output transfer function.
- if (wideColorState.hasColorMatrix() || wideColorState.hasOutputTransformMatrix() ||
- inputTransfer != outputTransfer) {
- switch (inputTransfer) {
- case Dataspace::TRANSFER_ST2084:
- wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
- break;
- case Dataspace::TRANSFER_HLG:
- wideColorState.setInputTransferFunction(Description::TransferFunction::HLG);
- break;
- case Dataspace::TRANSFER_LINEAR:
- wideColorState.setInputTransferFunction(Description::TransferFunction::LINEAR);
- break;
- default:
- wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB);
- break;
- }
-
- switch (outputTransfer) {
- case Dataspace::TRANSFER_ST2084:
- wideColorState.setOutputTransferFunction(Description::TransferFunction::ST2084);
- break;
- case Dataspace::TRANSFER_HLG:
- wideColorState.setOutputTransferFunction(Description::TransferFunction::HLG);
- break;
- default:
- wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
- break;
- }
- }
-
- ProgramCache::getInstance().useProgram(wideColorState);
-
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
- if (outputDebugPPMs) {
- static uint64_t wideColorFrameCount = 0;
- std::ostringstream out;
- out << "/data/texture_out" << wideColorFrameCount++;
- writePPM(out.str().c_str(), mVpWidth, mVpHeight);
- }
- } else {
- ProgramCache::getInstance().useProgram(mState);
-
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
- }
-
- if (mesh.getTexCoordsSize()) {
- glDisableVertexAttribArray(Program::texCoords);
- }
-}
-
-void GLES20RenderEngine::dump(String8& result) {
- RenderEngine::dump(result);
- result.appendFormat("RenderEngine last dataspace conversion: (%s) to (%s)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
-}
-
-bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
- const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
- const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
- return standard == Dataspace::STANDARD_BT2020 &&
- (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
-}
-
-// For convenience, we want to convert the input color space to XYZ color space first,
-// and then convert from XYZ color space to output color space when
-// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
-// HDR content will be tone-mapped to SDR; Or,
-// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
-// HLG content to PQ content.
-// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
-// input data space or output data space is HDR data space, and the input transfer function
-// doesn't match the output transfer function, we would enable an intermediate transfrom to
-// XYZ color space.
-bool GLES20RenderEngine::needsXYZTransformMatrix() const {
- const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
- const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
- const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
- const Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace &
- Dataspace::TRANSFER_MASK);
-
- return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
-}
-
-// ---------------------------------------------------------------------------
-} // namespace impl
-} // namespace RE
-} // namespace android
-// ---------------------------------------------------------------------------
-
-#if defined(__gl_h_)
-#error "don't include gl/gl.h in this file"
-#endif
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
deleted file mode 100644
index cc8eb1d..0000000
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 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 SF_GLES20RENDERENGINE_H_
-#define SF_GLES20RENDERENGINE_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <GLES2/gl2.h>
-#include <Transform.h>
-
-#include "Description.h"
-#include "ProgramCache.h"
-#include "RenderEngine.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Mesh;
-class Texture;
-
-namespace RE {
-namespace impl {
-
-class GLES20RenderEngine : public RenderEngine {
- GLuint mProtectedTexName;
- GLint mMaxViewportDims[2];
- GLint mMaxTextureSize;
- GLuint mVpWidth;
- GLuint mVpHeight;
-
- struct Group {
- GLuint texture;
- GLuint fbo;
- GLuint width;
- GLuint height;
- mat4 colorTransform;
- };
-
- Description mState;
- Vector<Group> mGroupStack;
-
- virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName,
- uint32_t* status);
- virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName);
-
-public:
- GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag
- virtual ~GLES20RenderEngine();
-
-protected:
- virtual void dump(String8& result);
- virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh,
- bool yswap, Transform::orientation_flags rotation);
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color) override;
-
- // Color management related functions and state
- void setSourceY410BT2020(bool enable) override;
- void setSourceDataSpace(ui::Dataspace source) override;
- void setOutputDataSpace(ui::Dataspace dataspace) override;
- void setDisplayMaxLuminance(const float maxLuminance) override;
-
- virtual void setupLayerTexturing(const Texture& texture);
- virtual void setupLayerBlackedOut();
- virtual void setupFillWithColor(float r, float g, float b, float a);
- virtual void setupColorTransform(const mat4& colorTransform);
- virtual void disableTexturing();
- virtual void disableBlending();
-
- virtual void drawMesh(const Mesh& mesh);
-
- virtual size_t getMaxTextureSize() const;
- virtual size_t getMaxViewportDims() const;
-
- // Current dataspace of layer being rendered
- ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
-
- // Current output dataspace of the render engine
- ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
-
- // Currently only supporting sRGB, BT2020 and DisplayP3 color spaces
- const bool mPlatformHasWideColor = false;
- mat4 mSrgbToDisplayP3;
- mat4 mDisplayP3ToSrgb;
- mat3 mSrgbToXyz;
- mat3 mBt2020ToXyz;
- mat3 mDisplayP3ToXyz;
- mat4 mXyzToSrgb;
- mat4 mXyzToDisplayP3;
- mat4 mXyzToBt2020;
-
-private:
- // A data space is considered HDR data space if it has BT2020 color space
- // with PQ or HLG transfer function.
- bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
- bool needsXYZTransformMatrix() const;
-};
-
-// ---------------------------------------------------------------------------
-} // namespace impl
-} // namespace RE
-} // namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* SF_GLES20RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/Image.h b/services/surfaceflinger/RenderEngine/Image.h
deleted file mode 100644
index 1ae7e09..0000000
--- a/services/surfaceflinger/RenderEngine/Image.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace RE {
-
-class Image {
-public:
- virtual ~Image() = 0;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected,
- int32_t cropWidth, int32_t cropHeight) = 0;
-};
-
-namespace impl {
-
-class RenderEngine;
-
-class Image : public RE::Image {
-public:
- explicit Image(const RenderEngine& engine);
- ~Image() override;
-
- Image(const Image&) = delete;
- Image& operator=(const Image&) = delete;
-
- bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
- int32_t cropHeight) override;
-
-private:
- // methods internal to RenderEngine
- friend class RenderEngine;
- EGLSurface getEGLImage() const { return mEGLImage; }
-
- EGLDisplay mEGLDisplay;
- EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
-};
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
deleted file mode 100644
index d745770..0000000
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * Copyright 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 <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-#include "GLES20RenderEngine.h"
-#include "GLExtensions.h"
-#include "Image.h"
-#include "Mesh.h"
-#include "RenderEngine.h"
-
-#include <SurfaceFlinger.h>
-#include <vector>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace RE {
-// ---------------------------------------------------------------------------
-
-RenderEngine::~RenderEngine() = default;
-
-namespace impl {
-
-std::unique_ptr<RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) {
- // initialize EGL for the default display
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (!eglInitialize(display, nullptr, nullptr)) {
- LOG_ALWAYS_FATAL("failed to initialize EGL");
- }
-
- GLExtensions& extensions = GLExtensions::getInstance();
- extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
- eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
-
- // The code assumes that ES2 or later is available if this extension is
- // supported.
- EGLConfig config = EGL_NO_CONFIG;
- if (!extensions.hasNoConfigContext()) {
- config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
- }
-
- EGLint renderableType = 0;
- if (config == EGL_NO_CONFIG) {
- renderableType = EGL_OPENGL_ES2_BIT;
- } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
- LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
- }
- EGLint contextClientVersion = 0;
- if (renderableType & EGL_OPENGL_ES2_BIT) {
- contextClientVersion = 2;
- } else if (renderableType & EGL_OPENGL_ES_BIT) {
- contextClientVersion = 1;
- } else {
- LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
- }
-
- std::vector<EGLint> contextAttributes;
- contextAttributes.reserve(6);
- contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
- contextAttributes.push_back(contextClientVersion);
- bool useContextPriority = overrideUseContextPriorityFromConfig(extensions.hasContextPriority());
- if (useContextPriority) {
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
- }
- contextAttributes.push_back(EGL_NONE);
-
- EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data());
-
- // if can't create a GL context, we can only abort.
- LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
-
- // now figure out what version of GL did we actually get
- // NOTE: a dummy surface is not needed if KHR_create_context is supported
-
- EGLConfig dummyConfig = config;
- if (dummyConfig == EGL_NO_CONFIG) {
- dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
- }
- EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
- EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
- LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
- EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
- LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
-
- extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
- glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
-
- GlesVersion version = parseGlesVersion(extensions.getVersion());
-
- // initialize the renderer while GL is current
-
- std::unique_ptr<RenderEngine> engine;
- switch (version) {
- case GLES_VERSION_1_0:
- case GLES_VERSION_1_1:
- LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
- break;
- case GLES_VERSION_2_0:
- case GLES_VERSION_3_0:
- engine = std::make_unique<GLES20RenderEngine>(featureFlags);
- break;
- }
- engine->setEGLHandles(display, config, ctxt);
-
- ALOGI("OpenGL ES informations:");
- ALOGI("vendor : %s", extensions.getVendor());
- ALOGI("renderer : %s", extensions.getRenderer());
- ALOGI("version : %s", extensions.getVersion());
- ALOGI("extensions: %s", extensions.getExtensions());
- ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
- ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
-
- eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglDestroySurface(display, dummy);
-
- return engine;
-}
-
-bool RenderEngine::overrideUseContextPriorityFromConfig(bool useContextPriority) {
- OptionalBool ret;
- ISurfaceFlingerConfigs::getService()->useContextPriority([&ret](OptionalBool b) { ret = b; });
- if (ret.specified) {
- return ret.value;
- } else {
- return useContextPriority;
- }
-}
-
-RenderEngine::RenderEngine(uint32_t featureFlags)
- : mEGLDisplay(EGL_NO_DISPLAY),
- mEGLConfig(nullptr),
- mEGLContext(EGL_NO_CONTEXT),
- mFeatureFlags(featureFlags) {}
-
-RenderEngine::~RenderEngine() {
- eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglTerminate(mEGLDisplay);
-}
-
-void RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) {
- mEGLDisplay = display;
- mEGLConfig = config;
- mEGLContext = ctxt;
-}
-
-EGLDisplay RenderEngine::getEGLDisplay() const {
- return mEGLDisplay;
-}
-
-EGLConfig RenderEngine::getEGLConfig() const {
- return mEGLConfig;
-}
-
-bool RenderEngine::supportsImageCrop() const {
- return GLExtensions::getInstance().hasImageCrop();
-}
-
-bool RenderEngine::isCurrent() const {
- return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
-}
-
-std::unique_ptr<RE::Surface> RenderEngine::createSurface() {
- return std::make_unique<Surface>(*this);
-}
-
-std::unique_ptr<RE::Image> RenderEngine::createImage() {
- return std::make_unique<Image>(*this);
-}
-
-bool RenderEngine::setCurrentSurface(const android::RE::Surface& surface) {
- // Note: RE::Surface is an abstract interface. This implementation only ever
- // creates RE::impl::Surface's, so it is safe to just cast to the actual
- // type.
- return setCurrentSurface(static_cast<const android::RE::impl::Surface&>(surface));
-}
-
-bool RenderEngine::setCurrentSurface(const android::RE::impl::Surface& surface) {
- bool success = true;
- EGLSurface eglSurface = surface.getEGLSurface();
- if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) {
- success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE;
- if (success && surface.getAsync()) {
- eglSwapInterval(mEGLDisplay, 0);
- }
- }
-
- return success;
-}
-
-void RenderEngine::resetCurrentSurface() {
- eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-}
-
-base::unique_fd RenderEngine::flush() {
- if (!GLExtensions::getInstance().hasNativeFenceSync()) {
- return base::unique_fd();
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
- return base::unique_fd();
- }
-
- // native fence fd will not be populated until flush() is done.
- glFlush();
-
- // get the fence fd
- base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
- }
-
- return fenceFd;
-}
-
-bool RenderEngine::finish() {
- if (!GLExtensions::getInstance().hasFenceSync()) {
- ALOGW("no synchronization support");
- return false;
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL fence sync: %#x", eglGetError());
- return false;
- }
-
- EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
- 2000000000 /*2 sec*/);
- EGLint error = eglGetError();
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (result != EGL_CONDITION_SATISFIED_KHR) {
- if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGW("fence wait timed out");
- } else {
- ALOGW("error waiting on EGL fence: %#x", error);
- }
- return false;
- }
-
- return true;
-}
-
-bool RenderEngine::waitFence(base::unique_fd fenceFd) {
- if (!GLExtensions::getInstance().hasNativeFenceSync() ||
- !GLExtensions::getInstance().hasWaitSync()) {
- return false;
- }
-
- EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
- return false;
- }
-
- // fenceFd is now owned by EGLSync
- (void)fenceFd.release();
-
- // XXX: The spec draft is inconsistent as to whether this should return an
- // EGLint or void. Ignore the return value for now, as it's not strictly
- // needed.
- eglWaitSyncKHR(mEGLDisplay, sync, 0);
- EGLint error = eglGetError();
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (error != EGL_SUCCESS) {
- ALOGE("failed to wait for EGL native fence sync: %#x", error);
- return false;
- }
-
- return true;
-}
-
-void RenderEngine::checkErrors() const {
- do {
- // there could be more than one error flag
- GLenum error = glGetError();
- if (error == GL_NO_ERROR) break;
- ALOGE("GL error 0x%04x", int(error));
- } while (true);
-}
-
-RenderEngine::GlesVersion RenderEngine::parseGlesVersion(const char* str) {
- int major, minor;
- if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
- if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
- ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
- return GLES_VERSION_1_0;
- }
- }
-
- if (major == 1 && minor == 0) return GLES_VERSION_1_0;
- if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
- if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
- if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
-
- ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
- return GLES_VERSION_1_0;
-}
-
-void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height, float red,
- float green, float blue, float alpha) {
- size_t c;
- Rect const* r = region.getArray(&c);
- Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- for (size_t i = 0; i < c; i++, r++) {
- position[i * 6 + 0].x = r->left;
- position[i * 6 + 0].y = height - r->top;
- position[i * 6 + 1].x = r->left;
- position[i * 6 + 1].y = height - r->bottom;
- position[i * 6 + 2].x = r->right;
- position[i * 6 + 2].y = height - r->bottom;
- position[i * 6 + 3].x = r->left;
- position[i * 6 + 3].y = height - r->top;
- position[i * 6 + 4].x = r->right;
- position[i * 6 + 4].y = height - r->bottom;
- position[i * 6 + 5].x = r->right;
- position[i * 6 + 5].y = height - r->top;
- }
- setupFillWithColor(red, green, blue, alpha);
- drawMesh(mesh);
-}
-
-void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
- glClearColor(red, green, blue, alpha);
- glClear(GL_COLOR_BUFFER_BIT);
-}
-
-void RenderEngine::setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) {
- glScissor(left, bottom, right, top);
- glEnable(GL_SCISSOR_TEST);
-}
-
-void RenderEngine::disableScissor() {
- glDisable(GL_SCISSOR_TEST);
-}
-
-void RenderEngine::genTextures(size_t count, uint32_t* names) {
- glGenTextures(count, names);
-}
-
-void RenderEngine::deleteTextures(size_t count, uint32_t const* names) {
- glDeleteTextures(count, names);
-}
-
-void RenderEngine::bindExternalTextureImage(uint32_t texName, const android::RE::Image& image) {
- // Note: RE::Image is an abstract interface. This implementation only ever
- // creates RE::impl::Image's, so it is safe to just cast to the actual type.
- return bindExternalTextureImage(texName, static_cast<const android::RE::impl::Image&>(image));
-}
-
-void RenderEngine::bindExternalTextureImage(uint32_t texName,
- const android::RE::impl::Image& image) {
- const GLenum target = GL_TEXTURE_EXTERNAL_OES;
-
- glBindTexture(target, texName);
- if (image.getEGLImage() != EGL_NO_IMAGE_KHR) {
- glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(image.getEGLImage()));
- }
-}
-
-void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
- glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-}
-
-void RenderEngine::dump(String8& result) {
- const GLExtensions& extensions = GLExtensions::getInstance();
-
- result.appendFormat("EGL implementation : %s\n", extensions.getEGLVersion());
- result.appendFormat("%s\n", extensions.getEGLExtensions());
-
- result.appendFormat("GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
- extensions.getVersion());
- result.appendFormat("%s\n", extensions.getExtensions());
-}
-
-// ---------------------------------------------------------------------------
-
-void RenderEngine::bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer,
- RE::BindNativeBufferAsFramebuffer* bindHelper) {
- bindHelper->mImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- buffer, nullptr);
- if (bindHelper->mImage == EGL_NO_IMAGE_KHR) {
- bindHelper->mStatus = NO_MEMORY;
- return;
- }
-
- uint32_t glStatus;
- bindImageAsFramebuffer(bindHelper->mImage, &bindHelper->mTexName, &bindHelper->mFbName,
- &glStatus);
-
- ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
- glStatus);
-
- bindHelper->mStatus = glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
-}
-
-void RenderEngine::unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) {
- if (bindHelper->mImage == EGL_NO_IMAGE_KHR) {
- return;
- }
-
- // back to main framebuffer
- unbindFramebuffer(bindHelper->mTexName, bindHelper->mFbName);
- eglDestroyImageKHR(mEGLDisplay, bindHelper->mImage);
-
- // Workaround for b/77935566 to force the EGL driver to release the
- // screenshot buffer
- setScissor(0, 0, 0, 0);
- clearWithColor(0.0, 0.0, 0.0, 0.0);
- disableScissor();
-}
-
-// ---------------------------------------------------------------------------
-
-static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
- EGLint wanted, EGLConfig* outConfig) {
- EGLint numConfigs = -1, n = 0;
- eglGetConfigs(dpy, nullptr, 0, &numConfigs);
- EGLConfig* const configs = new EGLConfig[numConfigs];
- eglChooseConfig(dpy, attrs, configs, numConfigs, &n);
-
- if (n) {
- if (attribute != EGL_NONE) {
- for (int i = 0; i < n; i++) {
- EGLint value = 0;
- eglGetConfigAttrib(dpy, configs[i], attribute, &value);
- if (wanted == value) {
- *outConfig = configs[i];
- delete[] configs;
- return NO_ERROR;
- }
- }
- } else {
- // just pick the first one
- *outConfig = configs[0];
- delete[] configs;
- return NO_ERROR;
- }
- }
- delete[] configs;
- return NAME_NOT_FOUND;
-}
-
-class EGLAttributeVector {
- struct Attribute;
- class Adder;
- friend class Adder;
- KeyedVector<Attribute, EGLint> mList;
- struct Attribute {
- Attribute() : v(0){};
- explicit Attribute(EGLint v) : v(v) {}
- EGLint v;
- bool operator<(const Attribute& other) const {
- // this places EGL_NONE at the end
- EGLint lhs(v);
- EGLint rhs(other.v);
- if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
- if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
- return lhs < rhs;
- }
- };
- class Adder {
- friend class EGLAttributeVector;
- EGLAttributeVector& v;
- EGLint attribute;
- Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {}
-
- public:
- void operator=(EGLint value) {
- if (attribute != EGL_NONE) {
- v.mList.add(Attribute(attribute), value);
- }
- }
- operator EGLint() const { return v.mList[attribute]; }
- };
-
-public:
- EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); }
- void remove(EGLint attribute) {
- if (attribute != EGL_NONE) {
- mList.removeItem(Attribute(attribute));
- }
- }
- Adder operator[](EGLint attribute) { return Adder(*this, attribute); }
- EGLint operator[](EGLint attribute) const { return mList[attribute]; }
- // cast-operator to (EGLint const*)
- operator EGLint const*() const { return &mList.keyAt(0).v; }
-};
-
-static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
- EGLConfig* config) {
- // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
- // it is to be used with WIFI displays
- status_t err;
- EGLint wantedAttribute;
- EGLint wantedAttributeValue;
-
- EGLAttributeVector attribs;
- if (renderableType) {
- attribs[EGL_RENDERABLE_TYPE] = renderableType;
- attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
- attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
- attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
- attribs[EGL_RED_SIZE] = 8;
- attribs[EGL_GREEN_SIZE] = 8;
- attribs[EGL_BLUE_SIZE] = 8;
- attribs[EGL_ALPHA_SIZE] = 8;
- wantedAttribute = EGL_NONE;
- wantedAttributeValue = EGL_NONE;
- } else {
- // if no renderable type specified, fallback to a simplified query
- wantedAttribute = EGL_NATIVE_VISUAL_ID;
- wantedAttributeValue = format;
- }
-
- err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config);
- if (err == NO_ERROR) {
- EGLint caveat;
- if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
- ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
- }
-
- return err;
-}
-
-EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
- status_t err;
- EGLConfig config;
-
- // First try to get an ES2 config
- err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
- if (err != NO_ERROR) {
- // If ES2 fails, try ES1
- err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config);
- if (err != NO_ERROR) {
- // still didn't work, probably because we're on the emulator...
- // try a simplified query
- ALOGW("no suitable EGLConfig found, trying a simpler query");
- err = selectEGLConfig(display, format, 0, &config);
- if (err != NO_ERROR) {
- // this EGL is too lame for android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
- }
- }
- }
-
- if (logConfig) {
- // print some debugging info
- EGLint r, g, b, a;
- eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
- eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
- eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
- eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
- ALOGI("EGL information:");
- ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
- ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
- ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
- ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
- ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
- }
-
- return config;
-}
-
-void RenderEngine::primeCache() const {
- ProgramCache::getInstance().primeCache(mFeatureFlags & WIDE_COLOR_SUPPORT);
-}
-
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
deleted file mode 100644
index 1786155..0000000
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 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 SF_RENDERENGINE_H_
-#define SF_RENDERENGINE_H_
-
-#include <memory>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <Transform.h>
-#include <android-base/unique_fd.h>
-#include <gui/SurfaceControl.h>
-#include <math/mat4.h>
-
-#define EGL_NO_CONFIG ((EGLConfig)0)
-
-struct ANativeWindowBuffer;
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
-class Rect;
-class Region;
-class Mesh;
-class Texture;
-
-namespace RE {
-
-class Image;
-class Surface;
-class BindNativeBufferAsFramebuffer;
-
-namespace impl {
-class RenderEngine;
-}
-
-class RenderEngine {
-public:
- enum FeatureFlag {
- WIDE_COLOR_SUPPORT = 1 << 0 // Platform has a wide color display
- };
-
- virtual ~RenderEngine() = 0;
-
- virtual std::unique_ptr<RE::Surface> createSurface() = 0;
- virtual std::unique_ptr<RE::Image> createImage() = 0;
-
- virtual void primeCache() const = 0;
-
- // dump the extension strings. always call the base class.
- virtual void dump(String8& result) = 0;
-
- virtual bool supportsImageCrop() const = 0;
-
- virtual bool isCurrent() const = 0;
- virtual bool setCurrentSurface(const RE::Surface& surface) = 0;
- virtual void resetCurrentSurface() = 0;
-
- // helpers
- // flush submits RenderEngine command stream for execution and returns a
- // native fence fd that is signaled when the execution has completed. It
- // returns -1 on errors.
- virtual base::unique_fd flush() = 0;
- // finish waits until RenderEngine command stream has been executed. It
- // returns false on errors.
- virtual bool finish() = 0;
- // waitFence inserts a wait on an external fence fd to RenderEngine
- // command stream. It returns false on errors.
- virtual bool waitFence(base::unique_fd fenceFd) = 0;
-
- virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
- virtual void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
- float blue, float alpha) = 0;
-
- // common to all GL versions
- virtual void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) = 0;
- virtual void disableScissor() = 0;
- virtual void genTextures(size_t count, uint32_t* names) = 0;
- virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
- virtual void bindExternalTextureImage(uint32_t texName, const RE::Image& image) = 0;
- virtual void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) = 0;
- virtual void bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer,
- RE::BindNativeBufferAsFramebuffer* bindHelper) = 0;
- virtual void unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) = 0;
-
- // set-up
- virtual void checkErrors() const;
- virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh,
- bool yswap, Transform::orientation_flags rotation) = 0;
- virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color) = 0;
- virtual void setupLayerTexturing(const Texture& texture) = 0;
- virtual void setupLayerBlackedOut() = 0;
- virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
-
- virtual void setupColorTransform(const mat4& /* colorTransform */) = 0;
-
- virtual void disableTexturing() = 0;
- virtual void disableBlending() = 0;
-
- // HDR and wide color gamut support
- virtual void setSourceY410BT2020(bool enable) = 0;
- virtual void setSourceDataSpace(ui::Dataspace source) = 0;
- virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0;
- virtual void setDisplayMaxLuminance(const float maxLuminance) = 0;
-
- // drawing
- virtual void drawMesh(const Mesh& mesh) = 0;
-
- // queries
- virtual size_t getMaxTextureSize() const = 0;
- virtual size_t getMaxViewportDims() const = 0;
-};
-
-class BindNativeBufferAsFramebuffer {
-public:
- BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
- : mEngine(engine) {
- mEngine.bindNativeBufferAsFrameBuffer(buffer, this);
- }
- ~BindNativeBufferAsFramebuffer() { mEngine.unbindNativeBufferAsFrameBuffer(this); }
- status_t getStatus() const { return mStatus; }
-
-protected:
- friend impl::RenderEngine;
-
- RenderEngine& mEngine;
- EGLImageKHR mImage;
- uint32_t mTexName, mFbName;
- status_t mStatus;
-};
-
-namespace impl {
-
-class Image;
-class Surface;
-
-class RenderEngine : public RE::RenderEngine {
- enum GlesVersion {
- GLES_VERSION_1_0 = 0x10000,
- GLES_VERSION_1_1 = 0x10001,
- GLES_VERSION_2_0 = 0x20000,
- GLES_VERSION_3_0 = 0x30000,
- };
- static GlesVersion parseGlesVersion(const char* str);
-
- EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
- EGLContext mEGLContext;
- void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt);
-
- static bool overrideUseContextPriorityFromConfig(bool useContextPriority);
-
-protected:
- RenderEngine(uint32_t featureFlags);
-
- const uint32_t mFeatureFlags;
-
-public:
- virtual ~RenderEngine() = 0;
-
- static std::unique_ptr<RenderEngine> create(int hwcFormat, uint32_t featureFlags);
-
- static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
-
- // RenderEngine interface implementation
-
- std::unique_ptr<RE::Surface> createSurface() override;
- std::unique_ptr<RE::Image> createImage() override;
-
- void primeCache() const override;
-
- // dump the extension strings. always call the base class.
- void dump(String8& result) override;
-
- bool supportsImageCrop() const override;
-
- bool isCurrent() const;
- bool setCurrentSurface(const RE::Surface& surface) override;
- void resetCurrentSurface() override;
-
- // synchronization
-
- // flush submits RenderEngine command stream for execution and returns a
- // native fence fd that is signaled when the execution has completed. It
- // returns -1 on errors.
- base::unique_fd flush() override;
- // finish waits until RenderEngine command stream has been executed. It
- // returns false on errors.
- bool finish() override;
- // waitFence inserts a wait on an external fence fd to RenderEngine
- // command stream. It returns false on errors.
- bool waitFence(base::unique_fd fenceFd) override;
-
- // helpers
- void clearWithColor(float red, float green, float blue, float alpha) override;
- void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
- float blue, float alpha) override;
-
- // common to all GL versions
- void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) override;
- void disableScissor() 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 RE::Image& image) override;
- void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) override;
-
- void checkErrors() const override;
-
- void setupColorTransform(const mat4& /* colorTransform */) override {}
-
- // internal to RenderEngine
- EGLDisplay getEGLDisplay() const;
- EGLConfig getEGLConfig() const;
-
- // Common implementation
- bool setCurrentSurface(const RE::impl::Surface& surface);
- void bindExternalTextureImage(uint32_t texName, const RE::impl::Image& image);
-
- void bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer,
- RE::BindNativeBufferAsFramebuffer* bindHelper) override;
- void unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) override;
-
- // Overriden by each specialization
- virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName,
- uint32_t* status) = 0;
- virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName) = 0;
-};
-
-} // namespace impl
-} // namespace RE
-} // namespace android
-
-#endif /* SF_RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/Surface.cpp b/services/surfaceflinger/RenderEngine/Surface.cpp
deleted file mode 100644
index 0d20f1f..0000000
--- a/services/surfaceflinger/RenderEngine/Surface.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Surface.h"
-
-#include "RenderEngine.h"
-
-#include <log/log.h>
-
-namespace android {
-namespace RE {
-
-Surface::~Surface() = default;
-
-namespace impl {
-
-Surface::Surface(const RenderEngine& engine)
- : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) {
- // RE does not assume any config when EGL_KHR_no_config_context is supported
- if (mEGLConfig == EGL_NO_CONFIG_KHR) {
- mEGLConfig = RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false);
- }
-}
-
-Surface::~Surface() {
- setNativeWindow(nullptr);
-}
-
-void Surface::setNativeWindow(ANativeWindow* window) {
- if (mEGLSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEGLDisplay, mEGLSurface);
- mEGLSurface = EGL_NO_SURFACE;
- }
-
- mWindow = window;
- if (mWindow) {
- mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr);
- }
-}
-
-void Surface::swapBuffers() const {
- if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
- EGLint error = eglGetError();
-
- const char format[] = "eglSwapBuffers(%p, %p) failed with 0x%08x";
- if (mCritical || error == EGL_CONTEXT_LOST) {
- LOG_ALWAYS_FATAL(format, mEGLDisplay, mEGLSurface, error);
- } else {
- ALOGE(format, mEGLDisplay, mEGLSurface, error);
- }
- }
-}
-
-EGLint Surface::queryConfig(EGLint attrib) const {
- EGLint value;
- if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) {
- value = 0;
- }
-
- return value;
-}
-
-EGLint Surface::querySurface(EGLint attrib) const {
- EGLint value;
- if (!eglQuerySurface(mEGLDisplay, mEGLSurface, attrib, &value)) {
- value = 0;
- }
-
- return value;
-}
-
-int32_t Surface::queryRedSize() const {
- return queryConfig(EGL_RED_SIZE);
-}
-
-int32_t Surface::queryGreenSize() const {
- return queryConfig(EGL_GREEN_SIZE);
-}
-
-int32_t Surface::queryBlueSize() const {
- return queryConfig(EGL_BLUE_SIZE);
-}
-
-int32_t Surface::queryAlphaSize() const {
- return queryConfig(EGL_ALPHA_SIZE);
-}
-
-int32_t Surface::queryWidth() const {
- return querySurface(EGL_WIDTH);
-}
-
-int32_t Surface::queryHeight() const {
- return querySurface(EGL_HEIGHT);
-}
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/Surface.h b/services/surfaceflinger/RenderEngine/Surface.h
deleted file mode 100644
index d4d3d8c..0000000
--- a/services/surfaceflinger/RenderEngine/Surface.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-
-struct ANativeWindow;
-
-namespace android {
-namespace RE {
-
-class Surface {
-public:
- virtual ~Surface() = 0;
-
- virtual void setCritical(bool enable) = 0;
- virtual void setAsync(bool enable) = 0;
-
- virtual void setNativeWindow(ANativeWindow* window) = 0;
- virtual void swapBuffers() const = 0;
-
- virtual int32_t queryRedSize() const = 0;
- virtual int32_t queryGreenSize() const = 0;
- virtual int32_t queryBlueSize() const = 0;
- virtual int32_t queryAlphaSize() const = 0;
-
- virtual int32_t queryWidth() const = 0;
- virtual int32_t queryHeight() const = 0;
-};
-
-namespace impl {
-
-class RenderEngine;
-
-class Surface final : public RE::Surface {
-public:
- Surface(const RenderEngine& engine);
- ~Surface();
-
- Surface(const Surface&) = delete;
- Surface& operator=(const Surface&) = delete;
-
- // RE::Surface implementation
- void setCritical(bool enable) override { mCritical = enable; }
- void setAsync(bool enable) override { mAsync = enable; }
-
- void setNativeWindow(ANativeWindow* window) override;
- void swapBuffers() const override;
-
- int32_t queryRedSize() const override;
- int32_t queryGreenSize() const override;
- int32_t queryBlueSize() const override;
- int32_t queryAlphaSize() const override;
-
- int32_t queryWidth() const override;
- int32_t queryHeight() const override;
-
-private:
- EGLint queryConfig(EGLint attrib) const;
- EGLint querySurface(EGLint attrib) const;
-
- // methods internal to RenderEngine
- friend class RenderEngine;
- bool getAsync() const { return mAsync; }
- EGLSurface getEGLSurface() const { return mEGLSurface; }
-
- EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
-
- bool mCritical = false;
- bool mAsync = false;
-
- ANativeWindow* mWindow = nullptr;
- EGLSurface mEGLSurface = EGL_NO_SURFACE;
-};
-
-} // namespace impl
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
similarity index 66%
rename from services/surfaceflinger/DispSync.cpp
rename to services/surfaceflinger/Scheduler/DispSync.cpp
index 37dc27d..6599f9e 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -24,11 +24,11 @@
#include <algorithm>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
#include <log/log.h>
-#include <utils/String8.h>
#include <utils/Thread.h>
#include <utils/Trace.h>
-#include <utils/Vector.h>
#include <ui/FenceTime.h>
@@ -36,14 +36,16 @@
#include "EventLog/EventLog.h"
#include "SurfaceFlinger.h"
+using android::base::StringAppendF;
using std::max;
using std::min;
namespace android {
-// Setting this to true enables verbose tracing that can be used to debug
-// vsync event model or phase issues.
-static const bool kTraceDetailedInfo = false;
+DispSync::~DispSync() = default;
+DispSync::Callback::~Callback() = default;
+
+namespace impl {
// Setting this to true adds a zero-phase tracer for correlating with hardware
// vsync events
@@ -59,23 +61,38 @@
#define LOG_TAG "DispSyncThread"
class DispSyncThread : public Thread {
public:
- explicit DispSyncThread(const char* name)
+ DispSyncThread(const char* name, bool showTraceDetailedInfo)
: mName(name),
mStop(false),
mPeriod(0),
mPhase(0),
mReferenceTime(0),
mWakeupLatency(0),
- mFrameNumber(0) {}
+ mFrameNumber(0),
+ mTraceDetailedInfo(showTraceDetailedInfo) {}
virtual ~DispSyncThread() {}
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- mPeriod = period;
+
mPhase = phase;
mReferenceTime = referenceTime;
+ if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
+ // Inflate the reference time to be the most recent predicted
+ // vsync before the current time.
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t baseTime = now - mReferenceTime;
+ const nsecs_t numOldPeriods = baseTime / mPeriod;
+ mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
+ }
+ mPeriod = period;
+ if (mTraceDetailedInfo) {
+ ATRACE_INT64("DispSync:Period", mPeriod);
+ ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
+ ATRACE_INT64("DispSync:Reference Time", mReferenceTime);
+ }
ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
" mReferenceTime = %" PRId64,
mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime));
@@ -83,7 +100,7 @@
}
void stop() {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
mStop = true;
mCond.signal();
@@ -94,14 +111,14 @@
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (true) {
- Vector<CallbackInvocation> callbackInvocations;
+ std::vector<CallbackInvocation> callbackInvocations;
nsecs_t targetTime = 0;
{ // Scope for lock
Mutex::Autolock lock(mMutex);
- if (kTraceDetailedInfo) {
+ if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Frame", mFrameNumber);
}
ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
@@ -125,7 +142,7 @@
bool isWakeup = false;
if (now < targetTime) {
- if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
+ if (mTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
if (targetTime == INT64_MAX) {
ALOGV("[%s] Waiting forever", mName);
@@ -151,7 +168,7 @@
if (isWakeup) {
mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64;
mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
- if (kTraceDetailedInfo) {
+ if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
}
@@ -168,8 +185,9 @@
return false;
}
- status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
+ nsecs_t lastCallbackTime) {
+ if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -184,23 +202,51 @@
listener.mCallback = callback;
// We want to allow the firstmost future event to fire without
- // allowing any past events to fire
- listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;
+ // allowing any past events to fire. To do this extrapolate from
+ // mReferenceTime the most recent hardware vsync, and pin the
+ // last event time there.
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (mPeriod != 0) {
+ const nsecs_t baseTime = now - mReferenceTime;
+ const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
+ const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
+ const nsecs_t phaseCorrection = mPhase + listener.mPhase;
+ const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
+ if (predictedLastEventTime >= now) {
+ // Make sure that the last event time does not exceed the current time.
+ // If it would, then back the last event time by a period.
+ listener.mLastEventTime = predictedLastEventTime - mPeriod;
+ } else {
+ listener.mLastEventTime = predictedLastEventTime;
+ }
+ } else {
+ listener.mLastEventTime = now + mPhase - mWakeupLatency;
+ }
- mEventListeners.push(listener);
+ if (lastCallbackTime <= 0) {
+ // If there is no prior callback time, try to infer one based on the
+ // logical last event time.
+ listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency;
+ } else {
+ listener.mLastCallbackTime = lastCallbackTime;
+ }
+
+ mEventListeners.push_back(listener);
mCond.signal();
return NO_ERROR;
}
- status_t removeEventListener(DispSync::Callback* callback) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) {
+ if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- for (size_t i = 0; i < mEventListeners.size(); i++) {
- if (mEventListeners[i].mCallback == callback) {
- mEventListeners.removeAt(i);
+ for (std::vector<EventListener>::iterator it = mEventListeners.begin();
+ it != mEventListeners.end(); ++it) {
+ if (it->mCallback == callback) {
+ *outLastCallback = it->mLastCallbackTime;
+ mEventListeners.erase(it);
mCond.signal();
return NO_ERROR;
}
@@ -210,14 +256,13 @@
}
status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- for (size_t i = 0; i < mEventListeners.size(); i++) {
- if (mEventListeners[i].mCallback == callback) {
- EventListener& listener = mEventListeners.editItemAt(i);
- const nsecs_t oldPhase = listener.mPhase;
- listener.mPhase = phase;
+ for (auto& eventListener : mEventListeners) {
+ if (eventListener.mCallback == callback) {
+ const nsecs_t oldPhase = eventListener.mPhase;
+ eventListener.mPhase = phase;
// Pretend that the last time this event was handled at the same frame but with the
// new offset to allow for a seamless offset change without double-firing or
@@ -228,7 +273,7 @@
} else if (diff < -mPeriod / 2) {
diff += mPeriod;
}
- listener.mLastEventTime -= diff;
+ eventListener.mLastEventTime -= diff;
mCond.signal();
return NO_ERROR;
}
@@ -237,19 +282,12 @@
return BAD_VALUE;
}
- // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
- // case.
- bool hasAnyEventListeners() {
- if (kTraceDetailedInfo) ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
- return !mEventListeners.empty();
- }
-
private:
struct EventListener {
const char* mName;
nsecs_t mPhase;
nsecs_t mLastEventTime;
+ nsecs_t mLastCallbackTime;
DispSync::Callback* mCallback;
};
@@ -259,7 +297,7 @@
};
nsecs_t computeNextEventTimeLocked(nsecs_t now) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ if (mTraceDetailedInfo) ATRACE_CALL();
ALOGV("[%s] computeNextEventTimeLocked", mName);
nsecs_t nextEventTime = INT64_MAX;
for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -274,23 +312,39 @@
return nextEventTime;
}
- Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ // Sanity check that the duration is close enough in length to a period without
+ // falling into double-rate vsyncs.
+ bool isCloseToPeriod(nsecs_t duration) {
+ // Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
+ return duration < (3 * mPeriod) / 5;
+ }
+
+ std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+ if (mTraceDetailedInfo) ATRACE_CALL();
ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now));
- Vector<CallbackInvocation> callbackInvocations;
+ std::vector<CallbackInvocation> callbackInvocations;
nsecs_t onePeriodAgo = now - mPeriod;
- for (size_t i = 0; i < mEventListeners.size(); i++) {
- nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], onePeriodAgo);
+ for (auto& eventListener : mEventListeners) {
+ nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
if (t < now) {
+ if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) {
+ eventListener.mLastEventTime = t;
+ eventListener.mLastCallbackTime = now;
+ ALOGV("[%s] [%s] Skipping event due to model error", mName,
+ eventListener.mName);
+ continue;
+ }
CallbackInvocation ci;
- ci.mCallback = mEventListeners[i].mCallback;
+ ci.mCallback = eventListener.mCallback;
ci.mEventTime = t;
- ALOGV("[%s] [%s] Preparing to fire", mName, mEventListeners[i].mName);
- callbackInvocations.push(ci);
- mEventListeners.editItemAt(i).mLastEventTime = t;
+ ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName,
+ t - eventListener.mLastEventTime);
+ callbackInvocations.push_back(ci);
+ eventListener.mLastEventTime = t;
+ eventListener.mLastCallbackTime = now;
}
}
@@ -298,7 +352,7 @@
}
nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ if (mTraceDetailedInfo) ATRACE_CALL();
ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName,
ns2us(baseTime));
@@ -337,7 +391,7 @@
// Check that it's been slightly more than half a period since the last
// event so that we don't accidentally fall into double-rate vsyncs
- if (t - listener.mLastEventTime < (3 * mPeriod / 5)) {
+ if (isCloseToPeriod(t - listener.mLastEventTime)) {
t += mPeriod;
ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
}
@@ -348,8 +402,8 @@
return t;
}
- void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
+ if (mTraceDetailedInfo) ATRACE_CALL();
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
@@ -366,10 +420,13 @@
int64_t mFrameNumber;
- Vector<EventListener> mEventListeners;
+ std::vector<EventListener> mEventListeners;
Mutex mMutex;
Condition mCond;
+
+ // Flag to turn on logging in systrace.
+ const bool mTraceDetailedInfo;
};
#undef LOG_TAG
@@ -388,10 +445,18 @@
bool mParity;
};
-DispSync::DispSync(const char* name)
- : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {}
+DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) {
+ // This flag offers the ability to turn on systrace logging from the shell.
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.dispsync_trace_detailed_info", value, "0");
+ mTraceDetailedInfo = atoi(value);
+ mThread = new DispSyncThread(name, mTraceDetailedInfo);
+}
-DispSync::~DispSync() {}
+DispSync::~DispSync() {
+ mThread->stop();
+ mThread->requestExitAndWait();
+}
void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
mIgnorePresentFences = !hasSyncFramework;
@@ -408,25 +473,29 @@
reset();
beginResync();
- if (kTraceDetailedInfo) {
- // If we're not getting present fences then the ZeroPhaseTracer
- // would prevent HW vsync event from ever being turned off.
- // Even if we're just ignoring the fences, the zero-phase tracing is
- // not needed because any time there is an event registered we will
- // turn on the HW vsync events.
- if (!mIgnorePresentFences && kEnableZeroPhaseTracer) {
- mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
- addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
- }
+ if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
+ mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
+ addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0);
}
}
void DispSync::reset() {
Mutex::Autolock lock(mMutex);
+ resetLocked();
+}
+void DispSync::resetLocked() {
mPhase = 0;
- mReferenceTime = 0;
+ const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
+ // Keep the most recent sample, when we resync to hardware we'll overwrite this
+ // with a more accurate signal
+ if (mResyncSamples[lastSampleIdx] != 0) {
+ mReferenceTime = mResyncSamples[lastSampleIdx];
+ }
mModelUpdated = false;
+ for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) {
+ mResyncSamples[i] = 0;
+ }
mNumResyncSamples = 0;
mFirstResyncSample = 0;
mNumResyncSamplesSincePresent = 0;
@@ -436,6 +505,10 @@
bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
Mutex::Autolock lock(mMutex);
+ if (mIgnorePresentFences) {
+ return true;
+ }
+
mPresentFences[mPresentSampleOffset] = fenceTime;
mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
mNumResyncSamplesSincePresent = 0;
@@ -481,12 +554,10 @@
}
if (mIgnorePresentFences) {
- // If we don't have the sync framework we will never have
- // addPresentFence called. This means we have no way to know whether
+ // If we're ignoring the present fences we have no way to know whether
// or not we're synchronized with the HW vsyncs, so we just request
- // that the HW vsync events be turned on whenever we need to generate
- // SW vsync events.
- return mThread->hasAnyEventListeners();
+ // that the HW vsync events be turned on.
+ return true;
}
// Check against kErrorThreshold / 2 to add some hysteresis before having to
@@ -498,9 +569,10 @@
void DispSync::endResync() {}
-status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback) {
+status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback,
+ nsecs_t lastCallbackTime) {
Mutex::Autolock lock(mMutex);
- return mThread->addEventListener(name, phase, callback);
+ return mThread->addEventListener(name, phase, callback, lastCallbackTime);
}
void DispSync::setRefreshSkipCount(int count) {
@@ -510,9 +582,9 @@
updateModelLocked();
}
-status_t DispSync::removeEventListener(Callback* callback) {
+status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) {
Mutex::Autolock lock(mMutex);
- return mThread->removeEventListener(callback);
+ return mThread->removeEventListener(callback, outLastCallbackTime);
}
status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
@@ -524,7 +596,6 @@
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = 0;
- mReferenceTime = 0;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
@@ -580,11 +651,6 @@
ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
}
- if (kTraceDetailedInfo) {
- ATRACE_INT64("DispSync:Period", mPeriod);
- ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
- }
-
// Artificially inflate the period if requested.
mPeriod += mPeriod * mRefreshSkipCount;
@@ -639,7 +705,7 @@
"No present times for model error.");
}
- if (kTraceDetailedInfo) {
+ if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Error", mError);
}
}
@@ -657,57 +723,84 @@
Mutex::Autolock lock(mMutex);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
nsecs_t phase = mReferenceTime + mPhase;
+ if (mPeriod == 0) {
+ return 0;
+ }
return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
}
-void DispSync::dump(String8& result) const {
+void DispSync::setIgnorePresentFences(bool ignore) {
Mutex::Autolock lock(mMutex);
- result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
- result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod,
- 1000000000.0 / mPeriod, mRefreshSkipCount);
- result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase);
- result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
- result.appendFormat("mNumResyncSamplesSincePresent: %d (limit %d)\n",
- mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
- result.appendFormat("mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples, MAX_RESYNC_SAMPLES);
+ if (mIgnorePresentFences != ignore) {
+ mIgnorePresentFences = ignore;
+ resetLocked();
+ }
+}
- result.appendFormat("mResyncSamples:\n");
+void DispSync::dump(std::string& result) const {
+ Mutex::Autolock lock(mMutex);
+ StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
+ StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod,
+ 1000000000.0 / mPeriod, mRefreshSkipCount);
+ StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase);
+ StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
+ StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n",
+ mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
+ StringAppendF(&result, "mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples,
+ MAX_RESYNC_SAMPLES);
+
+ result.append("mResyncSamples:\n");
nsecs_t previous = -1;
for (size_t i = 0; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sampleTime = mResyncSamples[idx];
if (i == 0) {
- result.appendFormat(" %" PRId64 "\n", sampleTime);
+ StringAppendF(&result, " %" PRId64 "\n", sampleTime);
} else {
- result.appendFormat(" %" PRId64 " (+%" PRId64 ")\n", sampleTime,
- sampleTime - previous);
+ StringAppendF(&result, " %" PRId64 " (+%" PRId64 ")\n", sampleTime,
+ sampleTime - previous);
}
previous = sampleTime;
}
- result.appendFormat("mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
+ StringAppendF(&result, "mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
previous = Fence::SIGNAL_TIME_INVALID;
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
nsecs_t presentTime = mPresentFences[idx]->getSignalTime();
if (presentTime == Fence::SIGNAL_TIME_PENDING) {
- result.appendFormat(" [unsignaled fence]\n");
+ StringAppendF(&result, " [unsignaled fence]\n");
} else if (presentTime == Fence::SIGNAL_TIME_INVALID) {
- result.appendFormat(" [invalid fence]\n");
+ StringAppendF(&result, " [invalid fence]\n");
} else if (previous == Fence::SIGNAL_TIME_PENDING ||
previous == Fence::SIGNAL_TIME_INVALID) {
- result.appendFormat(" %" PRId64 " (%.3f ms ago)\n", presentTime,
- (now - presentTime) / 1000000.0);
+ StringAppendF(&result, " %" PRId64 " (%.3f ms ago)\n", presentTime,
+ (now - presentTime) / 1000000.0);
} else {
- result.appendFormat(" %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n", presentTime,
- presentTime - previous, (presentTime - previous) / (double)mPeriod,
- (now - presentTime) / 1000000.0);
+ StringAppendF(&result, " %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n",
+ presentTime, presentTime - previous,
+ (presentTime - previous) / (double)mPeriod,
+ (now - presentTime) / 1000000.0);
}
previous = presentTime;
}
- result.appendFormat("current monotonic time: %" PRId64 "\n", now);
+ StringAppendF(&result, "current monotonic time: %" PRId64 "\n", now);
}
+nsecs_t DispSync::expectedPresentTime() {
+ // The HWC doesn't currently have a way to report additional latency.
+ // Assume that whatever we submit now will appear right after the flip.
+ // For a smart panel this might be 1. This is expressed in frames,
+ // rather than time, because we expect to have a constant frame delay
+ // regardless of the refresh rate.
+ const uint32_t hwcLatency = 0;
+
+ // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
+ return computeNextRefresh(hwcLatency);
+}
+
+} // namespace impl
+
} // namespace android
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
similarity index 68%
rename from services/surfaceflinger/DispSync.h
rename to services/surfaceflinger/Scheduler/DispSync.h
index 077256a..de2b874 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_DISPSYNC_H
-#define ANDROID_DISPSYNC_H
+#pragma once
#include <stddef.h>
@@ -29,8 +28,49 @@
namespace android {
-class String8;
class FenceTime;
+
+class DispSync {
+public:
+ class Callback {
+ public:
+ Callback() = default;
+ virtual ~Callback();
+ virtual void onDispSyncEvent(nsecs_t when) = 0;
+
+ protected:
+ Callback(Callback const&) = delete;
+ Callback& operator=(Callback const&) = delete;
+ };
+
+ DispSync() = default;
+ virtual ~DispSync();
+
+ virtual void reset() = 0;
+ virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
+ virtual void beginResync() = 0;
+ virtual bool addResyncSample(nsecs_t timestamp) = 0;
+ virtual void endResync() = 0;
+ virtual void setPeriod(nsecs_t period) = 0;
+ virtual nsecs_t getPeriod() = 0;
+ virtual void setRefreshSkipCount(int count) = 0;
+ virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
+ nsecs_t lastCallbackTime) = 0;
+ virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
+ virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
+ virtual nsecs_t computeNextRefresh(int periodOffset) const = 0;
+ virtual void setIgnorePresentFences(bool ignore) = 0;
+ virtual nsecs_t expectedPresentTime() = 0;
+
+ virtual void dump(std::string& result) const = 0;
+
+protected:
+ DispSync(DispSync const&) = delete;
+ DispSync& operator=(DispSync const&) = delete;
+};
+
+namespace impl {
+
class DispSyncThread;
// DispSync maintains a model of the periodic hardware-based vsync events of a
@@ -46,21 +86,15 @@
// current model accurately represents the hardware event times it will return
// false to indicate that a resynchronization (via addResyncSample) is not
// needed.
-class DispSync {
+class DispSync : public android::DispSync {
public:
- class Callback {
- public:
- virtual ~Callback(){};
- virtual void onDispSyncEvent(nsecs_t when) = 0;
- };
-
explicit DispSync(const char* name);
- ~DispSync();
+ ~DispSync() override;
void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset);
// reset clears the resync samples and error value.
- void reset();
+ void reset() override;
// addPresentFence adds a fence for use in validating the current vsync
// event model. The fence need not be signaled at the time
@@ -71,7 +105,7 @@
//
// This method should be called with the retire fence from each HWComposer
// set call that affects the display.
- bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+ bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override;
// The beginResync, addResyncSample, and endResync methods are used to re-
// synchronize the DispSync's model to the hardware vsync events. The re-
@@ -84,52 +118,73 @@
// is turned on (i.e. once immediately after it's turned on) and whenever
// addPresentFence returns true indicating that the model has drifted away
// from the hardware vsync events.
- void beginResync();
- bool addResyncSample(nsecs_t timestamp);
- void endResync();
+ void beginResync() override;
+ bool addResyncSample(nsecs_t timestamp) override;
+ void endResync() override;
// The setPeriod method sets the vsync event model's period to a specific
// value. This should be used to prime the model when a display is first
// turned on. It should NOT be used after that.
- void setPeriod(nsecs_t period);
+ void setPeriod(nsecs_t period) override;
// The getPeriod method returns the current vsync period.
- nsecs_t getPeriod();
+ nsecs_t getPeriod() override;
// setRefreshSkipCount specifies an additional number of refresh
// cycles to skip. For example, on a 60Hz display, a skip count of 1
// will result in events happening at 30Hz. Default is zero. The idea
// is to sacrifice smoothness for battery life.
- void setRefreshSkipCount(int count);
+ void setRefreshSkipCount(int count) override;
// addEventListener registers a callback to be called repeatedly at the
// given phase offset from the hardware vsync events. The callback is
// called from a separate thread and it should return reasonably quickly
// (i.e. within a few hundred microseconds).
- status_t addEventListener(const char* name, nsecs_t phase, Callback* callback);
+ // If the callback was previously registered, and the last clock time the
+ // callback was invoked was known to the caller (e.g. via removeEventListener),
+ // then the caller may pass that through to lastCallbackTime, so that
+ // callbacks do not accidentally double-fire if they are unregistered and
+ // reregistered in rapid succession.
+ status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
+ nsecs_t lastCallbackTime) override;
// removeEventListener removes an already-registered event callback. Once
// this method returns that callback will no longer be called by the
// DispSync object.
- status_t removeEventListener(Callback* callback);
+ // outLastCallbackTime will contain the last time that the callback was invoked.
+ // If the caller wishes to reregister the same callback, they should pass the
+ // callback time back into lastCallbackTime (see addEventListener).
+ status_t removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) override;
// changePhaseOffset changes the phase offset of an already-registered event callback. The
// method will make sure that there is no skipping or double-firing on the listener per frame,
// even when changing the offsets multiple times.
- status_t changePhaseOffset(Callback* callback, nsecs_t phase);
+ status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
// computeNextRefresh computes when the next refresh is expected to begin.
// The periodOffset value can be used to move forward or backward; an
// offset of zero is the next refresh, -1 is the previous refresh, 1 is
// the refresh after next. etc.
- nsecs_t computeNextRefresh(int periodOffset) const;
+ nsecs_t computeNextRefresh(int periodOffset) const override;
+
+ // In certain situations the present fences aren't a good indicator of vsync
+ // time, e.g. when vr flinger is active, or simply aren't available,
+ // e.g. when the sync framework isn't present. Use this method to toggle
+ // whether or not DispSync ignores present fences. If present fences are
+ // ignored, DispSync will always ask for hardware vsync events by returning
+ // true from addPresentFence() and addResyncSample().
+ void setIgnorePresentFences(bool ignore) override;
+
+ // Determine the expected present time when a buffer acquired now will be displayed.
+ nsecs_t expectedPresentTime();
// dump appends human-readable debug info to the result string.
- void dump(String8& result) const;
+ void dump(std::string& result) const override;
private:
void updateModelLocked();
void updateErrorLocked();
+ void resetLocked();
void resetErrorLocked();
enum { MAX_RESYNC_SAMPLES = 32 };
@@ -168,7 +223,7 @@
// These member variables are the state used during the resynchronization
// process to store information about the hardware vsync event times used
// to compute the model.
- nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+ nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
size_t mFirstResyncSample;
size_t mNumResyncSamples;
int mNumResyncSamplesSincePresent;
@@ -195,8 +250,11 @@
bool mIgnorePresentFences;
std::unique_ptr<Callback> mZeroPhaseTracer;
+
+ // Flag to turn on logging in systrace.
+ bool mTraceDetailedInfo = false;
};
-} // namespace android
+} // namespace impl
-#endif // ANDROID_DISPSYNC_H
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
new file mode 100644
index 0000000..6e89648
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 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 "DispSyncSource.h"
+
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <mutex>
+
+#include "DispSync.h"
+#include "EventThread.h"
+
+namespace android {
+
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+ const char* name)
+ : mName(name),
+ mTraceVsync(traceVsync),
+ mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
+ mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
+ mDispSync(dispSync),
+ mPhaseOffset(phaseOffset) {}
+
+void DispSyncSource::setVSyncEnabled(bool enable) {
+ std::lock_guard lock(mVsyncMutex);
+ if (enable) {
+ status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
+ static_cast<DispSync::Callback*>(this),
+ mLastCallbackTime);
+ if (err != NO_ERROR) {
+ ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
+ }
+ // ATRACE_INT(mVsyncOnLabel.c_str(), 1);
+ } else {
+ status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this),
+ &mLastCallbackTime);
+ if (err != NO_ERROR) {
+ ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err);
+ }
+ // ATRACE_INT(mVsyncOnLabel.c_str(), 0);
+ }
+ mEnabled = enable;
+}
+
+void DispSyncSource::setCallback(VSyncSource::Callback* callback) {
+ std::lock_guard lock(mCallbackMutex);
+ mCallback = callback;
+}
+
+void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
+ std::lock_guard lock(mVsyncMutex);
+
+ // Normalize phaseOffset to [0, period)
+ auto period = mDispSync->getPeriod();
+ phaseOffset %= period;
+ if (phaseOffset < 0) {
+ // If we're here, then phaseOffset is in (-period, 0). After this
+ // operation, it will be in (0, period)
+ phaseOffset += period;
+ }
+ mPhaseOffset = phaseOffset;
+
+ // If we're not enabled, we don't need to mess with the listeners
+ if (!mEnabled) {
+ return;
+ }
+
+ status_t err =
+ mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset);
+ if (err != NO_ERROR) {
+ ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
+ }
+}
+
+void DispSyncSource::pauseVsyncCallback(bool pause) {
+ std::lock_guard lock(mVsyncMutex);
+ mCallbackPaused = pause;
+}
+
+void DispSyncSource::onDispSyncEvent(nsecs_t when) {
+ {
+ std::lock_guard lock(mVsyncMutex);
+ if (mCallbackPaused) {
+ return;
+ }
+ }
+
+ VSyncSource::Callback* callback;
+ {
+ std::lock_guard lock(mCallbackMutex);
+ callback = mCallback;
+
+ if (mTraceVsync) {
+ mValue = (mValue + 1) % 2;
+ ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
+ }
+ }
+
+ if (callback != nullptr) {
+ callback->onVSyncEvent(when);
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
new file mode 100644
index 0000000..2858678
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2018 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 <mutex>
+#include <string>
+
+#include "DispSync.h"
+#include "EventThread.h"
+
+namespace android {
+
+class DispSyncSource final : public VSyncSource, private DispSync::Callback {
+public:
+ DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+
+ ~DispSyncSource() override = default;
+
+ // The following methods are implementation of VSyncSource.
+ void setVSyncEnabled(bool enable) override;
+ void setCallback(VSyncSource::Callback* callback) override;
+ void setPhaseOffset(nsecs_t phaseOffset) override;
+ void pauseVsyncCallback(bool pause) override;
+
+private:
+ // The following method is the implementation of the DispSync::Callback.
+ virtual void onDispSyncEvent(nsecs_t when);
+
+ const char* const mName;
+ int mValue = 0;
+
+ const bool mTraceVsync;
+ const std::string mVsyncOnLabel;
+ const std::string mVsyncEventLabel;
+ nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
+
+ DispSync* mDispSync;
+
+ std::mutex mCallbackMutex;
+ VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
+
+ std::mutex mVsyncMutex;
+ nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
+ bool mEnabled GUARDED_BY(mVsyncMutex) = false;
+ bool mCallbackPaused GUARDED_BY(mVsyncMutex) = false;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
similarity index 100%
rename from services/surfaceflinger/EventControlThread.cpp
rename to services/surfaceflinger/Scheduler/EventControlThread.cpp
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h
similarity index 100%
rename from services/surfaceflinger/EventControlThread.h
rename to services/surfaceflinger/Scheduler/EventControlThread.h
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
new file mode 100644
index 0000000..78bf7c5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -0,0 +1,501 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <cstdint>
+#include <optional>
+#include <type_traits>
+
+#include <android-base/stringprintf.h>
+
+#include <cutils/compiler.h>
+#include <cutils/sched_policy.h>
+
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+#include <utils/Trace.h>
+
+#include "EventThread.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using base::StringAppendF;
+using base::StringPrintf;
+
+namespace {
+
+auto vsyncPeriod(VSyncRequest request) {
+ return static_cast<std::underlying_type_t<VSyncRequest>>(request);
+}
+
+std::string toString(VSyncRequest request) {
+ switch (request) {
+ case VSyncRequest::None:
+ return "VSyncRequest::None";
+ case VSyncRequest::Single:
+ return "VSyncRequest::Single";
+ default:
+ return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
+ }
+}
+
+std::string toString(const EventThreadConnection& connection) {
+ return StringPrintf("Connection{%p, %s}", &connection,
+ toString(connection.vsyncRequest).c_str());
+}
+
+std::string toString(const DisplayEventReceiver::Event& event) {
+ switch (event.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ return StringPrintf("Hotplug{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", %s}",
+ event.header.displayId,
+ event.hotplug.connected ? "connected" : "disconnected");
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", count=%u}",
+ event.header.displayId, event.vsync.count);
+ default:
+ return "Event{}";
+ }
+}
+
+DisplayEventReceiver::Event makeHotplug(PhysicalDisplayId displayId, nsecs_t timestamp,
+ bool connected) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+ event.hotplug.connected = connected;
+ return event;
+}
+
+DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
+ uint32_t count) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+ event.vsync.count = count;
+ return event;
+}
+
+DisplayEventReceiver::Event makeConfigChanged(uint32_t displayId, int32_t configId) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
+ event.config.configId = configId;
+ return event;
+}
+
+} // namespace
+
+EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+ ResyncCallback resyncCallback,
+ ResetIdleTimerCallback resetIdleTimerCallback)
+ : resyncCallback(std::move(resyncCallback)),
+ resetIdleTimerCallback(std::move(resetIdleTimerCallback)),
+ mEventThread(eventThread),
+ mChannel(gui::BitTube::DefaultSize) {}
+
+EventThreadConnection::~EventThreadConnection() {
+ // do nothing here -- clean-up will happen automatically
+ // when the main thread wakes up
+}
+
+void EventThreadConnection::onFirstRef() {
+ // NOTE: mEventThread doesn't hold a strong reference on us
+ mEventThread->registerDisplayEventConnection(this);
+}
+
+status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
+ outChannel->setReceiveFd(mChannel.moveReceiveFd());
+ return NO_ERROR;
+}
+
+status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
+ mEventThread->setVsyncRate(rate, this);
+ return NO_ERROR;
+}
+
+void EventThreadConnection::requestNextVsync() {
+ ATRACE_NAME("requestNextVsync");
+ mEventThread->requestNextVsync(this, true);
+}
+
+void EventThreadConnection::requestNextVsyncForHWC() {
+ ATRACE_NAME("requestNextVsyncForHWC");
+ mEventThread->requestNextVsync(this, false);
+}
+
+status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
+ ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
+ return size < 0 ? status_t(size) : status_t(NO_ERROR);
+}
+
+// ---------------------------------------------------------------------------
+
+EventThread::~EventThread() = default;
+
+namespace impl {
+
+EventThread::EventThread(std::unique_ptr<VSyncSource> src,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+ : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {}
+
+EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
+ const char* threadName)
+ : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {}
+
+EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+ : mVSyncSource(src),
+ mVSyncSourceUnique(std::move(uniqueSrc)),
+ mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+ mThreadName(threadName) {
+ if (src == nullptr) {
+ mVSyncSource = mVSyncSourceUnique.get();
+ }
+ mVSyncSource->setCallback(this);
+
+ mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mMutex);
+ threadMain(lock);
+ });
+
+ pthread_setname_np(mThread.native_handle(), threadName);
+
+ pid_t tid = pthread_gettid_np(mThread.native_handle());
+
+ // Use SCHED_FIFO to minimize jitter
+ constexpr int EVENT_THREAD_PRIORITY = 2;
+ struct sched_param param = {0};
+ param.sched_priority = EVENT_THREAD_PRIORITY;
+ if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO for EventThread");
+ }
+
+ set_sched_policy(tid, SP_FOREGROUND);
+}
+
+EventThread::~EventThread() {
+ mVSyncSource->setCallback(nullptr);
+
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mState = State::Quit;
+ mCondition.notify_all();
+ }
+ mThread.join();
+}
+
+void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVSyncSource->setPhaseOffset(phaseOffset);
+}
+
+void EventThread::pauseVsyncCallback(bool pause) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ ATRACE_INT("vsyncPaused", pause);
+ mVSyncSource->pauseVsyncCallback(pause);
+}
+
+sp<EventThreadConnection> EventThread::createEventConnection(
+ ResyncCallback resyncCallback, ResetIdleTimerCallback resetIdleTimerCallback) const {
+ return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
+ std::move(resetIdleTimerCallback));
+}
+
+status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // this should never happen
+ auto it = std::find(mDisplayEventConnections.cbegin(),
+ mDisplayEventConnections.cend(), connection);
+ if (it != mDisplayEventConnections.cend()) {
+ ALOGW("DisplayEventConnection %p already exists", connection.get());
+ mCondition.notify_all();
+ return ALREADY_EXISTS;
+ }
+
+ mDisplayEventConnections.push_back(connection);
+ mCondition.notify_all();
+ return NO_ERROR;
+}
+
+void EventThread::removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection) {
+ auto it = std::find(mDisplayEventConnections.cbegin(),
+ mDisplayEventConnections.cend(), connection);
+ if (it != mDisplayEventConnections.cend()) {
+ mDisplayEventConnections.erase(it);
+ }
+}
+
+void EventThread::setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) {
+ if (static_cast<std::underlying_type_t<VSyncRequest>>(rate) < 0) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
+ if (connection->vsyncRequest != request) {
+ connection->vsyncRequest = request;
+ mCondition.notify_all();
+ }
+}
+
+void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection, bool reset) {
+ if (connection->resetIdleTimerCallback && reset) {
+ ATRACE_NAME("resetIdleTimer");
+ connection->resetIdleTimerCallback();
+ }
+
+ if (connection->resyncCallback) {
+ connection->resyncCallback();
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (connection->vsyncRequest == VSyncRequest::None) {
+ connection->vsyncRequest = VSyncRequest::Single;
+ mCondition.notify_all();
+ }
+}
+
+void EventThread::onScreenReleased() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mVSyncState || mVSyncState->synthetic) {
+ return;
+ }
+
+ mVSyncState->synthetic = true;
+ mCondition.notify_all();
+}
+
+void EventThread::onScreenAcquired() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mVSyncState || !mVSyncState->synthetic) {
+ return;
+ }
+
+ mVSyncState->synthetic = false;
+ mCondition.notify_all();
+}
+
+void EventThread::onVSyncEvent(nsecs_t timestamp) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ LOG_FATAL_IF(!mVSyncState);
+ mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
+ mCondition.notify_all();
+}
+
+void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected));
+ mCondition.notify_all();
+}
+
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeConfigChanged(displayId, configId));
+ mCondition.notify_all();
+}
+
+void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
+ DisplayEventConsumers consumers;
+
+ while (mState != State::Quit) {
+ std::optional<DisplayEventReceiver::Event> event;
+
+ // Determine next event to dispatch.
+ if (!mPendingEvents.empty()) {
+ event = mPendingEvents.front();
+ mPendingEvents.pop_front();
+
+ switch (event->header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ if (event->hotplug.connected && !mVSyncState) {
+ mVSyncState.emplace(event->header.displayId);
+ } else if (!event->hotplug.connected && mVSyncState &&
+ mVSyncState->displayId == event->header.displayId) {
+ mVSyncState.reset();
+ }
+ break;
+
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ if (mInterceptVSyncsCallback) {
+ mInterceptVSyncsCallback(event->header.timestamp);
+ }
+ break;
+ }
+ }
+
+ bool vsyncRequested = false;
+
+ // Find connections that should consume this event.
+ auto it = mDisplayEventConnections.begin();
+ while (it != mDisplayEventConnections.end()) {
+ if (const auto connection = it->promote()) {
+ vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
+
+ if (event && shouldConsumeEvent(*event, connection)) {
+ consumers.push_back(connection);
+ }
+
+ ++it;
+ } else {
+ it = mDisplayEventConnections.erase(it);
+ }
+ }
+
+ if (!consumers.empty()) {
+ dispatchEvent(*event, consumers);
+ consumers.clear();
+ }
+
+ State nextState;
+ if (mVSyncState && vsyncRequested) {
+ nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+ } else {
+ ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
+ nextState = State::Idle;
+ }
+
+ if (mState != nextState) {
+ if (mState == State::VSync) {
+ mVSyncSource->setVSyncEnabled(false);
+ } else if (nextState == State::VSync) {
+ mVSyncSource->setVSyncEnabled(true);
+ }
+
+ mState = nextState;
+ }
+
+ if (event) {
+ continue;
+ }
+
+ // Wait for event or client registration/request.
+ if (mState == State::Idle) {
+ mCondition.wait(lock);
+ } else {
+ // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
+ // display is off, keep feeding clients at 60 Hz.
+ const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
+ if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
+ ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+
+ LOG_FATAL_IF(!mVSyncState);
+ mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
+ systemTime(SYSTEM_TIME_MONOTONIC),
+ ++mVSyncState->count));
+ }
+ }
+ }
+}
+
+bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+ const sp<EventThreadConnection>& connection) const {
+ switch (event.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ return true;
+
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ switch (connection->vsyncRequest) {
+ case VSyncRequest::None:
+ return false;
+ case VSyncRequest::Single:
+ connection->vsyncRequest = VSyncRequest::None;
+ return true;
+ case VSyncRequest::Periodic:
+ return true;
+ default:
+ return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
+ }
+
+ default:
+ return false;
+ }
+}
+
+void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
+ const DisplayEventConsumers& consumers) {
+ for (const auto& consumer : consumers) {
+ switch (consumer->postEvent(event)) {
+ case NO_ERROR:
+ break;
+
+ case -EAGAIN:
+ // TODO: Try again if pipe is full.
+ ALOGW("Failed dispatching %s for %s", toString(event).c_str(),
+ toString(*consumer).c_str());
+ break;
+
+ default:
+ // Treat EPIPE and other errors as fatal.
+ removeDisplayEventConnectionLocked(consumer);
+ }
+ }
+}
+
+void EventThread::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
+ if (mVSyncState) {
+ StringAppendF(&result, "{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u%s}\n",
+ mVSyncState->displayId, mVSyncState->count,
+ mVSyncState->synthetic ? ", synthetic" : "");
+ } else {
+ StringAppendF(&result, "none\n");
+ }
+
+ StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size());
+ for (const auto& event : mPendingEvents) {
+ StringAppendF(&result, " %s\n", toString(event).c_str());
+ }
+
+ StringAppendF(&result, " connections (count=%zu):\n", mDisplayEventConnections.size());
+ for (const auto& ptr : mDisplayEventConnections) {
+ if (const auto connection = ptr.promote()) {
+ StringAppendF(&result, " %s\n", toString(*connection).c_str());
+ }
+ }
+}
+
+const char* EventThread::toCString(State state) {
+ switch (state) {
+ case State::Idle:
+ return "Idle";
+ case State::Quit:
+ return "Quit";
+ case State::SyntheticVSync:
+ return "SyntheticVSync";
+ case State::VSync:
+ return "VSync";
+ }
+}
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
new file mode 100644
index 0000000..67e6de9
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <cstdint>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
+
+#include <utils/Errors.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class EventThread;
+class EventThreadTest;
+class SurfaceFlinger;
+
+// ---------------------------------------------------------------------------
+
+using ResyncCallback = std::function<void()>;
+using ResetIdleTimerCallback = std::function<void()>;
+
+enum class VSyncRequest {
+ None = -1,
+ Single = 0,
+ Periodic = 1,
+ // Subsequent values are periods.
+};
+
+class VSyncSource {
+public:
+ class Callback {
+ public:
+ virtual ~Callback() {}
+ virtual void onVSyncEvent(nsecs_t when) = 0;
+ };
+
+ virtual ~VSyncSource() {}
+ virtual void setVSyncEnabled(bool enable) = 0;
+ virtual void setCallback(Callback* callback) = 0;
+ virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+ // pause/resume vsync callback generation
+ virtual void pauseVsyncCallback(bool pause) = 0;
+};
+
+class EventThreadConnection : public BnDisplayEventConnection {
+public:
+ EventThreadConnection(EventThread*, ResyncCallback, ResetIdleTimerCallback);
+ virtual ~EventThreadConnection();
+
+ virtual status_t postEvent(const DisplayEventReceiver::Event& event);
+
+ status_t stealReceiveChannel(gui::BitTube* outChannel) override;
+ status_t setVsyncRate(uint32_t rate) override;
+ void requestNextVsync() override; // asynchronous
+ // Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
+ // in order to update the configs.
+ void requestNextVsyncForHWC();
+
+ // Called in response to requestNextVsync.
+ const ResyncCallback resyncCallback;
+ const ResetIdleTimerCallback resetIdleTimerCallback;
+
+ VSyncRequest vsyncRequest = VSyncRequest::None;
+
+private:
+ virtual void onFirstRef();
+ EventThread* const mEventThread;
+ gui::BitTube mChannel;
+};
+
+class EventThread {
+public:
+ virtual ~EventThread();
+
+ virtual sp<EventThreadConnection> createEventConnection(ResyncCallback,
+ ResetIdleTimerCallback) const = 0;
+
+ // called before the screen is turned off from main thread
+ virtual void onScreenReleased() = 0;
+
+ // called after the screen is turned on from main thread
+ virtual void onScreenAcquired() = 0;
+
+ virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
+
+ // called when SF changes the active config and apps needs to be notified about the change
+ virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+
+ virtual void dump(std::string& result) const = 0;
+
+ virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+ virtual status_t registerDisplayEventConnection(
+ const sp<EventThreadConnection>& connection) = 0;
+ 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,
+ bool resetIdleTimer) = 0;
+
+ virtual void pauseVsyncCallback(bool pause) = 0;
+};
+
+namespace impl {
+
+class EventThread : public android::EventThread, private VSyncSource::Callback {
+public:
+ using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+
+ // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
+ EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName);
+ EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
+ ~EventThread();
+
+ sp<EventThreadConnection> createEventConnection(ResyncCallback,
+ ResetIdleTimerCallback) const override;
+
+ status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
+ void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
+ void requestNextVsync(const sp<EventThreadConnection>& connection,
+ bool resetIdleTimer) override;
+
+ // called before the screen is turned off from main thread
+ void onScreenReleased() override;
+
+ // called after the screen is turned on from main thread
+ void onScreenAcquired() override;
+
+ void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
+
+ void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+
+ void dump(std::string& result) const override;
+
+ void setPhaseOffset(nsecs_t phaseOffset) override;
+
+ void pauseVsyncCallback(bool pause) override;
+
+private:
+ friend EventThreadTest;
+
+ using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
+
+ // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
+ EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+
+ void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
+
+ bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+ const sp<EventThreadConnection>& connection) const REQUIRES(mMutex);
+ void dispatchEvent(const DisplayEventReceiver::Event& event,
+ const DisplayEventConsumers& consumers) REQUIRES(mMutex);
+
+ void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection)
+ REQUIRES(mMutex);
+
+ // Implements VSyncSource::Callback
+ void onVSyncEvent(nsecs_t timestamp) override;
+
+ // TODO(b/128863962): Once the Scheduler is complete this pointer will become obsolete.
+ VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
+ std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
+
+ const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const char* const mThreadName;
+
+ std::thread mThread;
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mCondition;
+
+ std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
+ std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+
+ // VSYNC state of connected display.
+ struct VSyncState {
+ explicit VSyncState(PhysicalDisplayId displayId) : displayId(displayId) {}
+
+ const PhysicalDisplayId displayId;
+
+ // Number of VSYNC events since display was connected.
+ uint32_t count = 0;
+
+ // True if VSYNC should be faked, e.g. when display is off.
+ bool synthetic = false;
+ };
+
+ // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
+ // and support headless mode by injecting a fake display with synthetic VSYNC.
+ std::optional<VSyncState> mVSyncState GUARDED_BY(mMutex);
+
+ // State machine for event loop.
+ enum class State {
+ Idle,
+ Quit,
+ SyntheticVSync,
+ VSync,
+ };
+
+ State mState GUARDED_BY(mMutex) = State::Idle;
+
+ static const char* toCString(State);
+};
+
+// ---------------------------------------------------------------------------
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
new file mode 100644
index 0000000..b28b1aa
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2018 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 "IdleTimer.h"
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace scheduler {
+
+IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback)
+ : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
+
+IdleTimer::~IdleTimer() {
+ stop();
+}
+
+void IdleTimer::start() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mState = TimerState::RESET;
+ }
+ mThread = std::thread(&IdleTimer::loop, this);
+}
+
+void IdleTimer::stop() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mState = TimerState::STOPPED;
+ }
+ mCondition.notify_all();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void IdleTimer::loop() {
+ while (true) {
+ bool triggerReset = false;
+ bool triggerTimeout = false;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mState == TimerState::STOPPED) {
+ break;
+ }
+
+ if (mState == TimerState::IDLE) {
+ mCondition.wait(mMutex);
+ continue;
+ }
+
+ if (mState == TimerState::RESET) {
+ triggerReset = true;
+ }
+ }
+ if (triggerReset && mResetCallback) {
+ mResetCallback();
+ }
+
+ { // lock the mutex again. someone might have called stop meanwhile
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mState == TimerState::STOPPED) {
+ break;
+ }
+
+ auto triggerTime = std::chrono::steady_clock::now() + mInterval;
+ mState = TimerState::WAITING;
+ while (mState == TimerState::WAITING) {
+ constexpr auto zero = std::chrono::steady_clock::duration::zero();
+ auto waitTime = triggerTime - std::chrono::steady_clock::now();
+ if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
+ if (mState == TimerState::WAITING &&
+ (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+ triggerTimeout = true;
+ mState = TimerState::IDLE;
+ }
+ }
+ }
+ if (triggerTimeout && mTimeoutCallback) {
+ mTimeoutCallback();
+ }
+ }
+} // namespace scheduler
+
+void IdleTimer::reset() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mState = TimerState::RESET;
+ }
+ mCondition.notify_all();
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
new file mode 100644
index 0000000..19f1267
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 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 <chrono>
+#include <condition_variable>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace scheduler {
+
+/*
+ * Class that sets off a timer for a given interval, and fires a callback when the
+ * interval expires.
+ */
+class IdleTimer {
+public:
+ using Interval = std::chrono::milliseconds;
+ using ResetCallback = std::function<void()>;
+ using TimeoutCallback = std::function<void()>;
+
+ IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback);
+ ~IdleTimer();
+
+ void start();
+ void stop();
+ void reset();
+
+private:
+ // Enum to track in what state is the timer.
+ enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 };
+
+ // Function that loops until the condition for stopping is met.
+ void loop();
+
+ // Thread waiting for timer to expire.
+ std::thread mThread;
+
+ // Condition used to notify mThread.
+ std::condition_variable_any mCondition;
+
+ // Lock used for synchronizing the waiting thread with the application thread.
+ std::mutex mMutex;
+
+ TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
+
+ // Interval after which timer expires.
+ const Interval mInterval;
+
+ // Callback that happens when timer resets.
+ const ResetCallback mResetCallback;
+
+ // Callback that happens when timer expires.
+ const TimeoutCallback mTimeoutCallback;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
new file mode 100644
index 0000000..90609af
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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 <mutex>
+
+#include "EventThread.h"
+
+namespace android {
+
+/**
+ * VSync signals used during SurfaceFlinger trace playback (traces we captured
+ * with SurfaceInterceptor).
+ */
+class InjectVSyncSource final : public VSyncSource {
+public:
+ ~InjectVSyncSource() override = default;
+
+ void setCallback(VSyncSource::Callback* callback) override {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ mCallback = callback;
+ }
+
+ void onInjectSyncEvent(nsecs_t when) {
+ std::lock_guard<std::mutex> lock(mCallbackMutex);
+ if (mCallback) {
+ mCallback->onVSyncEvent(when);
+ }
+ }
+
+ void setVSyncEnabled(bool) override {}
+ void setPhaseOffset(nsecs_t) override {}
+ void pauseVsyncCallback(bool) {}
+
+private:
+ std::mutex mCallbackMutex;
+ VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
new file mode 100644
index 0000000..d5ccbe1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 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 "LayerHistory.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+
+LayerHistory::LayerHistory() {}
+
+LayerHistory::~LayerHistory() = default;
+
+void LayerHistory::insert(const std::string layerName, nsecs_t presentTime) {
+ mElements[mCounter].insert(std::make_pair(layerName, presentTime));
+}
+
+void LayerHistory::incrementCounter() {
+ mCounter++;
+ mCounter = mCounter % scheduler::ARRAY_SIZE;
+ // Clear all the previous data from the history. This is a ring buffer, so we are
+ // reusing memory.
+ mElements[mCounter].clear();
+}
+
+const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
+ // For the purposes of the layer history, the index = 0 always needs to start at the
+ // current counter, and then decrement to access the layers in correct historical order.
+ return mElements.at((scheduler::ARRAY_SIZE + (mCounter - (index % scheduler::ARRAY_SIZE))) %
+ scheduler::ARRAY_SIZE);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
new file mode 100644
index 0000000..c6fab07
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 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 <array>
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <utils/Timers.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+
+/*
+ * This class represents a circular buffer in which we keep layer history for
+ * the past ARRAY_SIZE frames. Each time, a signal for new frame comes, the counter
+ * gets incremented and includes all the layers that are requested to draw in that
+ * frame.
+ *
+ * Once the buffer reaches the end of the array, it starts overriding the elements
+ * at the beginning of the array.
+ */
+class LayerHistory {
+public:
+ LayerHistory();
+ ~LayerHistory();
+
+ // Method for inserting layers and their requested present time into the ring buffer.
+ // The elements are going to be inserted into an unordered_map at the position 'now'.
+ void insert(const std::string layerName, nsecs_t presentTime);
+ // Method for incrementing the current slot in the ring buffer. It also clears the
+ // unordered_map, if it was created previously.
+ void incrementCounter();
+ // Returns unordered_map at the given at index. The index is decremented from 'now'. For
+ // example, 0 is now, 1 is previous frame.
+ const std::unordered_map<std::string, nsecs_t>& get(size_t index) const;
+ // Returns the total size of the ring buffer. The value is always the same regardless
+ // of how many slots we filled in.
+ static constexpr size_t getSize() { return scheduler::ARRAY_SIZE; }
+
+private:
+ size_t mCounter = 0;
+ std::array<std::unordered_map<std::string, nsecs_t>, scheduler::ARRAY_SIZE> mElements;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
similarity index 88%
rename from services/surfaceflinger/MessageQueue.cpp
rename to services/surfaceflinger/Scheduler/MessageQueue.cpp
index 056d381..1f18ead 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -85,7 +85,8 @@
mHandler = new Handler(*this);
}
-void MessageQueue::setEventThread(android::EventThread* eventThread) {
+void MessageQueue::setEventThread(android::EventThread* eventThread,
+ ResyncCallback resyncCallback) {
if (mEventThread == eventThread) {
return;
}
@@ -95,7 +96,19 @@
}
mEventThread = eventThread;
- mEvents = eventThread->createEventConnection();
+ mEvents =
+ eventThread->createEventConnection(std::move(resyncCallback), ResetIdleTimerCallback());
+ mEvents->stealReceiveChannel(&mEventTube);
+ mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+ this);
+}
+
+void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
+ if (mEventTube.getFd() >= 0) {
+ mLooper->removeFd(mEventTube.getFd());
+ }
+
+ mEvents = connection;
mEvents->stealReceiveChannel(&mEventTube);
mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
this);
@@ -137,6 +150,10 @@
mEvents->requestNextVsync();
}
+void MessageQueue::invalidateForHWC() {
+ mEvents->requestNextVsyncForHWC();
+}
+
void MessageQueue::refresh() {
mHandler->dispatchRefresh();
}
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
similarity index 84%
rename from services/surfaceflinger/MessageQueue.h
rename to services/surfaceflinger/Scheduler/MessageQueue.h
index 90d1c72..245a8ac 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,12 +29,12 @@
#include <private/gui/BitTube.h>
#include "Barrier.h"
+#include "EventThread.h"
#include <functional>
namespace android {
-class EventThread;
class SurfaceFlinger;
// ---------------------------------------------------------------------------
@@ -85,10 +85,13 @@
virtual ~MessageQueue();
virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
- virtual void setEventThread(EventThread* events) = 0;
+ // TODO(b/128863962): Remove this function once everything is migrated to Scheduler.
+ virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 0;
+ virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
virtual void waitMessage() = 0;
virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
virtual void invalidate() = 0;
+ virtual void invalidateForHWC() = 0;
virtual void refresh() = 0;
};
@@ -114,7 +117,7 @@
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
android::EventThread* mEventThread;
- sp<IDisplayEventConnection> mEvents;
+ sp<EventThreadConnection> mEvents;
gui::BitTube mEventTube;
sp<Handler> mHandler;
@@ -124,13 +127,17 @@
public:
~MessageQueue() override = default;
void init(const sp<SurfaceFlinger>& flinger) override;
- void setEventThread(android::EventThread* events) override;
+ void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
+ void setEventConnection(const sp<EventThreadConnection>& connection) override;
void waitMessage() override;
status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override;
// sends INVALIDATE message at next VSYNC
void invalidate() override;
+
+ // sends INVALIDATE message at next VSYNC, without resetting the idle timer in the Scheduler
+ void invalidateForHWC();
// sends REFRESH message at next VSYNC
void refresh() override;
};
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
new file mode 100644
index 0000000..7e2b03d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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 "PhaseOffsets.h"
+
+#include <cutils/properties.h>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+using namespace android::sysprop;
+
+namespace scheduler {
+
+PhaseOffsets::~PhaseOffsets() = default;
+
+namespace impl {
+PhaseOffsets::PhaseOffsets() {
+ int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
+
+ int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.early_phase_offset_ns", value, "-1");
+ const int earlySfOffsetNs = atoi(value);
+
+ property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
+ const int earlyGlSfOffsetNs = atoi(value);
+
+ property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
+ const int earlyAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
+ const int earlyGlAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1");
+ const int highFpsEarlySfOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1");
+ const int highFpsEarlyGlSfOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1");
+ const int highFpsEarlyAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1");
+ const int highFpsEarlyGlAppOffsetNs = atoi(value);
+
+ // TODO(b/122905996): Define these in device.mk.
+ property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000");
+ const int highFpsLateAppOffsetNs = atoi(value);
+
+ property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
+ const int highFpsLateSfOffsetNs = atoi(value);
+
+ mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
+ : sfVsyncPhaseOffsetNs,
+ earlyAppOffsetNs != -1 ? earlyAppOffsetNs
+ : vsyncPhaseOffsetNs};
+ mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs
+ : sfVsyncPhaseOffsetNs,
+ earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs
+ : vsyncPhaseOffsetNs};
+ mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
+
+ mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
+ : highFpsLateSfOffsetNs,
+ highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
+ : highFpsLateAppOffsetNs};
+ mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
+ : highFpsLateSfOffsetNs,
+ highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
+ : highFpsLateAppOffsetNs};
+ mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
+ switch (mRefreshRateType) {
+ case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
+ return mHighRefreshRateOffsets;
+ default:
+ return mDefaultRefreshRateOffsets;
+ }
+}
+
+void PhaseOffsets::dump(std::string& result) const {
+ const auto [early, earlyGl, late] = getCurrentOffsets();
+ base::StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
+ "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n",
+ late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf);
+}
+
+nsecs_t PhaseOffsets::getCurrentAppOffset() {
+ return getCurrentOffsets().late.app;
+}
+
+nsecs_t PhaseOffsets::getCurrentSfOffset() {
+ return getCurrentOffsets().late.sf;
+}
+
+} // namespace impl
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
new file mode 100644
index 0000000..cbcaade
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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 <cinttypes>
+
+#include "RefreshRateConfigs.h"
+#include "VSyncModulator.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class encapsulates offsets for different refresh rates. Depending
+ * on what refresh rate we are using, and wheter we are composing in GL,
+ * different offsets will help us with latency. This class keeps track of
+ * which mode the device is on, and returns approprate offsets when needed.
+ */
+class PhaseOffsets {
+public:
+ struct Offsets {
+ VSyncModulator::Offsets early;
+ VSyncModulator::Offsets earlyGl;
+ VSyncModulator::Offsets late;
+ };
+
+ virtual ~PhaseOffsets();
+
+ virtual nsecs_t getCurrentAppOffset() = 0;
+ virtual nsecs_t getCurrentSfOffset() = 0;
+ virtual Offsets getCurrentOffsets() const = 0;
+ virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
+ virtual void dump(std::string& result) const = 0;
+};
+
+namespace impl {
+class PhaseOffsets : public scheduler::PhaseOffsets {
+public:
+ PhaseOffsets();
+
+ nsecs_t getCurrentAppOffset() override;
+ nsecs_t getCurrentSfOffset() override;
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ Offsets getCurrentOffsets() const override;
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override {
+ mRefreshRateType = refreshRateType;
+ }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+private:
+ Offsets getmDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
+ Offsets getmHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
+
+ std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
+ RefreshRateConfigs::RefreshRateType::DEFAULT;
+
+ Offsets mDefaultRefreshRateOffsets;
+ Offsets mHighRefreshRateOffsets;
+};
+} // namespace impl
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
new file mode 100644
index 0000000..cbcc031
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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 <algorithm>
+#include <numeric>
+
+#include "android-base/stringprintf.h"
+
+#include "DisplayHardware/HWComposer.h"
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * This class is used to encapsulate configuration for refresh rates. It holds information
+ * about available refresh rates on the device, and the mapping between the numbers and human
+ * readable names.
+ */
+class RefreshRateConfigs {
+public:
+ // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
+ // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+ // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
+ enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+
+ struct RefreshRate {
+ // This config ID corresponds to the position of the config in the vector that is stored
+ // on the device.
+ int configId;
+ // Human readable name of the refresh rate.
+ std::string name;
+ // Refresh rate in frames per second, rounded to the nearest integer.
+ uint32_t fps = 0;
+ };
+
+ // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+ // baking them in.
+ explicit RefreshRateConfigs(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ init(configs);
+ }
+ ~RefreshRateConfigs() = default;
+
+ const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
+ return mRefreshRates;
+ }
+ const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }
+
+private:
+ void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
+ mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+
+ if (configs.size() < 1) {
+ ALOGE("Device does not have valid configs. Config size is 0.");
+ return;
+ }
+
+ // Create a map between config index and vsync period. This is all the info we need
+ // from the configs.
+ std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
+ for (int i = 0; i < configs.size(); ++i) {
+ configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
+ }
+
+ std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
+ [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
+ return a.second > b.second;
+ });
+
+ // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
+ nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
+ if (vsyncPeriod != 0) {
+ const float fps = 1e9 / vsyncPeriod;
+ mRefreshRates.emplace(RefreshRateType::DEFAULT,
+ RefreshRate{configIdToVsyncPeriod[0].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)});
+ }
+
+ if (configs.size() < 2) {
+ return;
+ }
+
+ // When the configs are ordered by the resync rate. We assume that the second one is
+ // PERFORMANCE, eg. the higher rate.
+ vsyncPeriod = configIdToVsyncPeriod[1].second;
+ if (vsyncPeriod != 0) {
+ const float fps = 1e9 / vsyncPeriod;
+ mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
+ RefreshRate{configIdToVsyncPeriod[1].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)});
+ }
+ }
+
+ std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
new file mode 100644
index 0000000..2491081
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 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 <numeric>
+
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/SchedulerUtils.h"
+#include "TimeStats/TimeStats.h"
+
+#include "android-base/stringprintf.h"
+#include "utils/Timers.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * Class to encapsulate statistics about refresh rates that the display is using. When the power
+ * mode is set to HWC_POWER_MODE_NORMAL, SF is switching between refresh rates that are stored in
+ * the device's configs. Otherwise, we assume the HWC is running in power saving mode under the
+ * hood (eg. the device is in DOZE, or screen off mode).
+ */
+class RefreshRateStats {
+ static constexpr int64_t MS_PER_S = 1000;
+ static constexpr int64_t MS_PER_MIN = 60 * MS_PER_S;
+ static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN;
+ static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
+
+public:
+ explicit RefreshRateStats(const std::shared_ptr<RefreshRateConfigs>& refreshRateConfigs,
+ const std::shared_ptr<TimeStats>& timeStats)
+ : mRefreshRateConfigs(refreshRateConfigs),
+ mTimeStats(timeStats),
+ mPreviousRecordedTime(systemTime()) {}
+ ~RefreshRateStats() = default;
+
+ // Sets power mode. We only collect the information when the power mode is not
+ // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
+ // on config mode.
+ void setPowerMode(int mode) {
+ if (mCurrentPowerMode == mode) {
+ return;
+ }
+ // If power mode is normal, the time is going to be recorded under config modes.
+ if (mode == HWC_POWER_MODE_NORMAL) {
+ mCurrentPowerMode = mode;
+ return;
+ }
+ flushTime();
+ mCurrentPowerMode = mode;
+ }
+
+ // Sets config mode. If the mode has changed, it records how much time was spent in the previous
+ // mode.
+ void setConfigMode(int mode) {
+ if (mCurrentConfigMode == mode) {
+ return;
+ }
+ flushTime();
+ mCurrentConfigMode = mode;
+ }
+
+ // Returns a map between human readable refresh rate and number of seconds the device spent in
+ // that mode.
+ std::unordered_map<std::string, int64_t> getTotalTimes() {
+ // If the power mode is on, then we are probably switching between the config modes. If
+ // it's not then the screen is probably off. Make sure to flush times before printing
+ // them.
+ flushTime();
+
+ std::unordered_map<std::string, int64_t> totalTime;
+ for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
+ int64_t totalTimeForConfig = 0;
+ if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
+ totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+ }
+ totalTime[config.name] = totalTimeForConfig;
+ }
+ return totalTime;
+ }
+
+ // Traverses through the map of config modes and returns how long they've been running in easy
+ // to read format.
+ std::string doDump() {
+ std::ostringstream stream;
+ stream << "+ Refresh rate: running time in seconds\n";
+ for (auto stats : getTotalTimes()) {
+ stream << stats.first.c_str() << ": " << getDateFormatFromMs(stats.second) << "\n";
+ }
+ return stream.str();
+ }
+
+private:
+ void flushTime() {
+ // Normal power mode is counted under different config modes.
+ if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+ flushTimeForMode(mCurrentConfigMode);
+ } else {
+ flushTimeForMode(SCREEN_OFF_CONFIG_ID);
+ }
+ }
+
+ // Calculates the time that passed in ms between the last time we recorded time and the time
+ // this method was called.
+ void flushTimeForMode(int mode) {
+ nsecs_t currentTime = systemTime();
+ nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
+ int64_t timeElapsedMs = ns2ms(timeElapsed);
+ mPreviousRecordedTime = currentTime;
+
+ mConfigModesTotalTime[mode] += timeElapsedMs;
+ for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
+ if (config.configId == mode) {
+ mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+ }
+ }
+ }
+
+ // Formats the time in milliseconds into easy to read format.
+ static std::string getDateFormatFromMs(int64_t timeMs) {
+ auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+ auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
+ auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
+ auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
+ return base::StringPrintf("%" PRId64 "d%02" PRId64 ":%02" PRId64 ":%02" PRId64
+ ".%03" PRId64,
+ days, hours, mins, sec, secRemainderMs);
+ }
+
+ // Keeps information about refresh rate configs that device has.
+ std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+
+ // Aggregate refresh rate statistics for telemetry.
+ std::shared_ptr<TimeStats> mTimeStats;
+
+ int64_t mCurrentConfigMode = 0;
+ int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+
+ std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+
+ nsecs_t mPreviousRecordedTime;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
new file mode 100644
index 0000000..0063c8a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2018 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 "Scheduler.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <system/window.h>
+#include <ui/DisplayStatInfo.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include "DispSync.h"
+#include "DispSyncSource.h"
+#include "EventControlThread.h"
+#include "EventThread.h"
+#include "IdleTimer.h"
+#include "InjectVSyncSource.h"
+#include "SchedulerUtils.h"
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
+
+#define RETURN_VALUE_IF_INVALID(value) \
+ if (handle == nullptr || mConnections.count(handle->id) == 0) return value
+#define RETURN_IF_INVALID() \
+ if (handle == nullptr || mConnections.count(handle->id) == 0) return
+
+std::atomic<int64_t> Scheduler::sNextId = 0;
+
+Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function)
+ : mHasSyncFramework(running_without_sync_framework(true)),
+ mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
+ mPrimaryHWVsyncEnabled(false),
+ mHWVsyncAvailable(false) {
+ // Note: We create a local temporary with the real DispSync implementation
+ // type temporarily so we can initialize it with the configured values,
+ // before storing it for more generic use using the interface type.
+ auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
+ primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
+ mPrimaryDispSync = std::move(primaryDispSync);
+ mEventControlThread = std::make_unique<impl::EventControlThread>(function);
+
+ mSetIdleTimerMs = set_idle_timer_ms(0);
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.set_idle_timer_ms", value, "0");
+ int int_value = atoi(value);
+ if (int_value) {
+ mSetIdleTimerMs = atoi(value);
+ }
+
+ if (mSetIdleTimerMs > 0) {
+ mIdleTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
+ [this] { resetTimerCallback(); },
+ [this] { expiredTimerCallback(); });
+ mIdleTimer->start();
+ }
+}
+
+Scheduler::~Scheduler() {
+ // Ensure the IdleTimer thread is joined before we start destroying state.
+ mIdleTimer.reset();
+}
+
+sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
+ const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+ impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+ const int64_t id = sNextId++;
+ ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
+
+ std::unique_ptr<EventThread> eventThread =
+ makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
+ std::move(interceptCallback));
+
+ auto eventThreadConnection =
+ createConnectionInternal(eventThread.get(), std::move(resyncCallback));
+ mConnections.emplace(id,
+ std::make_unique<Connection>(new ConnectionHandle(id),
+ eventThreadConnection,
+ std::move(eventThread)));
+ return mConnections[id]->handle;
+}
+
+std::unique_ptr<EventThread> Scheduler::makeEventThread(
+ const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+ std::unique_ptr<VSyncSource> eventThreadSource =
+ std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+ return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
+ std::move(interceptCallback), connectionName);
+}
+
+sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
+ ResyncCallback&& resyncCallback) {
+ return eventThread->createEventConnection(std::move(resyncCallback),
+ [this] { resetIdleTimer(); });
+}
+
+sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
+ const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
+ RETURN_VALUE_IF_INVALID(nullptr);
+ return createConnectionInternal(mConnections[handle->id]->thread.get(),
+ std::move(resyncCallback));
+}
+
+EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
+ RETURN_VALUE_IF_INVALID(nullptr);
+ return mConnections[handle->id]->thread.get();
+}
+
+sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
+ RETURN_VALUE_IF_INVALID(nullptr);
+ return mConnections[handle->id]->eventConnection;
+}
+
+void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
+ PhysicalDisplayId displayId, bool connected) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->onHotplugReceived(displayId, connected);
+}
+
+void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->onScreenAcquired();
+}
+
+void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->onScreenReleased();
+}
+
+void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
+ int32_t configId) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->onConfigChanged(displayId, configId);
+}
+
+void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
+ RETURN_IF_INVALID();
+ mConnections.at(handle->id)->thread->dump(result);
+}
+
+void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
+}
+
+void Scheduler::pauseVsyncCallback(const android::sp<android::Scheduler::ConnectionHandle>& handle,
+ bool pause) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->pauseVsyncCallback(pause);
+}
+
+void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
+ stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
+ stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
+}
+
+void Scheduler::enableHardwareVsync() {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
+ mPrimaryDispSync->beginResync();
+ mEventControlThread->setVsyncEnabled(true);
+ mPrimaryHWVsyncEnabled = true;
+ }
+}
+
+void Scheduler::disableHardwareVsync(bool makeUnavailable) {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ if (mPrimaryHWVsyncEnabled) {
+ mEventControlThread->setVsyncEnabled(false);
+ mPrimaryDispSync->endResync();
+ mPrimaryHWVsyncEnabled = false;
+ }
+ if (makeUnavailable) {
+ mHWVsyncAvailable = false;
+ }
+}
+
+void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
+ {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ if (makeAvailable) {
+ mHWVsyncAvailable = makeAvailable;
+ } else if (!mHWVsyncAvailable) {
+ // Hardware vsync is not currently available, so abort the resync
+ // attempt for now
+ return;
+ }
+ }
+
+ if (period <= 0) {
+ return;
+ }
+
+ setVsyncPeriod(period);
+}
+
+ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
+ std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
+ return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
+ if (const auto vsync = ptr.lock()) {
+ vsync->resync(getVsyncPeriod);
+ }
+ };
+}
+
+void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
+ static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
+
+ const nsecs_t now = systemTime();
+ const nsecs_t last = lastResyncTime.exchange(now);
+
+ if (now - last > kIgnoreDelay) {
+ scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
+ }
+}
+
+void Scheduler::setRefreshSkipCount(int count) {
+ mPrimaryDispSync->setRefreshSkipCount(count);
+}
+
+void Scheduler::setVsyncPeriod(const nsecs_t period) {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ mPrimaryDispSync->reset();
+ mPrimaryDispSync->setPeriod(period);
+
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync->beginResync();
+ mEventControlThread->setVsyncEnabled(true);
+ mPrimaryHWVsyncEnabled = true;
+ }
+}
+
+void Scheduler::addResyncSample(const nsecs_t timestamp) {
+ bool needsHwVsync = false;
+ { // Scope for the lock
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ if (mPrimaryHWVsyncEnabled) {
+ needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp);
+ }
+ }
+
+ if (needsHwVsync) {
+ enableHardwareVsync();
+ } else {
+ disableHardwareVsync(false);
+ }
+}
+
+void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
+ if (mPrimaryDispSync->addPresentFence(fenceTime)) {
+ enableHardwareVsync();
+ } else {
+ disableHardwareVsync(false);
+ }
+}
+
+void Scheduler::setIgnorePresentFences(bool ignore) {
+ mPrimaryDispSync->setIgnorePresentFences(ignore);
+}
+
+nsecs_t Scheduler::expectedPresentTime() {
+ return mPrimaryDispSync->expectedPresentTime();
+}
+
+void Scheduler::dumpPrimaryDispSync(std::string& result) const {
+ mPrimaryDispSync->dump(result);
+}
+
+void Scheduler::addNativeWindowApi(int apiId) {
+ std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+ mWindowApiHistory[mApiHistoryCounter] = apiId;
+ mApiHistoryCounter++;
+ mApiHistoryCounter = mApiHistoryCounter % scheduler::ARRAY_SIZE;
+}
+
+void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
+ fn(*mPrimaryDispSync);
+}
+
+void Scheduler::updateFpsBasedOnNativeWindowApi() {
+ int mode;
+ {
+ std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+ mode = scheduler::calculate_mode(mWindowApiHistory);
+ }
+ ATRACE_INT("NativeWindowApiMode", mode);
+
+ if (mode == NATIVE_WINDOW_API_MEDIA) {
+ // TODO(b/127365162): These callback names are not accurate anymore. Update.
+ mediaChangeRefreshRate(MediaFeatureState::MEDIA_PLAYING);
+ ATRACE_INT("DetectedVideo", 1);
+ } else {
+ mediaChangeRefreshRate(MediaFeatureState::MEDIA_OFF);
+ ATRACE_INT("DetectedVideo", 0);
+ }
+}
+
+void Scheduler::setChangeRefreshRateCallback(
+ const ChangeRefreshRateCallback& changeRefreshRateCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mChangeRefreshRateCallback = changeRefreshRateCallback;
+}
+
+void Scheduler::updateFrameSkipping(const int64_t skipCount) {
+ ATRACE_INT("FrameSkipCount", skipCount);
+ if (mSkipCount != skipCount) {
+ // Only update DispSync if it hasn't been updated yet.
+ mPrimaryDispSync->setRefreshSkipCount(skipCount);
+ mSkipCount = skipCount;
+ }
+}
+
+void Scheduler::resetIdleTimer() {
+ if (mIdleTimer) {
+ mIdleTimer->reset();
+ }
+}
+
+void Scheduler::resetTimerCallback() {
+ // We do not notify the applications about config changes when idle timer is reset.
+ timerChangeRefreshRate(IdleTimerState::RESET);
+ ATRACE_INT("ExpiredIdleTimer", 0);
+}
+
+void Scheduler::expiredTimerCallback() {
+ // We do not notify the applications about config changes when idle timer expires.
+ timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ ATRACE_INT("ExpiredIdleTimer", 1);
+}
+
+std::string Scheduler::doDump() {
+ std::ostringstream stream;
+ stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
+ return stream.str();
+}
+
+void Scheduler::mediaChangeRefreshRate(MediaFeatureState mediaFeatureState) {
+ // Default playback for media is DEFAULT when media is on.
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ ConfigEvent configEvent = ConfigEvent::None;
+
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mCurrentMediaFeatureState = mediaFeatureState;
+ // Only switch to PERFORMANCE if idle timer was reset, when turning
+ // media off. If the timer is IDLE, stay at DEFAULT.
+ if (mediaFeatureState == MediaFeatureState::MEDIA_OFF &&
+ mCurrentIdleTimerState == IdleTimerState::RESET) {
+ refreshRateType = RefreshRateType::PERFORMANCE;
+ }
+ }
+ changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ ConfigEvent configEvent = ConfigEvent::None;
+
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mCurrentIdleTimerState = idleTimerState;
+ // Only switch to PERFOMANCE if the idle timer was reset, and media is
+ // not playing. Otherwise, stay at DEFAULT.
+ if (idleTimerState == IdleTimerState::RESET &&
+ mCurrentMediaFeatureState == MediaFeatureState::MEDIA_OFF) {
+ refreshRateType = RefreshRateType::PERFORMANCE;
+ }
+ }
+ changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mChangeRefreshRateCallback) {
+ mChangeRefreshRateCallback(refreshRateType, configEvent);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
new file mode 100644
index 0000000..73896d5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2018 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 <cstdint>
+#include <functional>
+#include <memory>
+
+#include <ui/DisplayStatInfo.h>
+#include <ui/GraphicTypes.h>
+
+#include "DispSync.h"
+#include "EventControlThread.h"
+#include "EventThread.h"
+#include "IdleTimer.h"
+#include "InjectVSyncSource.h"
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "SchedulerUtils.h"
+
+namespace android {
+
+class EventControlThread;
+
+class Scheduler {
+public:
+ // Enum to keep track of whether we trigger event to notify choreographer of config changes.
+ enum class ConfigEvent { None, Changed };
+
+ // logical or operator with the semantics of at least one of the events is Changed
+ friend ConfigEvent operator|(const ConfigEvent& first, const ConfigEvent& second) {
+ if (first == ConfigEvent::Changed) return ConfigEvent::Changed;
+ if (second == ConfigEvent::Changed) return ConfigEvent::Changed;
+ return ConfigEvent::None;
+ }
+
+ using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
+ using GetVsyncPeriod = std::function<nsecs_t()>;
+
+ // Enum to indicate whether to start the transaction early, or at vsync time.
+ enum class TransactionStart { EARLY, NORMAL };
+
+ /* The scheduler handle is a BBinder object passed to the client from which we can extract
+ * an ID for subsequent operations.
+ */
+ class ConnectionHandle : public BBinder {
+ public:
+ ConnectionHandle(int64_t id) : id(id) {}
+
+ ~ConnectionHandle() = default;
+
+ const int64_t id;
+ };
+
+ class Connection {
+ public:
+ Connection(sp<ConnectionHandle> handle, sp<EventThreadConnection> eventConnection,
+ std::unique_ptr<EventThread> eventThread)
+ : handle(handle), eventConnection(eventConnection), thread(std::move(eventThread)) {}
+
+ ~Connection() = default;
+
+ sp<ConnectionHandle> handle;
+ sp<EventThreadConnection> eventConnection;
+ const std::unique_ptr<EventThread> thread;
+ };
+
+ // Stores per-display state about VSYNC.
+ struct VsyncState {
+ explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {}
+
+ void resync(const GetVsyncPeriod&);
+
+ Scheduler& scheduler;
+ std::atomic<nsecs_t> lastResyncTime = 0;
+ };
+
+ explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function);
+
+ virtual ~Scheduler();
+
+ /** Creates an EventThread connection. */
+ sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs,
+ ResyncCallback,
+ impl::EventThread::InterceptVSyncsCallback);
+
+ sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
+ ResyncCallback);
+
+ // Getter methods.
+ EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+
+ // Provides access to the DispSync object for the primary display.
+ void withPrimaryDispSync(std::function<void(DispSync&)> const& fn);
+
+ sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
+
+ // Should be called when receiving a hotplug event.
+ void hotplugReceived(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
+ bool connected);
+
+ // Should be called after the screen is turned on.
+ void onScreenAcquired(const sp<ConnectionHandle>& handle);
+
+ // Should be called before the screen is turned off.
+ void onScreenReleased(const sp<ConnectionHandle>& handle);
+
+ // Should be called when display config changed
+ void onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
+ int32_t configId);
+
+ // Should be called when dumpsys command is received.
+ void dump(const sp<ConnectionHandle>& handle, std::string& result) const;
+
+ // Offers ability to modify phase offset in the event thread.
+ void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset);
+
+ // pause/resume vsync callback generation to avoid sending vsync callbacks during config switch
+ void pauseVsyncCallback(const sp<ConnectionHandle>& handle, bool pause);
+
+ void getDisplayStatInfo(DisplayStatInfo* stats);
+
+ void enableHardwareVsync();
+ void disableHardwareVsync(bool makeUnavailable);
+ void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
+ // Creates a callback for resyncing.
+ ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod);
+ void setRefreshSkipCount(int count);
+ void addResyncSample(const nsecs_t timestamp);
+ void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+ void setIgnorePresentFences(bool ignore);
+ nsecs_t expectedPresentTime();
+ // apiId indicates the API (NATIVE_WINDOW_API_xxx) that queues the buffer.
+ // TODO(b/123956502): Remove this call with V1 go/content-fps-detection-in-scheduler.
+ void addNativeWindowApi(int apiId);
+ // Updates FPS based on the most occured request for Native Window API.
+ void updateFpsBasedOnNativeWindowApi();
+ // Callback that gets invoked when Scheduler wants to change the refresh rate.
+ void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+
+ // Returns whether idle timer is enabled or not
+ bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
+ // Returns relevant information about Scheduler for dumpsys purposes.
+ std::string doDump();
+
+ // calls DispSync::dump() on primary disp sync
+ void dumpPrimaryDispSync(std::string& result) const;
+
+protected:
+ virtual std::unique_ptr<EventThread> makeEventThread(
+ const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ impl::EventThread::InterceptVSyncsCallback interceptCallback);
+
+private:
+ friend class TestableScheduler;
+
+ // In order to make sure that the features don't override themselves, we need a state machine
+ // to keep track which feature requested the config change.
+ enum class MediaFeatureState { MEDIA_PLAYING, MEDIA_OFF };
+ enum class IdleTimerState { EXPIRED, RESET };
+
+ // Creates a connection on the given EventThread and forwards the given callbacks.
+ sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
+
+ nsecs_t calculateAverage() const;
+ void updateFrameSkipping(const int64_t skipCount);
+ // Function that resets the idle timer.
+ void resetIdleTimer();
+ // Function that is called when the timer resets.
+ void resetTimerCallback();
+ // Function that is called when the timer expires.
+ void expiredTimerCallback();
+ // Sets vsync period.
+ void setVsyncPeriod(const nsecs_t period);
+ // Media feature's function to change the refresh rate.
+ void mediaChangeRefreshRate(MediaFeatureState mediaFeatureState);
+ // Idle timer feature's function to change the refresh rate.
+ void timerChangeRefreshRate(IdleTimerState idleTimerState);
+ // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
+ void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
+
+ // If fences from sync Framework are supported.
+ const bool mHasSyncFramework;
+
+ // The offset in nanoseconds to use, when DispSync timestamps present fence
+ // signaling time.
+ nsecs_t mDispSyncPresentTimeOffset;
+
+ // Each connection has it's own ID. This variable keeps track of the count.
+ static std::atomic<int64_t> sNextId;
+
+ // Connections are stored in a map <connection ID, connection> for easy retrieval.
+ std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections;
+
+ std::mutex mHWVsyncLock;
+ bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock);
+ bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock);
+ const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+
+ std::unique_ptr<DispSync> mPrimaryDispSync;
+ std::unique_ptr<EventControlThread> mEventControlThread;
+
+ // TODO(b/113612090): The following set of variables needs to be revised. For now, this is
+ // a proof of concept. We turn on frame skipping if the difference between the timestamps
+ // is between 32 and 34ms. We expect this currently for 30fps videos, so we render them at 30Hz.
+ nsecs_t mPreviousFrameTimestamp = 0;
+ // Keeping track of whether we are skipping the refresh count. If we want to
+ // simulate 30Hz rendering, we skip every other frame, and this variable is set
+ // to 1.
+ int64_t mSkipCount = 0;
+ std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
+ size_t mCounter = 0;
+
+ // The following few fields follow native window api bits that come with buffers. If there are
+ // more buffers with NATIVE_WINDOW_API_MEDIA we render at 60Hz, otherwise we render at 90Hz.
+ // There is not dependency on timestamp for V0.
+ // TODO(b/123956502): Remove this when more robust logic for content fps detection is developed.
+ std::mutex mWindowApiHistoryLock;
+ std::array<int, scheduler::ARRAY_SIZE> mWindowApiHistory GUARDED_BY(mWindowApiHistoryLock);
+ int64_t mApiHistoryCounter = 0;
+
+ // Timer that records time between requests for next vsync. If the time is higher than a given
+ // interval, a callback is fired. Set this variable to >0 to use this feature.
+ int64_t mSetIdleTimerMs = 0;
+ std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
+
+ std::mutex mCallbackLock;
+ ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+
+ // In order to make sure that the features don't override themselves, we need a state machine
+ // to keep track which feature requested the config change.
+ std::mutex mFeatureStateLock;
+ MediaFeatureState mCurrentMediaFeatureState GUARDED_BY(mFeatureStateLock) =
+ MediaFeatureState::MEDIA_OFF;
+ IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
new file mode 100644
index 0000000..fb5414f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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 "SchedulerUtils.h"
+
+#include <cinttypes>
+#include <numeric>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+
+int64_t calculate_median(std::vector<int64_t>* v) {
+ if (!v || v->empty()) {
+ return 0;
+ }
+
+ size_t n = v->size() / 2;
+ nth_element(v->begin(), v->begin() + n, v->end());
+ return v->at(n);
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
new file mode 100644
index 0000000..9e6e8c7
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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 <cinttypes>
+#include <numeric>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+// This number is used to set the size of the arrays in scheduler that hold information
+// about layers.
+static constexpr size_t ARRAY_SIZE = 30;
+
+// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
+// the config is not visible to SF, and is completely maintained by HWC. However, we would
+// still like to keep track of time when the device is in this config.
+static constexpr int SCREEN_OFF_CONFIG_ID = -1;
+
+// Calculates the statistical mean (average) in the data structure (array, vector). The
+// function does not modify the contents of the array.
+template <typename T>
+auto calculate_mean(const T& v) {
+ using V = typename T::value_type;
+ V sum = std::accumulate(v.begin(), v.end(), 0);
+ return sum / static_cast<V>(v.size());
+}
+
+// Calculates the statistical median in the vector. Return 0 if the vector is empty. The
+// function modifies the vector contents.
+int64_t calculate_median(std::vector<int64_t>* v);
+
+// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
+template <typename T>
+auto calculate_mode(const T& v) {
+ if (v.empty()) {
+ return 0;
+ }
+
+ // Create a map with all the counts for the indivicual values in the vector.
+ std::unordered_map<int64_t, int> counts;
+ for (int64_t value : v) {
+ counts[value]++;
+ }
+
+ // Sort the map, and return the number with the highest count. If two numbers have
+ // the same count, first one is returned.
+ using ValueType = const decltype(counts)::value_type&;
+ const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+ return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
+}
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
similarity index 64%
rename from services/surfaceflinger/VSyncModulator.h
rename to services/surfaceflinger/Scheduler/VSyncModulator.h
index e071a59..dab2003 100644
--- a/services/surfaceflinger/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -18,9 +18,10 @@
#include <utils/Errors.h>
+#include <cinttypes>
#include <mutex>
-using namespace android::surfaceflinger;
+#include "Scheduler.h"
namespace android {
@@ -29,23 +30,16 @@
*/
class VSyncModulator {
private:
-
// Number of frames we'll keep the early phase offsets once they are activated. This acts as a
// low-pass filter in case the client isn't quick enough in sending new transactions.
const int MIN_EARLY_FRAME_COUNT = 2;
public:
-
struct Offsets {
nsecs_t sf;
nsecs_t app;
};
- enum TransactionStart {
- EARLY,
- NORMAL
- };
-
// Sets the phase offsets
//
// sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
@@ -60,30 +54,43 @@
mEarlyOffsets = early;
mEarlyGlOffsets = earlyGl;
mLateOffsets = late;
+
+ if (mSfConnectionHandle && late.sf != mOffsets.load().sf) {
+ mScheduler->setPhaseOffset(mSfConnectionHandle, late.sf);
+ }
+
+ if (mAppConnectionHandle && late.app != mOffsets.load().app) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, late.app);
+ }
+
mOffsets = late;
}
- Offsets getEarlyOffsets() const {
- return mEarlyOffsets;
- }
+ Offsets getEarlyOffsets() const { return mEarlyOffsets; }
- Offsets getEarlyGlOffsets() const {
- return mEarlyGlOffsets;
- }
+ Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; }
void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
mSfEventThread = sfEventThread;
mAppEventThread = appEventThread;
}
- void setTransactionStart(TransactionStart transactionStart) {
+ void setSchedulerAndHandles(Scheduler* scheduler,
+ Scheduler::ConnectionHandle* appConnectionHandle,
+ Scheduler::ConnectionHandle* sfConnectionHandle) {
+ mScheduler = scheduler;
+ mAppConnectionHandle = appConnectionHandle;
+ mSfConnectionHandle = sfConnectionHandle;
+ }
- if (transactionStart == TransactionStart::EARLY) {
+ void setTransactionStart(Scheduler::TransactionStart transactionStart) {
+ if (transactionStart == Scheduler::TransactionStart::EARLY) {
mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT;
}
// An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) {
+ if (transactionStart == mTransactionStart ||
+ mTransactionStart == Scheduler::TransactionStart::EARLY) {
return;
}
mTransactionStart = transactionStart;
@@ -91,8 +98,8 @@
}
void onTransactionHandled() {
- if (mTransactionStart == TransactionStart::NORMAL) return;
- mTransactionStart = TransactionStart::NORMAL;
+ if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
+ mTransactionStart = Scheduler::TransactionStart::NORMAL;
updateOffsets();
}
@@ -112,18 +119,25 @@
}
private:
-
void updateOffsets() {
const Offsets desired = getOffsets();
const Offsets current = mOffsets;
bool changed = false;
if (desired.sf != current.sf) {
- mSfEventThread->setPhaseOffset(desired.sf);
+ if (mSfConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
+ } else {
+ mSfEventThread->setPhaseOffset(desired.sf);
+ }
changed = true;
}
if (desired.app != current.app) {
- mAppEventThread->setPhaseOffset(desired.app);
+ if (mAppConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
+ } else {
+ mAppEventThread->setPhaseOffset(desired.app);
+ }
changed = true;
}
@@ -133,7 +147,8 @@
}
Offsets getOffsets() {
- if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) {
+ if (mTransactionStart == Scheduler::TransactionStart::EARLY ||
+ mRemainingEarlyFrameCount > 0) {
return mEarlyOffsets;
} else if (mLastFrameUsedRenderEngine) {
return mEarlyGlOffsets;
@@ -149,9 +164,14 @@
EventThread* mSfEventThread = nullptr;
EventThread* mAppEventThread = nullptr;
+ Scheduler* mScheduler = nullptr;
+ Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
+ Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
+
std::atomic<Offsets> mOffsets;
- std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
+ std::atomic<Scheduler::TransactionStart> mTransactionStart =
+ Scheduler::TransactionStart::NORMAL;
std::atomic<bool> mLastFrameUsedRenderEngine = false;
std::atomic<int> mRemainingEarlyFrameCount = 0;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 78d751a..77c8d10 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,19 +14,21 @@
* limitations under the License.
*/
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <stdint.h>
#include <sys/types.h>
-#include <algorithm>
#include <errno.h>
-#include <math.h>
-#include <mutex>
#include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
#include <optional>
+#include <unordered_map>
#include <cutils/properties.h>
#include <log/log.h>
@@ -35,101 +37,161 @@
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <dvr/vr_flinger.h>
-
+#include <gui/BufferQueue.h>
+#include <gui/GuiConfig.h>
+#include <gui/IDisplayEventConnection.h>
+#include <gui/IProducerListener.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <input/IInputFlinger.h>
+#include <renderengine/RenderEngine.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayStatInfo.h>
-
-#include <gui/BufferQueue.h>
-#include <gui/GuiConfig.h>
-#include <gui/IDisplayEventConnection.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
#include <ui/UiConfig.h>
-
-#include <utils/misc.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <utils/StopWatch.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <utils/misc.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
#include "BufferLayer.h"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
#include "Client.h"
#include "ColorLayer.h"
#include "Colorizer.h"
#include "ContainerLayer.h"
-#include "DispSync.h"
#include "DisplayDevice.h"
-#include "EventControlThread.h"
-#include "EventThread.h"
#include "Layer.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
-#include "Transform.h"
-#include "clz.h"
+#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
+#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
-
#include "Effects/Daltonizer.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/DispSyncSource.h"
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/InjectVSyncSource.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
-#include "RenderEngine/RenderEngine.h"
#include <cutils/compiler.h>
+#include "android-base/stringprintf.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 <configstore/Utils.h>
#include <layerproto/LayerProtoParser.h>
-
-#define DISPLAY_COUNT 1
-
-/*
- * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all
- * black pixels.
- */
-#define DEBUG_SCREENSHOTS false
+#include "SurfaceFlingerProperties.h"
namespace android {
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
+
+using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
+using ui::DisplayPrimaries;
using ui::Hdr;
using ui::RenderIntent;
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
namespace {
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wswitch-enum"
-Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
+bool isWideColorMode(const ColorMode colorMode) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::ADOBE_RGB:
+ case ColorMode::DCI_P3:
+ case ColorMode::BT2020:
+ case ColorMode::DISPLAY_BT2020:
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ return true;
+ case ColorMode::NATIVE:
+ case ColorMode::STANDARD_BT601_625:
+ case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+ case ColorMode::STANDARD_BT601_525:
+ case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+ case ColorMode::STANDARD_BT709:
+ case ColorMode::SRGB:
+ return false;
+ }
+ return false;
+}
+
+bool isHdrColorMode(const ColorMode colorMode) {
+ switch (colorMode) {
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ return true;
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::ADOBE_RGB:
+ case ColorMode::DCI_P3:
+ case ColorMode::BT2020:
+ case ColorMode::DISPLAY_BT2020:
+ case ColorMode::NATIVE:
+ case ColorMode::STANDARD_BT601_625:
+ case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+ case ColorMode::STANDARD_BT601_525:
+ case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+ case ColorMode::STANDARD_BT709:
+ case ColorMode::SRGB:
+ return false;
+ }
+ return false;
+}
+
+ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
switch (rotation) {
case ISurfaceComposer::eRotateNone:
- return Transform::ROT_0;
+ return ui::Transform::ROT_0;
case ISurfaceComposer::eRotate90:
- return Transform::ROT_90;
+ return ui::Transform::ROT_90;
case ISurfaceComposer::eRotate180:
- return Transform::ROT_180;
+ return ui::Transform::ROT_180;
case ISurfaceComposer::eRotate270:
- return Transform::ROT_270;
+ return ui::Transform::ROT_270;
}
ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
- return Transform::ROT_0;
+ return ui::Transform::ROT_0;
}
#pragma clang diagnostic pop
@@ -146,6 +208,12 @@
Mutex& mMutex;
bool mLocked;
};
+
+// Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
+bool validateCompositionDataspace(Dataspace dataspace) {
+ return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -156,17 +224,20 @@
const String16 sDump("android.permission.DUMP");
// ---------------------------------------------------------------------------
-int64_t SurfaceFlinger::vsyncPhaseOffsetNs;
-int64_t SurfaceFlinger::sfVsyncPhaseOffsetNs;
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
uint64_t SurfaceFlinger::maxVirtualDisplaySize;
bool SurfaceFlinger::hasSyncFramework;
bool SurfaceFlinger::useVrFlinger;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
-// TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning.
bool SurfaceFlinger::hasWideColorDisplay;
int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
+bool SurfaceFlinger::useColorManagement;
+bool SurfaceFlinger::useContextPriority;
+Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
+ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
+Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
+ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
std::string getHwcServiceName() {
char value[PROPERTY_VALUE_MAX] = {};
@@ -196,51 +267,24 @@
}
}
-NativeWindowSurface::~NativeWindowSurface() = default;
-
-namespace impl {
-
-class NativeWindowSurface final : public android::NativeWindowSurface {
-public:
- static std::unique_ptr<android::NativeWindowSurface> create(
- const sp<IGraphicBufferProducer>& producer) {
- return std::make_unique<NativeWindowSurface>(producer);
- }
-
- explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer)
- : surface(new Surface(producer, false)) {}
-
- ~NativeWindowSurface() override = default;
-
-private:
- sp<ANativeWindow> getNativeWindow() const override { return surface; }
-
- void preallocateBuffers() override { surface->allocateBuffers(); }
-
- sp<Surface> surface;
-};
-
-} // namespace impl
-
SurfaceFlingerBE::SurfaceFlingerBE()
: mHwcServiceName(getHwcServiceName()),
- mRenderEngine(nullptr),
mFrameBuckets(),
mTotalTime(0),
mLastSwapTime(0),
mComposerSequenceId(0) {
}
-SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag)
+SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory,
+ SurfaceFlinger::SkipInitializationTag)
: BnSurfaceComposer(),
- mTransactionFlags(0),
+ mFactory(factory),
mTransactionPending(false),
mAnimTransactionPending(false),
mLayersRemoved(false),
mLayersAdded(false),
- mRepaintEverything(0),
mBootTime(systemTime()),
- mBuiltinDisplays(),
+ mPhaseOffsets{getFactory().createPhaseOffsets()},
mVisibleRegionsDirty(false),
mGeometryInvalid(false),
mAnimCompositionPending(false),
@@ -248,64 +292,65 @@
mDebugRegion(0),
mDebugDisableHWC(0),
mDebugDisableTransformHint(0),
- mDebugInSwapBuffers(0),
- mLastSwapBufferTime(0),
+ mDebugEnableProtectedContent(false),
mDebugInTransaction(0),
mLastTransactionTime(0),
mForceFullDamage(false),
- mPrimaryDispSync("PrimaryDispSync"),
- mPrimaryHWVsyncEnabled(false),
- mHWVsyncAvailable(false),
+ mTracing(*this),
+ mTimeStats(factory.createTimeStats()),
+ mRefreshStartTime(0),
mHasPoweredOff(false),
mNumLayers(0),
mVrFlingerRequestsDisplay(false),
mMainThreadId(std::this_thread::get_id()),
- mCreateBufferQueue(&BufferQueue::createBufferQueue),
- mCreateNativeWindowSurface(&impl::NativeWindowSurface::create) {}
+ mCompositionEngine{getFactory().createCompositionEngine()} {
+ mSetInputWindowsListener = new SetInputWindowsListener(this);
+}
-SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) {
+SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory)
+ : SurfaceFlinger(factory, SkipInitialization) {
ALOGI("SurfaceFlinger is starting");
- vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+ hasSyncFramework = running_without_sync_framework(true);
- sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+ dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);
- hasSyncFramework = getBool< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::hasSyncFramework>(true);
+ useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
- dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
-
- useHwcForRgbToYuv = getBool< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(false);
-
- maxVirtualDisplaySize = getUInt64<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(0);
+ maxVirtualDisplaySize = max_virtual_display_dimension(0);
// Vr flinger is only enabled on Daydream ready devices.
- useVrFlinger = getBool< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::useVrFlinger>(false);
+ useVrFlinger = use_vr_flinger(false);
- maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+ maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
- hasWideColorDisplay =
- getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+ hasWideColorDisplay = has_wide_color_display(false);
- V1_1::DisplayOrientation primaryDisplayOrientation =
- getDisplayOrientation< V1_1::ISurfaceFlingerConfigs, &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
- V1_1::DisplayOrientation::ORIENTATION_0);
+ useColorManagement = use_color_management(false);
- switch (primaryDisplayOrientation) {
- case V1_1::DisplayOrientation::ORIENTATION_90:
+ mDefaultCompositionDataspace =
+ static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
+ mWideColorGamutCompositionDataspace =
+ static_cast<ui::Dataspace>(wcg_composition_dataspace(Dataspace::V0_SRGB));
+ defaultCompositionDataspace = mDefaultCompositionDataspace;
+ wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
+ defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
+ default_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+ wideColorGamutCompositionPixelFormat =
+ static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+
+ useContextPriority = use_context_priority(true);
+
+ auto tmpPrimaryDisplayOrientation = primary_display_orientation(
+ SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0);
+ switch (tmpPrimaryDisplayOrientation) {
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90;
break;
- case V1_1::DisplayOrientation::ORIENTATION_180:
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
break;
- case V1_1::DisplayOrientation::ORIENTATION_270:
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
break;
default:
@@ -314,7 +359,7 @@
}
ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
- mPrimaryDispSync.init(SurfaceFlinger::hasSyncFramework, SurfaceFlinger::dispSyncPresentTimeOffset);
+ mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
// debugging stuff...
char value[PROPERTY_VALUE_MAX];
@@ -338,9 +383,9 @@
property_get("debug.sf.enable_hwc_vds", value, "0");
mUseHwcVirtualDisplays = atoi(value);
- ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
+ ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
- property_get("ro.sf.disable_triple_buffer", value, "1");
+ property_get("ro.sf.disable_triple_buffer", value, "0");
mLayerTripleBufferingDisabled = atoi(value);
ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
@@ -348,26 +393,19 @@
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
- property_get("debug.sf.early_phase_offset_ns", value, "-1");
- const int earlySfOffsetNs = atoi(value);
+ mUseSmart90ForVideo = use_smart_90_for_video(false);
+ property_get("debug.sf.use_smart_90_for_video", value, "0");
- property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
- const int earlyGlSfOffsetNs = atoi(value);
+ int int_value = atoi(value);
+ if (int_value) {
+ mUseSmart90ForVideo = true;
+ }
- property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
- const int earlyAppOffsetNs = atoi(value);
+ property_get("debug.sf.luma_sampling", value, "1");
+ mLumaSampling = atoi(value);
- property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
- const int earlyGlAppOffsetNs = atoi(value);
-
- const VSyncModulator::Offsets earlyOffsets =
- {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
- earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
- const VSyncModulator::Offsets earlyGlOffsets =
- {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
- earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
- mVsyncModulator.setPhaseOffsets(earlyOffsets, earlyGlOffsets,
- {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs});
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
@@ -416,19 +454,6 @@
return initClient(new Client(this));
}
-sp<ISurfaceComposerClient> SurfaceFlinger::createScopedConnection(
- const sp<IGraphicBufferProducer>& gbp) {
- if (authenticateSurfaceTexture(gbp) == false) {
- return nullptr;
- }
- const auto& layer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- if (layer == nullptr) {
- return nullptr;
- }
-
- return initClient(new Client(this, layer));
-}
-
sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
bool secure)
{
@@ -449,38 +474,78 @@
sp<BBinder> token = new DisplayToken(this);
Mutex::Autolock _l(mStateLock);
- DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
- info.displayName = displayName;
- mCurrentState.displays.add(token, info);
- mInterceptor->saveDisplayCreation(info);
+ // Display ID is assigned when virtual display is allocated by HWC.
+ DisplayDeviceState state;
+ state.isSecure = secure;
+ state.displayName = displayName;
+ mCurrentState.displays.add(token, state);
+ mInterceptor->saveDisplayCreation(state);
return token;
}
-void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) {
+void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) {
Mutex::Autolock _l(mStateLock);
- ssize_t idx = mCurrentState.displays.indexOfKey(display);
- if (idx < 0) {
- ALOGW("destroyDisplay: invalid display token");
+ ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+ if (index < 0) {
+ ALOGE("destroyDisplay: Invalid display token %p", displayToken.get());
return;
}
- const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
- if (!info.isVirtualDisplay()) {
+ const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+ if (!state.isVirtual()) {
ALOGE("destroyDisplay called for non-virtual display");
return;
}
- mInterceptor->saveDisplayDeletion(info.displayId);
- mCurrentState.displays.removeItemsAt(idx);
+ mInterceptor->saveDisplayDeletion(state.sequenceId);
+ mCurrentState.displays.removeItemsAt(index);
setTransactionFlags(eDisplayTransactionNeeded);
}
-sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
- if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
- return nullptr;
+std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const {
+ Mutex::Autolock lock(mStateLock);
+
+ const auto internalDisplayId = getInternalDisplayIdLocked();
+ if (!internalDisplayId) {
+ return {};
}
- return mBuiltinDisplays[id];
+
+ std::vector<PhysicalDisplayId> displayIds;
+ displayIds.reserve(mPhysicalDisplayTokens.size());
+ displayIds.push_back(internalDisplayId->value);
+
+ for (const auto& [id, token] : mPhysicalDisplayTokens) {
+ if (id != *internalDisplayId) {
+ displayIds.push_back(id.value);
+ }
+ }
+
+ return displayIds;
+}
+
+sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
+ Mutex::Autolock lock(mStateLock);
+ return getPhysicalDisplayTokenLocked(DisplayId{displayId});
+}
+
+status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
+ if (!outGetColorManagement) {
+ return BAD_VALUE;
+ }
+ *outGetColorManagement = useColorManagement;
+ return NO_ERROR;
+}
+
+HWComposer& SurfaceFlinger::getHwComposer() const {
+ return mCompositionEngine->getHwComposer();
+}
+
+renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const {
+ return mCompositionEngine->getRenderEngine();
+}
+
+compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const {
+ return *mCompositionEngine.get();
}
void SurfaceFlinger::bootFinished()
@@ -498,6 +563,13 @@
if (window != 0) {
window->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();
@@ -512,11 +584,23 @@
LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
- sp<LambdaMessage> readProperties = new LambdaMessage([&]() {
+ postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
readPersistentProperties();
mBootStage = BootStage::FINISHED;
- });
- postMessageAsync(readProperties);
+
+ // set the refresh rate according to the policy
+ const auto displayId = getInternalDisplayIdLocked();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
+ const auto performanceRefreshRate =
+ mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
+
+ if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
+ } else {
+ setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+ }
+ }));
}
uint32_t SurfaceFlinger::getNewTexture() {
@@ -541,268 +625,119 @@
}
void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
- class MessageDestroyGLTexture : public MessageBase {
- RE::RenderEngine& engine;
- uint32_t texture;
- public:
- MessageDestroyGLTexture(RE::RenderEngine& engine, uint32_t texture)
- : engine(engine), texture(texture) {}
- virtual bool handler() {
- engine.deleteTextures(1, &texture);
- return true;
- }
- };
- postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture));
+ postMessageAsync(new LambdaMessage([=] { getRenderEngine().deleteTextures(1, &texture); }));
}
-class DispSyncSource final : public VSyncSource, private DispSync::Callback {
-public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
- const char* name) :
- mName(name),
- mValue(0),
- mTraceVsync(traceVsync),
- mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
- mVsyncEventLabel(String8::format("VSYNC-%s", name)),
- mDispSync(dispSync),
- mCallbackMutex(),
- mVsyncMutex(),
- mPhaseOffset(phaseOffset),
- mEnabled(false) {}
-
- ~DispSyncSource() override = default;
-
- void setVSyncEnabled(bool enable) override {
- Mutex::Autolock lock(mVsyncMutex);
- if (enable) {
- status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error registering vsync callback: %s (%d)",
- strerror(-err), err);
- }
- //ATRACE_INT(mVsyncOnLabel.string(), 1);
- } else {
- status_t err = mDispSync->removeEventListener(
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error unregistering vsync callback: %s (%d)",
- strerror(-err), err);
- }
- //ATRACE_INT(mVsyncOnLabel.string(), 0);
- }
- mEnabled = enable;
- }
-
- void setCallback(VSyncSource::Callback* callback) override{
- Mutex::Autolock lock(mCallbackMutex);
- mCallback = callback;
- }
-
- void setPhaseOffset(nsecs_t phaseOffset) override {
- Mutex::Autolock lock(mVsyncMutex);
-
- // Normalize phaseOffset to [0, period)
- auto period = mDispSync->getPeriod();
- phaseOffset %= period;
- if (phaseOffset < 0) {
- // If we're here, then phaseOffset is in (-period, 0). After this
- // operation, it will be in (0, period)
- phaseOffset += period;
- }
- mPhaseOffset = phaseOffset;
-
- // If we're not enabled, we don't need to mess with the listeners
- if (!mEnabled) {
- return;
- }
-
- status_t err = mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this),
- mPhaseOffset);
- if (err != NO_ERROR) {
- ALOGE("error changing vsync offset: %s (%d)",
- strerror(-err), err);
- }
- }
-
-private:
- virtual void onDispSyncEvent(nsecs_t when) {
- VSyncSource::Callback* callback;
- {
- Mutex::Autolock lock(mCallbackMutex);
- callback = mCallback;
-
- if (mTraceVsync) {
- mValue = (mValue + 1) % 2;
- ATRACE_INT(mVsyncEventLabel.string(), mValue);
- }
- }
-
- if (callback != nullptr) {
- callback->onVSyncEvent(when);
- }
- }
-
- const char* const mName;
-
- int mValue;
-
- const bool mTraceVsync;
- const String8 mVsyncOnLabel;
- const String8 mVsyncEventLabel;
-
- DispSync* mDispSync;
-
- Mutex mCallbackMutex; // Protects the following
- VSyncSource::Callback* mCallback = nullptr;
-
- Mutex mVsyncMutex; // Protects the following
- nsecs_t mPhaseOffset;
- bool mEnabled;
-};
-
-class InjectVSyncSource final : public VSyncSource {
-public:
- InjectVSyncSource() = default;
- ~InjectVSyncSource() override = default;
-
- void setCallback(VSyncSource::Callback* callback) override {
- std::lock_guard<std::mutex> lock(mCallbackMutex);
- mCallback = callback;
- }
-
- void onInjectSyncEvent(nsecs_t when) {
- std::lock_guard<std::mutex> lock(mCallbackMutex);
- if (mCallback) {
- mCallback->onVSyncEvent(when);
- }
- }
-
- void setVSyncEnabled(bool) override {}
- void setPhaseOffset(nsecs_t) override {}
-
-private:
- std::mutex mCallbackMutex; // Protects the following
- VSyncSource::Callback* mCallback = nullptr;
-};
-
// Do not call property_set on main thread which will be blocked by init
// Use StartPropertySetThread instead.
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- ALOGI("Phase offest NS: %" PRId64 "", vsyncPhaseOffsetNs);
+ ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
Mutex::Autolock _l(mStateLock);
-
// start the EventThread
- mEventThreadSource =
- std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
- true, "app");
- mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
- [this]() { resyncWithRateLimit(); },
- impl::EventThread::InterceptVSyncsCallback(),
- "appEventThread");
- mSfEventThreadSource =
- std::make_unique<DispSyncSource>(&mPrimaryDispSync,
- SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+ mScheduler =
+ getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+ auto resyncCallback =
+ mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
- mSFEventThread =
- std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
- [this]() { resyncWithRateLimit(); },
- [this](nsecs_t timestamp) {
- mInterceptor->saveVSyncEvent(timestamp);
- },
- "sfEventThread");
- mEventQueue->setEventThread(mSFEventThread.get());
- mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());
+ mAppConnectionHandle =
+ mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
+ resyncCallback,
+ impl::EventThread::InterceptVSyncsCallback());
+ mSfConnectionHandle = mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
+ resyncCallback, [this](nsecs_t timestamp) {
+ mInterceptor->saveVSyncEvent(timestamp);
+ });
+
+ mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+ mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
+ mSfConnectionHandle.get());
+
+ mRegionSamplingThread =
+ new RegionSamplingThread(*this, *mScheduler,
+ RegionSamplingThread::EnvironmentTimingTunables());
// Get a RenderEngine for the given display / config (can't fail)
- getBE().mRenderEngine =
- RE::impl::RenderEngine::create(HAL_PIXEL_FORMAT_RGBA_8888,
- hasWideColorDisplay
- ? RE::RenderEngine::WIDE_COLOR_SUPPORT
- : 0);
- LOG_ALWAYS_FATAL_IF(getBE().mRenderEngine == nullptr, "couldn't create RenderEngine");
+ int32_t renderEngineFeature = 0;
+ renderEngineFeature |= (useColorManagement ?
+ renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0);
+ renderEngineFeature |= (useContextPriority ?
+ renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0);
+
+ // TODO(b/77156734): We need to stop casting and use HAL types when possible.
+ // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
+ mCompositionEngine->setRenderEngine(
+ renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat),
+ renderEngineFeature, maxFrameBufferAcquiredBuffers));
LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
"Starting with vr flinger active is not currently supported.");
- getBE().mHwc.reset(
- new HWComposer(std::make_unique<Hwc2::impl::Composer>(getBE().mHwcServiceName)));
- getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);
+ mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
+ mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();
- LOG_ALWAYS_FATAL_IF(!getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY),
- "Registered composer callback but didn't create the default primary display");
-
- // make the default display GLContext current so that we can create textures
- // when creating Layers (which may happens before we render something)
- getDefaultDisplayDeviceLocked()->makeCurrent();
+ const auto display = getDefaultDisplayDeviceLocked();
+ LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
+ LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
+ "Internal display is disconnected.");
if (useVrFlinger) {
- auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
+ auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) {
// This callback is called from the vr flinger dispatch thread. We
// need to call signalTransaction(), which requires holding
// mStateLock when we're not on the main thread. Acquiring
// mStateLock from the vr flinger dispatch thread might trigger a
// deadlock in surface flinger (see b/66916578), so post a message
// to be handled on the main thread instead.
- sp<LambdaMessage> message = new LambdaMessage([=]() {
+ postMessageAsync(new LambdaMessage([=] {
ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
mVrFlingerRequestsDisplay = requestDisplay;
signalTransaction();
- });
- postMessageAsync(message);
+ }));
};
- mVrFlinger = dvr::VrFlinger::Create(getBE().mHwc->getComposer(),
- getBE().mHwc->getHwcDisplayId(HWC_DISPLAY_PRIMARY).value_or(0),
- vrFlingerRequestDisplayCallback);
+ mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(),
+ getHwComposer()
+ .fromPhysicalDisplayId(*display->getId())
+ .value_or(0),
+ vrFlingerRequestDisplayCallback);
if (!mVrFlinger) {
ALOGE("Failed to start vrflinger");
}
}
- mEventControlThread = std::make_unique<impl::EventControlThread>(
- [this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); });
-
// initialize our drawing state
mDrawingState = mCurrentState;
// set initial conditions (e.g. unblank default device)
initializeDisplays();
- getBE().mRenderEngine->primeCache();
+ getRenderEngine().primeCache();
// Inform native graphics APIs whether the present timestamp is supported:
- if (getHwComposer().hasCapability(
- HWC2::Capability::PresentFenceIsNotReliable)) {
- mStartPropertySetThread = new StartPropertySetThread(false);
- } else {
- mStartPropertySetThread = new StartPropertySetThread(true);
- }
+
+ const bool presentFenceReliable =
+ !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable);
+ mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
- // This is a hack. Per definition of getDataspaceSaturationMatrix, the returned matrix
- // is used to saturate legacy sRGB content. However, to make sure the same color under
- // Display P3 will be saturated to the same color, we intentionally break the API spec
- // and apply this saturation matrix on Display P3 content. Unless the risk of applying
- // such saturation matrix on Display P3 is understood fully, the API should always return
- // identify matrix.
- mEnhancedSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(HWC_DISPLAY_PRIMARY,
- Dataspace::SRGB_LINEAR);
-
- // we will apply this on Display P3.
- if (mEnhancedSaturationMatrix != mat4()) {
- ColorSpace srgb(ColorSpace::sRGB());
- ColorSpace displayP3(ColorSpace::DisplayP3());
- mat4 srgbToP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform());
- mat4 p3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform());
- mEnhancedSaturationMatrix = srgbToP3 * mEnhancedSaturationMatrix * p3ToSrgb;
+ if (mScheduler->isIdleTimerEnabled()) {
+ mScheduler->setChangeRefreshRateCallback(
+ [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(type, event);
+ });
}
+ mRefreshRateConfigs[*display->getId()] = std::make_shared<scheduler::RefreshRateConfigs>(
+ getHwComposer().getConfigs(*display->getId()));
+ mRefreshRateStats =
+ std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()],
+ mTimeStats);
ALOGV("Done initializing");
}
@@ -819,6 +754,9 @@
property_get("persist.sys.sf.native_mode", value, "0");
mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value));
+
+ property_get("persist.sys.sf.color_mode", value, "0");
+ mForceColorMode = static_cast<ColorMode>(atoi(value));
}
void SurfaceFlinger::startBootAnim() {
@@ -832,11 +770,11 @@
}
size_t SurfaceFlinger::getMaxTextureSize() const {
- return getBE().mRenderEngine->getMaxTextureSize();
+ return getRenderEngine().getMaxTextureSize();
}
size_t SurfaceFlinger::getMaxViewportDims() const {
- return getBE().mRenderEngine->getMaxViewportDims();
+ return getRenderEngine().getMaxViewportDims();
}
// ----------------------------------------------------------------------------
@@ -874,55 +812,49 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs) {
- if (configs == nullptr || display.get() == nullptr) {
+status_t SurfaceFlinger::getDisplayConfigsLocked(const sp<IBinder>& displayToken,
+ Vector<DisplayInfo>* configs) {
+ if (!displayToken || !configs) {
return BAD_VALUE;
}
- if (!display.get())
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
return NAME_NOT_FOUND;
-
- int32_t type = NAME_NOT_FOUND;
- for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
- if (display == mBuiltinDisplays[i]) {
- type = i;
- break;
- }
- }
-
- if (type < 0) {
- return type;
}
// TODO: Not sure if display density should handled by SF any longer
class Density {
- static int getDensityFromProperty(char const* propName) {
+ static float getDensityFromProperty(char const* propName) {
char property[PROPERTY_VALUE_MAX];
- int density = 0;
+ float density = 0.0f;
if (property_get(propName, property, nullptr) > 0) {
- density = atoi(property);
+ density = strtof(property, nullptr);
}
return density;
}
public:
- static int getEmuDensity() {
+ static float getEmuDensity() {
return getDensityFromProperty("qemu.sf.lcd_density"); }
- static int getBuildDensity() {
+ static float getBuildDensity() {
return getDensityFromProperty("ro.sf.lcd_density"); }
};
configs->clear();
- ConditionalLock _l(mStateLock,
- std::this_thread::get_id() != mMainThreadId);
- for (const auto& hwConfig : getHwComposer().getConfigs(type)) {
+ for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
DisplayInfo info = DisplayInfo();
float xdpi = hwConfig->getDpiX();
float ydpi = hwConfig->getDpiY();
- if (type == DisplayDevice::DISPLAY_PRIMARY) {
+ info.w = hwConfig->getWidth();
+ info.h = hwConfig->getHeight();
+ // Default display viewport to display width and height
+ info.viewportW = info.w;
+ info.viewportH = info.h;
+
+ if (displayId == getInternalDisplayIdLocked()) {
// The density of the device is provided by a build property
float density = Density::getBuildDensity() / 160.0f;
if (density == 0) {
@@ -939,8 +871,15 @@
info.density = density;
// TODO: this needs to go away (currently needed only by webkit)
- sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
- info.orientation = hw ? hw->getOrientation() : 0;
+ const auto display = getDefaultDisplayDeviceLocked();
+ info.orientation = display ? display->getOrientation() : 0;
+
+ // This is for screenrecord
+ const Rect viewport = display->getViewport();
+ if (viewport.isValid()) {
+ info.viewportW = uint32_t(viewport.getWidth());
+ info.viewportH = uint32_t(viewport.getHeight());
+ }
} else {
// TODO: where should this value come from?
static const int TV_DENSITY = 213;
@@ -948,12 +887,10 @@
info.orientation = 0;
}
- info.w = hwConfig->getWidth();
- info.h = hwConfig->getHeight();
info.xdpi = xdpi;
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- info.appVsyncOffset = vsyncPhaseOffsetNs;
+ info.appVsyncOffset = mPhaseOffsets->getCurrentAppOffset();
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
@@ -967,13 +904,13 @@
//
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
- info.presentationDeadline = hwConfig->getVsyncPeriod() -
- sfVsyncPhaseOffsetNs + 1000000;
+ info.presentationDeadline =
+ hwConfig->getVsyncPeriod() - mPhaseOffsets->getCurrentSfOffset() + 1000000;
// All non-virtual displays are currently considered secure.
info.secure = true;
- if (type == DisplayDevice::DISPLAY_PRIMARY &&
+ if (displayId == getInternalDisplayIdLocked() &&
primaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
std::swap(info.w, info.h);
}
@@ -984,129 +921,166 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */,
- DisplayStatInfo* stats) {
- if (stats == nullptr) {
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) {
+ if (!stats) {
return BAD_VALUE;
}
- // FIXME for now we always return stats for the primary display
- memset(stats, 0, sizeof(*stats));
- stats->vsyncTime = mPrimaryDispSync.computeNextRefresh(0);
- stats->vsyncPeriod = mPrimaryDispSync.getPeriod();
+ mScheduler->getDisplayStatInfo(stats);
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) {
- if (outViewport == nullptr || display.get() == nullptr) {
+int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
+ const auto display = getDisplayDevice(displayToken);
+ if (!display) {
+ ALOGE("getActiveConfig: Invalid display token %p", displayToken.get());
return BAD_VALUE;
}
- sp<const DisplayDevice> device(getDisplayDevice(display));
- if (device == nullptr) {
- return BAD_VALUE;
- }
-
- *outViewport = device->getViewport();
-
- return NO_ERROR;
+ return display->getActiveConfig();
}
-int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
- if (display == nullptr) {
- ALOGE("%s : display is nullptr", __func__);
- return BAD_VALUE;
+void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
+ Scheduler::ConfigEvent event) {
+ ATRACE_CALL();
+
+ // Lock is acquired by setRefreshRateTo.
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set active config %d for invalid display token %p", mode,
+ displayToken.get());
+ return;
}
-
- sp<const DisplayDevice> device(getDisplayDevice(display));
- if (device != nullptr) {
- return device->getActiveConfig();
+ if (display->isVirtual()) {
+ ALOGW("Attempt to set active config %d for virtual display", mode);
+ return;
}
-
- return BAD_VALUE;
-}
-
-void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) {
- ALOGD("Set active config mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
- this);
- int32_t type = hw->getDisplayType();
- int currentMode = hw->getActiveConfig();
-
- if (mode == currentMode) {
- ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
+ int currentDisplayPowerMode = display->getPowerMode();
+ if (currentDisplayPowerMode != HWC_POWER_MODE_NORMAL) {
+ // Don't change active config when in AoD.
return;
}
- if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGW("Trying to set config for virtual display");
- return;
- }
+ // Don't check against the current mode yet. Worst case we set the desired
+ // config twice. However event generation config might have changed so we need to update it
+ // accordingly
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ const Scheduler::ConfigEvent desiredConfig = mDesiredActiveConfig.event | event;
+ mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken, desiredConfig};
- hw->setActiveConfig(mode);
- getHwComposer().setActiveConfig(type, mode);
+ if (!mDesiredActiveConfigChanged) {
+ // This is the first time we set the desired
+ mScheduler->pauseVsyncCallback(mAppConnectionHandle, true);
+
+ // This will trigger HWC refresh without resetting the idle timer.
+ repaintEverythingForHWC();
+ }
+ mDesiredActiveConfigChanged = true;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
}
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) {
- class MessageSetActiveConfig: public MessageBase {
- SurfaceFlinger& mFlinger;
- sp<IBinder> mDisplay;
- int mMode;
- public:
- MessageSetActiveConfig(SurfaceFlinger& flinger, const sp<IBinder>& disp,
- int mode) :
- mFlinger(flinger), mDisplay(disp) { mMode = mode; }
- virtual bool handler() {
- Vector<DisplayInfo> configs;
- mFlinger.getDisplayConfigs(mDisplay, &configs);
- if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
- ALOGE("Attempt to set active config = %d for display with %zu configs",
- mMode, configs.size());
- return true;
- }
- sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
- if (hw == nullptr) {
- ALOGE("Attempt to set active config = %d for null display %p",
- mMode, mDisplay.get());
- } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
- ALOGW("Attempt to set active config = %d for virtual display",
- mMode);
- } else {
- mFlinger.setActiveConfigInternal(hw, mMode);
- }
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_CALL();
+
+ std::vector<int32_t> allowedConfig;
+ allowedConfig.push_back(mode);
+
+ return setAllowedDisplayConfigs(displayToken, allowedConfig);
+}
+
+void SurfaceFlinger::setActiveConfigInternal() {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
+
+ const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
+ display->setActiveConfig(mUpcomingActiveConfig.configId);
+
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
+ ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+ if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mUpcomingActiveConfig.configId);
+ }
+}
+
+bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+ if (mCheckPendingFence) {
+ if (mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) {
+ // fence has not signaled yet. wait for the next invalidate
+ repaintEverythingForHWC();
return true;
}
- };
- sp<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode);
- postMessageSync(msg);
- return NO_ERROR;
-}
-status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display,
- Vector<ColorMode>* outColorModes) {
- if ((outColorModes == nullptr) || (display.get() == nullptr)) {
- return BAD_VALUE;
+
+ // We received the present fence from the HWC, so we assume it successfully updated
+ // the config, hence we update SF.
+ mCheckPendingFence = false;
+ setActiveConfigInternal();
}
- if (!display.get()) {
- return NAME_NOT_FOUND;
- }
-
- int32_t type = NAME_NOT_FOUND;
- for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
- if (display == mBuiltinDisplays[i]) {
- type = i;
- break;
+ // Store the local variable to release the lock.
+ ActiveConfigInfo desiredActiveConfig;
+ {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (!mDesiredActiveConfigChanged) {
+ return false;
}
+ desiredActiveConfig = mDesiredActiveConfig;
}
- if (type < 0) {
- return type;
+ const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
+ if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
+ // display is not valid or we are already in the requested mode
+ // on both cases there is nothing left to do
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mScheduler->pauseVsyncCallback(mAppConnectionHandle, false);
+ mDesiredActiveConfigChanged = false;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ return false;
+ }
+
+ // Desired active config was set, it is different than the config currently in use, however
+ // allowed configs might have change by the time we process the refresh.
+ // Make sure the desired config is still allowed
+ if (!isConfigAllowed(*display->getId(), desiredActiveConfig.configId)) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig.configId = display->getActiveConfig();
+ return false;
+ }
+ mUpcomingActiveConfig = desiredActiveConfig;
+ const auto displayId = display->getId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
+ ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
+ getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+
+ // we need to submit an empty frame to HWC to start the process
+ mCheckPendingFence = true;
+
+ return false;
+}
+
+status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
+ Vector<ColorMode>* outColorModes) {
+ if (!displayToken || !outColorModes) {
+ return BAD_VALUE;
}
std::vector<ColorMode> modes;
{
- ConditionalLock _l(mStateLock,
- std::this_thread::get_id() != mMainThreadId);
- modes = getHwComposer().getColorModes(type);
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ return NAME_NOT_FOUND;
+ }
+
+ modes = getHwComposer().getColorModes(*displayId);
}
outColorModes->clear();
std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
@@ -1114,79 +1088,51 @@
return NO_ERROR;
}
-ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) {
- sp<const DisplayDevice> device(getDisplayDevice(display));
- if (device != nullptr) {
- return device->getActiveColorMode();
+status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+ ui::DisplayPrimaries &primaries) {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ // Currently we only support this API for a single internal display.
+ if (getInternalDisplayToken() != displayToken) {
+ return BAD_VALUE;
+ }
+
+ memcpy(&primaries, &mInternalDisplayPrimaries, sizeof(ui::DisplayPrimaries));
+ return NO_ERROR;
+}
+
+ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
+ if (const auto display = getDisplayDevice(displayToken)) {
+ return display->getCompositionDisplay()->getState().colorMode;
}
return static_cast<ColorMode>(BAD_VALUE);
}
-void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
- ColorMode mode, Dataspace dataSpace,
- RenderIntent renderIntent) {
- int32_t type = hw->getDisplayType();
- ColorMode currentMode = hw->getActiveColorMode();
- Dataspace currentDataSpace = hw->getCompositionDataSpace();
- RenderIntent currentRenderIntent = hw->getActiveRenderIntent();
-
- if (mode == currentMode && dataSpace == currentDataSpace &&
- renderIntent == currentRenderIntent) {
- return;
- }
-
- if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGW("Trying to set config for virtual display");
- return;
- }
-
- hw->setActiveColorMode(mode);
- hw->setCompositionDataSpace(dataSpace);
- hw->setActiveRenderIntent(renderIntent);
- getHwComposer().setActiveColorMode(type, mode, renderIntent);
-
- ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), type=%d",
- decodeColorMode(mode).c_str(), mode,
- decodeRenderIntent(renderIntent).c_str(), renderIntent,
- hw->getDisplayType());
-}
-
-
-status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display,
- ColorMode colorMode) {
- class MessageSetActiveColorMode: public MessageBase {
- SurfaceFlinger& mFlinger;
- sp<IBinder> mDisplay;
- ColorMode mMode;
- public:
- MessageSetActiveColorMode(SurfaceFlinger& flinger, const sp<IBinder>& disp,
- ColorMode mode) :
- mFlinger(flinger), mDisplay(disp) { mMode = mode; }
- virtual bool handler() {
- Vector<ColorMode> modes;
- mFlinger.getDisplayColorModes(mDisplay, &modes);
- bool exists = std::find(std::begin(modes), std::end(modes), mMode) != std::end(modes);
- if (mMode < ColorMode::NATIVE || !exists) {
- ALOGE("Attempt to set invalid active color mode %s (%d) for display %p",
- decodeColorMode(mMode).c_str(), mMode, mDisplay.get());
- return true;
- }
- sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
- if (hw == nullptr) {
- ALOGE("Attempt to set active color mode %s (%d) for null display %p",
- decodeColorMode(mMode).c_str(), mMode, mDisplay.get());
- } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
- ALOGW("Attempt to set active color mode %s %d for virtual display",
- decodeColorMode(mMode).c_str(), mMode);
- } else {
- mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN,
- RenderIntent::COLORIMETRIC);
- }
- return true;
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
+ postMessageSync(new LambdaMessage([&] {
+ Vector<ColorMode> modes;
+ getDisplayColorModes(displayToken, &modes);
+ bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
+ if (mode < ColorMode::NATIVE || !exists) {
+ ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
+ decodeColorMode(mode).c_str(), mode, displayToken.get());
+ return;
}
- };
- sp<MessageBase> msg = new MessageSetActiveColorMode(*this, display, colorMode);
- postMessageSync(msg);
+ const auto display = getDisplayDevice(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
+ decodeColorMode(mode).c_str(), mode, displayToken.get());
+ } else if (display->isVirtual()) {
+ ALOGW("Attempt to set active color mode %s (%d) for virtual display",
+ decodeColorMode(mode).c_str(), mode);
+ } else {
+ display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN,
+ RenderIntent::COLORIMETRIC);
+ }
+ }));
+
return NO_ERROR;
}
@@ -1202,20 +1148,20 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& display,
- HdrCapabilities* outCapabilities) const {
+status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken,
+ HdrCapabilities* outCapabilities) const {
Mutex::Autolock _l(mStateLock);
- sp<const DisplayDevice> displayDevice(getDisplayDeviceLocked(display));
- if (displayDevice == nullptr) {
- ALOGE("getHdrCapabilities: Invalid display %p", displayDevice.get());
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("getHdrCapabilities: Invalid display token %p", displayToken.get());
return BAD_VALUE;
}
// At this point the DisplayDeivce should already be set up,
// meaning the luminance information is already queried from
// hardware composer and stored properly.
- const HdrCapabilities& capabilities = displayDevice->getHdrCapabilities();
+ const HdrCapabilities& capabilities = display->getHdrCapabilities();
*outCapabilities = HdrCapabilities(capabilities.getSupportedHdrTypes(),
capabilities.getDesiredMaxLuminance(),
capabilities.getDesiredMaxAverageLuminance(),
@@ -1224,33 +1170,102 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const {
+ if (!outFormat || !outDataspace || !outComponentMask) {
+ return BAD_VALUE;
+ }
+ const auto display = getDisplayDevice(displayToken);
+ if (!display || !display->getId()) {
+ ALOGE("getDisplayedContentSamplingAttributes: Bad display token: %p", display.get());
+ return BAD_VALUE;
+ }
+ return getHwComposer().getDisplayedContentSamplingAttributes(*display->getId(), outFormat,
+ outDataspace, outComponentMask);
+}
+
+status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
+ bool enable, uint8_t componentMask,
+ uint64_t maxFrames) const {
+ const auto display = getDisplayDevice(displayToken);
+ if (!display || !display->getId()) {
+ ALOGE("setDisplayContentSamplingEnabled: Bad display token: %p", display.get());
+ return BAD_VALUE;
+ }
+
+ return getHwComposer().setDisplayContentSamplingEnabled(*display->getId(), enable,
+ componentMask, maxFrames);
+}
+
+status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
+ uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) const {
+ const auto display = getDisplayDevice(displayToken);
+ if (!display || !display->getId()) {
+ ALOGE("getDisplayContentSample: Bad display token: %p", displayToken.get());
+ return BAD_VALUE;
+ }
+
+ return getHwComposer().getDisplayedContentSample(*display->getId(), maxFrames, timestamp,
+ outStats);
+}
+
+status_t SurfaceFlinger::getProtectedContentSupport(bool* outSupported) const {
+ if (!outSupported) {
+ return BAD_VALUE;
+ }
+ *outSupported = getRenderEngine().supportsProtectedContent();
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken,
+ bool* outIsWideColorDisplay) const {
+ if (!displayToken || !outIsWideColorDisplay) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mStateLock);
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return BAD_VALUE;
+ }
+ *outIsWideColorDisplay = display->hasWideColorGamut();
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
- sp<LambdaMessage> enableVSyncInjections = new LambdaMessage([&]() {
+ postMessageSync(new LambdaMessage([&] {
Mutex::Autolock _l(mStateLock);
if (mInjectVSyncs == enable) {
return;
}
+ auto resyncCallback =
+ mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
+
+ // TODO(b/128863962): Part of the Injector should be refactored, so that it
+ // can be passed to Scheduler.
if (enable) {
ALOGV("VSync Injections enabled");
if (mVSyncInjector.get() == nullptr) {
mVSyncInjector = std::make_unique<InjectVSyncSource>();
mInjectorEventThread = std::make_unique<
impl::EventThread>(mVSyncInjector.get(),
- [this]() { resyncWithRateLimit(); },
impl::EventThread::InterceptVSyncsCallback(),
"injEventThread");
}
- mEventQueue->setEventThread(mInjectorEventThread.get());
+ mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
} else {
ALOGV("VSync Injections disabled");
- mEventQueue->setEventThread(mSFEventThread.get());
+ mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
+ std::move(resyncCallback));
}
mInjectVSyncs = enable;
- });
- postMessageSync(enableVSyncInjections);
+ }));
+
return NO_ERROR;
}
@@ -1270,15 +1285,6 @@
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
NO_THREAD_SAFETY_ANALYSIS {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if ((uid != AID_SHELL) &&
- !PermissionCache::checkPermission(sDump, pid, uid)) {
- ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
-
// Try to acquire a lock for 1s, fail gracefully
const status_t err = mStateLock.timedLock(s2ns(1));
const bool locked = (err == NO_ERROR);
@@ -1296,15 +1302,74 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getCompositionPreference(
+ Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
+ Dataspace* outWideColorGamutDataspace,
+ ui::PixelFormat* outWideColorGamutPixelFormat) const {
+ *outDataspace = mDefaultCompositionDataspace;
+ *outPixelFormat = defaultCompositionPixelFormat;
+ *outWideColorGamutDataspace = mWideColorGamutCompositionDataspace;
+ *outWideColorGamutPixelFormat = wideColorGamutCompositionPixelFormat;
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ if (!listener || samplingArea == Rect::INVALID_RECT) {
+ return BAD_VALUE;
+ }
+ mRegionSamplingThread->addListener(samplingArea, stopLayerHandle, listener);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+ mRegionSamplingThread->removeListener(listener);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ if (!displayToken || !outSupport) {
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ return NAME_NOT_FOUND;
+ }
+ *outSupport =
+ getHwComposer().hasDisplayCapability(displayId, HWC2::DisplayCapability::Brightness);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
+ float brightness) const {
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ return NAME_NOT_FOUND;
+ }
+ return getHwComposer().setDisplayBrightness(*displayId, brightness);
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource) {
- if (vsyncSource == eVsyncSourceSurfaceFlinger) {
- return mSFEventThread->createEventConnection();
- } else {
- return mEventThread->createEventConnection();
- }
+ auto resyncCallback = mScheduler->makeResyncCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ return getVsyncPeriod();
+ });
+
+ const auto& handle =
+ vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
+
+ return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback));
}
// ----------------------------------------------------------------------------
@@ -1346,93 +1411,36 @@
} while (true);
}
-void SurfaceFlinger::enableHardwareVsync() {
- Mutex::Autolock _l(mHWVsyncLock);
- if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mPrimaryDispSync.beginResync();
- //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
- mEventControlThread->setVsyncEnabled(true);
- mPrimaryHWVsyncEnabled = true;
+nsecs_t SurfaceFlinger::getVsyncPeriod() const {
+ const auto displayId = getInternalDisplayIdLocked();
+ if (!displayId || !getHwComposer().isConnected(*displayId)) {
+ return 0;
}
+
+ const auto config = getHwComposer().getActiveConfig(*displayId);
+ return config ? config->getVsyncPeriod() : 0;
}
-void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
- Mutex::Autolock _l(mHWVsyncLock);
+void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
+ int64_t timestamp) {
+ ATRACE_NAME("SF onVsync");
- if (makeAvailable) {
- mHWVsyncAvailable = true;
- } else if (!mHWVsyncAvailable) {
- // Hardware vsync is not currently available, so abort the resync
- // attempt for now
- return;
- }
-
- const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
- const nsecs_t period = activeConfig->getVsyncPeriod();
-
- mPrimaryDispSync.reset();
- mPrimaryDispSync.setPeriod(period);
-
- if (!mPrimaryHWVsyncEnabled) {
- mPrimaryDispSync.beginResync();
- //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
- mEventControlThread->setVsyncEnabled(true);
- mPrimaryHWVsyncEnabled = true;
- }
-}
-
-void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
- Mutex::Autolock _l(mHWVsyncLock);
- if (mPrimaryHWVsyncEnabled) {
- //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
- mEventControlThread->setVsyncEnabled(false);
- mPrimaryDispSync.endResync();
- mPrimaryHWVsyncEnabled = false;
- }
- if (makeUnavailable) {
- mHWVsyncAvailable = false;
- }
-}
-
-void SurfaceFlinger::resyncWithRateLimit() {
- static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
-
- // No explicit locking is needed here since EventThread holds a lock while calling this method
- static nsecs_t sLastResyncAttempted = 0;
- const nsecs_t now = systemTime();
- if (now - sLastResyncAttempted > kIgnoreDelay) {
- resyncToHardwareVsync(false);
- }
- sLastResyncAttempted = now;
-}
-
-void SurfaceFlinger::onVsyncReceived(int32_t sequenceId,
- hwc2_display_t displayId, int64_t timestamp) {
Mutex::Autolock lock(mStateLock);
// Ignore any vsyncs from a previous hardware composer.
if (sequenceId != getBE().mComposerSequenceId) {
return;
}
- int32_t type;
- if (!getBE().mHwc->onVsync(displayId, timestamp, &type)) {
+ if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
return;
}
- bool needsHwVsync = false;
-
- { // Scope for the lock
- Mutex::Autolock _l(mHWVsyncLock);
- if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
- }
+ if (hwcDisplayId != getHwComposer().getInternalHwcDisplayId()) {
+ // For now, we don't do anything with external display vsyncs.
+ return;
}
- if (needsHwVsync) {
- enableHardwareVsync();
- } else {
- disableHardwareVsync(false);
- }
+ mScheduler->addResyncSample(timestamp);
}
void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
@@ -1440,9 +1448,46 @@
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+bool SurfaceFlinger::isConfigAllowed(const DisplayId& displayId, int32_t config) {
+ std::lock_guard lock(mAllowedConfigsLock);
+
+ // if allowed configs are not set yet for this display, every config is considered allowed
+ if (mAllowedConfigs.find(displayId) == mAllowedConfigs.end()) {
+ return true;
+ }
+
+ return mAllowedConfigs[displayId]->isConfigAllowed(config);
+}
+
+void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+ if (mBootStage != BootStage::FINISHED) {
+ return;
+ }
+ ATRACE_CALL();
+
+ // Don't do any updating if the current fps is the same as the new one.
+ const auto displayId = getInternalDisplayIdLocked();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ const auto displayToken = getInternalDisplayTokenLocked();
+
+ auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (desiredConfigId == display->getActiveConfig()) {
+ return;
+ }
+
+ if (!isConfigAllowed(*displayId, desiredConfigId)) {
+ ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
+ return;
+ }
+
+ mPhaseOffsets->setRefreshRateType(refreshRate);
+ setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
+}
+
+void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
HWC2::Connection connection) {
- ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s)", sequenceId, display,
+ ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
connection == HWC2::Connection::Connected ? "connected" : "disconnected");
// Ignore events that do not have the right sequenceId.
@@ -1456,7 +1501,7 @@
// acquire it here.
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- mPendingHotplugEvents.emplace_back(HotplugEvent{display, connection});
+ mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection});
if (std::this_thread::get_id() == mMainThreadId) {
// Process all pending hot plug events immediately if we are on the main thread.
@@ -1466,43 +1511,44 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-void SurfaceFlinger::onRefreshReceived(int sequenceId,
- hwc2_display_t /*display*/) {
+void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
Mutex::Autolock lock(mStateLock);
if (sequenceId != getBE().mComposerSequenceId) {
return;
}
- repaintEverything();
+ repaintEverythingForHWC();
}
-void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
+void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
ATRACE_CALL();
Mutex::Autolock lock(mStateLock);
- getHwComposer().setVsyncEnabled(disp,
- enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+ if (const auto displayId = getInternalDisplayIdLocked()) {
+ getHwComposer().setVsyncEnabled(*displayId,
+ enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+ }
}
// Note: it is assumed the caller holds |mStateLock| when this is called
void SurfaceFlinger::resetDisplayState() {
- disableHardwareVsync(true);
+ mScheduler->disableHardwareVsync(true);
// Clear the drawing state so that the logic inside of
// handleTransactionLocked will fire. It will determine the delta between
// mCurrentState and mDrawingState and re-apply all changes when we make the
// transition.
mDrawingState.displays.clear();
- getRenderEngine().resetCurrentSurface();
mDisplays.clear();
}
void SurfaceFlinger::updateVrFlinger() {
+ ATRACE_CALL();
if (!mVrFlinger)
return;
bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay;
- if (vrFlingerRequestsDisplay == getBE().mHwc->isUsingVrComposer()) {
+ if (vrFlingerRequestsDisplay == getHwComposer().isUsingVrComposer()) {
return;
}
- if (vrFlingerRequestsDisplay && !getBE().mHwc->getComposer()->isRemote()) {
+ if (vrFlingerRequestsDisplay && !getHwComposer().getComposer()->isRemote()) {
ALOGE("Vr flinger is only supported for remote hardware composer"
" service connections. Ignoring request to transition to vr"
" flinger.");
@@ -1512,46 +1558,67 @@
Mutex::Autolock _l(mStateLock);
- int currentDisplayPowerMode = getDisplayDeviceLocked(
- mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])->getPowerMode();
+ sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
+ LOG_ALWAYS_FATAL_IF(!display);
+
+ const int currentDisplayPowerMode = display->getPowerMode();
+
+ // Clear out all the output layers from the composition engine for all
+ // displays before destroying the hardware composer interface. This ensures
+ // any HWC layers are destroyed through that interface before it becomes
+ // invalid.
+ for (const auto& [token, displayDevice] : mDisplays) {
+ displayDevice->getCompositionDisplay()->setOutputLayersOrderedByZ(
+ compositionengine::Output::OutputLayers());
+ }
+
+ // This DisplayDevice will no longer be relevant once resetDisplayState() is
+ // called below. Clear the reference now so we don't accidentally use it
+ // later.
+ display.clear();
if (!vrFlingerRequestsDisplay) {
mVrFlinger->SeizeDisplayOwnership();
}
resetDisplayState();
- getBE().mHwc.reset(); // Delete the current instance before creating the new one
- getBE().mHwc.reset(new HWComposer(std::make_unique<Hwc2::impl::Composer>(
- vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName)));
- getBE().mHwc->registerCallback(this, ++getBE().mComposerSequenceId);
+ // Delete the current instance before creating the new one
+ mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+ mCompositionEngine->setHwComposer(getFactory().createHWComposer(
+ vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
+ getHwComposer().registerCallback(this, ++getBE().mComposerSequenceId);
- LOG_ALWAYS_FATAL_IF(!getBE().mHwc->getComposer()->isRemote(),
+ LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
"Switched to non-remote hardware composer");
if (vrFlingerRequestsDisplay) {
mVrFlinger->GrantDisplayOwnership();
- } else {
- enableHardwareVsync();
}
mVisibleRegionsDirty = true;
invalidateHwcGeometry();
// Re-enable default display.
- sp<DisplayDevice> hw(getDisplayDeviceLocked(
- mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]));
- setPowerModeInternal(hw, currentDisplayPowerMode, /*stateLockHeld*/ true);
+ display = getDefaultDisplayDeviceLocked();
+ LOG_ALWAYS_FATAL_IF(!display);
+ setPowerModeInternal(display, currentDisplayPowerMode);
// Reset the timing values to account for the period of the swapped in HWC
- const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
- const nsecs_t period = activeConfig->getVsyncPeriod();
- mAnimFrameTracker.setDisplayRefreshPeriod(period);
+ const nsecs_t vsyncPeriod = getVsyncPeriod();
+ mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
+
+ // The present fences returned from vr_hwc are not an accurate
+ // representation of vsync times.
+ mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || !hasSyncFramework);
// Use phase of 0 since phase is not known.
// Use latency of 0, which will snap to the ideal latency.
- setCompositorTimingSnapped(0, period, 0);
+ DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
+ setCompositorTimingSnapped(stats, 0);
- android_atomic_or(1, &mRepaintEverything);
+ mScheduler->resyncToHardwareVsync(false, vsyncPeriod);
+
+ mRepaintEverything = true;
setTransactionFlags(eDisplayTransactionNeeded);
}
@@ -1559,13 +1626,38 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
- bool frameMissed = !mHadClientComposition &&
- mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getSignalTime() ==
- Fence::SIGNAL_TIME_PENDING);
+ bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
+ bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
+ bool gpuFrameMissed = mHadClientComposition && frameMissed;
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
+ ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
+ ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
if (frameMissed) {
- mTimeStats.incrementMissedFrames();
+ mFrameMissedCount++;
+ mTimeStats->incrementMissedFrames();
+ }
+
+ if (hwcFrameMissed) {
+ mHwcFrameMissedCount++;
+ }
+
+ if (gpuFrameMissed) {
+ mGpuFrameMissedCount++;
+ }
+
+ if (mUseSmart90ForVideo) {
+ // This call is made each time SF wakes up and creates a new frame. It is part
+ // of video detection feature.
+ mScheduler->updateFpsBasedOnNativeWindowApi();
+ }
+
+ if (performSetActiveConfig()) {
+ break;
+ }
+
+ // For now, only propagate backpressure when missing a hwc frame.
+ if (hwcFrameMissed) {
if (mPropagateBackpressure) {
signalLayerUpdate();
break;
@@ -1579,6 +1671,10 @@
bool refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
+
+ updateCursorAsync();
+ updateInputFlinger();
+
refreshNeeded |= mRepaintEverything;
if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
// Signal a refresh if a transaction modified the window state,
@@ -1596,17 +1692,25 @@
}
bool SurfaceFlinger::handleMessageTransaction() {
+ ATRACE_CALL();
uint32_t transactionFlags = peekTransactionFlags();
+
+ // Apply any ready transactions in the queues if there are still transactions that have not been
+ // applied, wake up during the next vsync period and check again
+ bool transactionNeeded = false;
+ if (!flushTransactionQueues()) {
+ transactionNeeded = true;
+ }
+
if (transactionFlags) {
handleTransaction(transactionFlags);
- return true;
}
- return false;
-}
-bool SurfaceFlinger::handleMessageInvalidate() {
- ATRACE_CALL();
- return handlePageFlip();
+ if (transactionNeeded) {
+ setTransactionFlags(eTransactionNeeded);
+ }
+
+ return transactionFlags;
}
void SurfaceFlinger::handleMessageRefresh() {
@@ -1614,111 +1718,218 @@
mRefreshPending = false;
- nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
- preComposition(refreshStartTime);
+ const bool repaintEverything = mRepaintEverything.exchange(false);
+ preComposition();
rebuildLayerStacks();
- setUpHWComposer();
- doDebugFlashRegions();
- doTracing("handleRefresh");
- logLayerStats();
- doComposition();
- postComposition(refreshStartTime);
+ calculateWorkingSet();
+ for (const auto& [token, display] : mDisplays) {
+ beginFrame(display);
+ prepareFrame(display);
+ doDebugFlashRegions(display, repaintEverything);
+ doComposition(display, repaintEverything);
+ }
- mPreviousPresentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
+ logLayerStats();
+
+ postFrame();
+ postComposition();
mHadClientComposition = false;
- for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- const sp<DisplayDevice>& displayDevice = mDisplays[displayId];
- mHadClientComposition = mHadClientComposition ||
- getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
+ mHadDeviceComposition = false;
+ for (const auto& [token, displayDevice] : mDisplays) {
+ auto display = displayDevice->getCompositionDisplay();
+ const auto displayId = display->getId();
+ mHadClientComposition =
+ mHadClientComposition || getHwComposer().hasClientComposition(displayId);
+ mHadDeviceComposition =
+ mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
}
+
mVsyncModulator.onRefreshed(mHadClientComposition);
mLayersWithQueuedFrames.clear();
}
-void SurfaceFlinger::doDebugFlashRegions()
-{
- // is debugging enabled
- if (CC_LIKELY(!mDebugRegion))
- return;
- const bool repaintEverything = mRepaintEverything;
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- if (hw->isDisplayOn()) {
- // transform the dirty region into this screen's coordinate space
- const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
- if (!dirtyRegion.isEmpty()) {
- // redraw the whole screen
- doComposeSurfaces(hw);
+bool SurfaceFlinger::handleMessageInvalidate() {
+ ATRACE_CALL();
+ bool refreshNeeded = handlePageFlip();
- // and draw the dirty region
- const int32_t height = hw->getHeight();
- auto& engine(getRenderEngine());
- engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1);
+ if (mVisibleRegionsDirty) {
+ computeLayerBounds();
+ if (mTracingEnabled) {
+ mTracing.notify("visibleRegionsDirty");
+ }
+ }
- hw->swapBuffers(getHwComposer());
+ for (auto& layer : mLayersPendingRefresh) {
+ Region visibleReg;
+ visibleReg.set(layer->getScreenBounds());
+ invalidateLayerStack(layer, visibleReg);
+ }
+ mLayersPendingRefresh.clear();
+ return refreshNeeded;
+}
+
+void SurfaceFlinger::calculateWorkingSet() {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ // build the h/w work list
+ if (CC_UNLIKELY(mGeometryInvalid)) {
+ mGeometryInvalid = false;
+ for (const auto& [token, displayDevice] : mDisplays) {
+ auto display = displayDevice->getCompositionDisplay();
+
+ uint32_t zOrder = 0;
+
+ for (auto& layer : display->getOutputLayersOrderedByZ()) {
+ auto& compositionState = layer->editState();
+ compositionState.forceClientComposition = false;
+ if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) {
+ compositionState.forceClientComposition = true;
+ }
+
+ // The output Z order is set here based on a simple counter.
+ compositionState.z = zOrder++;
+
+ // Update the display independent composition state. This goes
+ // to the general composition layer state structure.
+ // TODO: Do this once per compositionengine::CompositionLayer.
+ layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
+ true);
+
+ // Recalculate the geometry state of the output layer.
+ layer->updateCompositionState(true);
+
+ // Write the updated geometry state to the HWC
+ layer->writeStateToHWC(true);
}
}
}
- postFramebuffer();
+ // Set the per-frame data
+ for (const auto& [token, displayDevice] : mDisplays) {
+ auto display = displayDevice->getCompositionDisplay();
+ const auto displayId = display->getId();
+ if (!displayId) {
+ continue;
+ }
+ auto* profile = display->getDisplayColorProfile();
+
+ if (mDrawingState.colorMatrixChanged) {
+ display->setColorTransform(mDrawingState.colorMatrix);
+ }
+ Dataspace targetDataspace = Dataspace::UNKNOWN;
+ if (useColorManagement) {
+ ColorMode colorMode;
+ RenderIntent renderIntent;
+ pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
+ display->setColorMode(colorMode, targetDataspace, renderIntent);
+ }
+ for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+ if (layer->isHdrY410()) {
+ layer->forceClientComposition(displayDevice);
+ } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
+ layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
+ !profile->hasHDR10Support()) {
+ layer->forceClientComposition(displayDevice);
+ } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
+ layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
+ !profile->hasHLGSupport()) {
+ layer->forceClientComposition(displayDevice);
+ }
+
+ // TODO(b/111562338) remove when composer 2.3 is shipped.
+ if (layer->hasColorTransform()) {
+ layer->forceClientComposition(displayDevice);
+ }
+
+ if (layer->getRoundedCornerState().radius > 0.0f) {
+ layer->forceClientComposition(displayDevice);
+ }
+
+ if (layer->getForceClientComposition(displayDevice)) {
+ ALOGV("[%s] Requesting Client composition", layer->getName().string());
+ layer->setCompositionType(displayDevice,
+ Hwc2::IComposerClient::Composition::CLIENT);
+ continue;
+ }
+
+ const auto& displayState = display->getState();
+ layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
+ displayDevice->getSupportedPerFrameMetadata(),
+ isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN
+ : targetDataspace);
+ }
+ }
+
+ mDrawingState.colorMatrixChanged = false;
+
+ for (const auto& [token, displayDevice] : mDisplays) {
+ auto display = displayDevice->getCompositionDisplay();
+ for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+ auto& layerState = layer->getCompositionLayer()->editState().frontEnd;
+ layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>(
+ layer->getCompositionType(displayDevice));
+ }
+ }
+}
+
+void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice,
+ bool repaintEverything) {
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
+
+ // is debugging enabled
+ if (CC_LIKELY(!mDebugRegion))
+ return;
+
+ if (displayState.isEnabled) {
+ // transform the dirty region into this screen's coordinate space
+ const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
+ if (!dirtyRegion.isEmpty()) {
+ base::unique_fd readyFence;
+ // redraw the whole screen
+ doComposeSurfaces(displayDevice, dirtyRegion, &readyFence);
+
+ display->getRenderSurface()->queueBuffer(std::move(readyFence));
+ }
+ }
+
+ postFramebuffer(displayDevice);
if (mDebugRegion > 1) {
usleep(mDebugRegion * 1000);
}
- for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- auto& displayDevice = mDisplays[displayId];
- if (!displayDevice->isDisplayOn()) {
- continue;
- }
-
- status_t result = displayDevice->prepareFrame(*getBE().mHwc);
- ALOGE_IF(result != NO_ERROR,
- "prepareFrame for display %zd failed:"
- " %d (%s)",
- displayId, result, strerror(-result));
- }
-}
-
-void SurfaceFlinger::doTracing(const char* where) {
- ATRACE_CALL();
- ATRACE_NAME(where);
- if (CC_UNLIKELY(mTracing.isEnabled())) {
- mTracing.traceLayers(where, dumpProtoInfo(LayerVector::StateSet::Drawing));
- }
+ prepareFrame(displayDevice);
}
void SurfaceFlinger::logLayerStats() {
ATRACE_CALL();
if (CC_UNLIKELY(mLayerStats.isEnabled())) {
- int32_t hwcId = -1;
- for (size_t dpy = 0; dpy < mDisplays.size(); ++dpy) {
- const sp<const DisplayDevice>& displayDevice(mDisplays[dpy]);
- if (displayDevice->isPrimary()) {
- hwcId = displayDevice->getHwcDisplayId();
- break;
+ for (const auto& [token, display] : mDisplays) {
+ if (display->isPrimary()) {
+ mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(display));
+ return;
}
}
- if (hwcId < 0) {
- ALOGE("LayerStats: Hmmm, no primary display?");
- return;
- }
- mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(hwcId));
+
+ ALOGE("logLayerStats: no primary display");
}
}
-void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::preComposition()
{
ATRACE_CALL();
ALOGV("preComposition");
+ mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
bool needExtraInvalidate = false;
mDrawingState.traverseInZOrder([&](Layer* layer) {
- if (layer->onPreComposition(refreshStartTime)) {
+ if (layer->onPreComposition(mRefreshStartTime)) {
needExtraInvalidate = true;
}
});
@@ -1728,9 +1939,8 @@
}
}
-void SurfaceFlinger::updateCompositorTiming(
- nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
- std::shared_ptr<FenceTime>& presentFenceTime) {
+void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
+ std::shared_ptr<FenceTime>& presentFenceTime) {
// Update queue of past composite+present times and determine the
// most recently known composite to present latency.
getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime});
@@ -1752,42 +1962,40 @@
getBE().mCompositePresentTimes.pop();
}
- setCompositorTimingSnapped(
- vsyncPhase, vsyncInterval, compositeToPresentLatency);
+ setCompositorTimingSnapped(stats, compositeToPresentLatency);
}
-void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase,
- nsecs_t vsyncInterval, nsecs_t compositeToPresentLatency) {
+void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
+ nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0) ?
- (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) :
- ((-sfVsyncPhaseOffsetNs) % vsyncInterval);
+ nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
+ ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
+ : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
- // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval.
+ // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
if (idealLatency <= 0) {
- idealLatency = vsyncInterval;
+ idealLatency = stats.vsyncPeriod;
}
// Snap the latency to a value that removes scheduling jitter from the
// composition and present times, which often have >1ms of jitter.
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+ // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
// with (presentLatency % interval).
- nsecs_t bias = vsyncInterval / 2;
- int64_t extraVsyncs =
- (compositeToPresentLatency - idealLatency + bias) / vsyncInterval;
- nsecs_t snappedCompositeToPresentLatency = (extraVsyncs > 0) ?
- idealLatency + (extraVsyncs * vsyncInterval) : idealLatency;
+ nsecs_t bias = stats.vsyncPeriod / 2;
+ int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
+ nsecs_t snappedCompositeToPresentLatency =
+ (extraVsyncs > 0) ? idealLatency + (extraVsyncs * stats.vsyncPeriod) : idealLatency;
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
- getBE().mCompositorTiming.deadline = vsyncPhase - idealLatency;
- getBE().mCompositorTiming.interval = vsyncInterval;
+ getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
+ getBE().mCompositorTiming.interval = stats.vsyncPeriod;
getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
ATRACE_CALL();
ALOGV("postComposition");
@@ -1799,31 +2007,33 @@
}
// |mStateLock| not needed as we are on the main thread
- const sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
+ const auto displayDevice = getDefaultDisplayDeviceLocked();
getBE().mGlCompositionDoneTimeline.updateSignalTimes();
std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+ if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
glCompositionDoneFenceTime =
- std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+ std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+ ->getRenderSurface()
+ ->getClientTargetAcquireFence());
getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
} else {
glCompositionDoneFenceTime = FenceTime::NO_FENCE;
}
getBE().mDisplayTimeline.updateSignalTimes();
- sp<Fence> presentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
- auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+ mPreviousPresentFence = displayDevice ? getHwComposer().getPresentFence(*displayDevice->getId())
+ : Fence::NO_FENCE;
+ auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence);
getBE().mDisplayTimeline.push(presentFenceTime);
- nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0);
- nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod();
+ DisplayStatInfo stats;
+ mScheduler->getDisplayStatInfo(&stats);
- // We use the refreshStartTime which might be sampled a little later than
+ // We use the mRefreshStartTime which might be sampled a little later than
// when we started doing work for this frame, but that should be okay
// since updateCompositorTiming has snapping logic.
- updateCompositorTiming(
- vsyncPhase, vsyncInterval, refreshStartTime, presentFenceTime);
+ updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime);
CompositorTiming compositorTiming;
{
std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -1831,8 +2041,9 @@
}
mDrawingState.traverseInZOrder([&](Layer* layer) {
- bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
- presentFenceTime, compositorTiming);
+ bool frameLatched =
+ layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
+ presentFenceTime, compositorTiming);
if (frameLatched) {
recordBufferingStats(layer->getName().string(),
layer->getOccupancyHistory(false));
@@ -1840,16 +2051,13 @@
});
if (presentFenceTime->isValid()) {
- if (mPrimaryDispSync.addPresentFence(presentFenceTime)) {
- enableHardwareVsync();
- } else {
- disableHardwareVsync(false);
- }
+ mScheduler->addPresentFence(presentFenceTime);
}
if (!hasSyncFramework) {
- if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) {
- enableHardwareVsync();
+ if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+ displayDevice->isPoweredOn()) {
+ mScheduler->enableHardwareVsync();
}
}
@@ -1859,23 +2067,25 @@
if (presentFenceTime->isValid()) {
mAnimFrameTracker.setActualPresentFence(
std::move(presentFenceTime));
- } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
+ } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
- nsecs_t presentTime =
- getBE().mHwc->getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
+ const nsecs_t presentTime =
+ getHwComposer().getRefreshTimestamp(*displayDevice->getId());
mAnimFrameTracker.setActualPresentTime(presentTime);
}
mAnimFrameTracker.advanceFrame();
}
- mTimeStats.incrementTotalFrames();
+ mTimeStats->incrementTotalFrames();
if (mHadClientComposition) {
- mTimeStats.incrementClientCompositionFrames();
+ mTimeStats->incrementClientCompositionFrames();
}
- if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) &&
- hw->getPowerMode() == HWC_POWER_MODE_OFF) {
+ mTimeStats->setPresentFenceGlobal(presentFenceTime);
+
+ if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+ !displayDevice->isPoweredOn()) {
return;
}
@@ -1884,7 +2094,7 @@
mHasPoweredOff = false;
} else {
nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
- size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval);
+ size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
getBE().mFrameBuckets[numPeriods] += elapsedTime;
} else {
@@ -1904,6 +2114,28 @@
ATRACE_INT("TexturePoolSize", mTexturePool.size());
}
}
+
+ mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
+ mTransactionCompletedThread.sendCallbacks();
+
+ if (mLumaSampling && mRegionSamplingThread) {
+ mRegionSamplingThread->notifyNewContent();
+ }
+}
+
+void SurfaceFlinger::computeLayerBounds() {
+ for (const auto& pair : mDisplays) {
+ const auto& displayDevice = pair.second;
+ const auto display = displayDevice->getCompositionDisplay();
+ for (const auto& layer : mDrawingState.layersSortedByZ) {
+ // only consider the layers on the given layer stack
+ if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+ continue;
+ }
+
+ layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+ }
+ }
}
void SurfaceFlinger::rebuildLayerStacks() {
@@ -1916,59 +2148,78 @@
mVisibleRegionsDirty = false;
invalidateHwcGeometry();
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
+ for (const auto& pair : mDisplays) {
+ const auto& displayDevice = pair.second;
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
Region opaqueRegion;
Region dirtyRegion;
- Vector<sp<Layer>> layersSortedByZ;
+ compositionengine::Output::OutputLayers layersSortedByZ;
+ Vector<sp<Layer>> deprecated_layersSortedByZ;
Vector<sp<Layer>> layersNeedingFences;
- const sp<DisplayDevice>& displayDevice(mDisplays[dpy]);
- const Transform& tr(displayDevice->getTransform());
- const Rect bounds(displayDevice->getBounds());
- if (displayDevice->isDisplayOn()) {
+ const ui::Transform& tr = displayState.transform;
+ const Rect bounds = displayState.bounds;
+ if (displayState.isEnabled) {
computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
mDrawingState.traverseInZOrder([&](Layer* layer) {
- bool hwcLayerDestroyed = false;
- if (layer->belongsToDisplay(displayDevice->getLayerStack(),
- displayDevice->isPrimary())) {
+ auto compositionLayer = layer->getCompositionLayer();
+ if (compositionLayer == nullptr) {
+ return;
+ }
+
+ const auto displayId = displayDevice->getId();
+ sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE();
+ LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr);
+
+ bool needsOutputLayer = false;
+
+ if (display->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
Region drawRegion(tr.transform(
layer->visibleNonTransparentRegion));
drawRegion.andSelf(bounds);
if (!drawRegion.isEmpty()) {
- layersSortedByZ.add(layer);
- } else {
- // Clear out the HWC layer if this layer was
- // previously visible, but no longer is
- hwcLayerDestroyed = layer->destroyHwcLayer(
- displayDevice->getHwcDisplayId());
+ needsOutputLayer = true;
}
- } else {
- // WM changes displayDevice->layerStack upon sleep/awake.
- // Here we make sure we delete the HWC layers even if
- // WM changed their layer stack.
- hwcLayerDestroyed = layer->destroyHwcLayer(
- displayDevice->getHwcDisplayId());
}
- // If a layer is not going to get a release fence because
- // it is invisible, but it is also going to release its
- // old buffer, add it to the list of layers needing
- // fences.
- if (hwcLayerDestroyed) {
- auto found = std::find(mLayersWithQueuedFrames.cbegin(),
- mLayersWithQueuedFrames.cend(), layer);
- if (found != mLayersWithQueuedFrames.cend()) {
+ if (needsOutputLayer) {
+ layersSortedByZ.emplace_back(
+ display->getOrCreateOutputLayer(displayId, compositionLayer,
+ layerFE));
+ deprecated_layersSortedByZ.add(layer);
+
+ auto& outputLayerState = layersSortedByZ.back()->editState();
+ outputLayerState.visibleRegion =
+ tr.transform(layer->visibleRegion.intersect(displayState.viewport));
+ } else if (displayId) {
+ // For layers that are being removed from a HWC display,
+ // and that have queued frames, add them to a a list of
+ // released layers so we can properly set a fence.
+ bool hasExistingOutputLayer =
+ display->getOutputLayerForLayer(compositionLayer.get()) != nullptr;
+ bool hasQueuedFrames = std::find(mLayersWithQueuedFrames.cbegin(),
+ mLayersWithQueuedFrames.cend(),
+ layer) != mLayersWithQueuedFrames.cend();
+
+ if (hasExistingOutputLayer && hasQueuedFrames) {
layersNeedingFences.add(layer);
}
}
});
}
- displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
+
+ display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
+
+ displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ);
displayDevice->setLayersNeedingFences(layersNeedingFences);
- displayDevice->undefinedRegion.set(bounds);
- displayDevice->undefinedRegion.subtractSelf(
- tr.transform(opaqueRegion));
- displayDevice->dirtyRegion.orSelf(dirtyRegion);
+
+ Region undefinedRegion{bounds};
+ undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
+
+ display->editState().undefinedRegion = undefinedRegion;
+ display->editState().dirtyRegion.orSelf(dirtyRegion);
}
}
}
@@ -1977,28 +2228,37 @@
// can only be one of
// - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
// - Dataspace::DISPLAY_P3
+// - Dataspace::DISPLAY_BT2020
// The returned HDR data space is one of
// - Dataspace::UNKNOWN
// - Dataspace::BT2020_HLG
// - Dataspace::BT2020_PQ
-Dataspace SurfaceFlinger::getBestDataspace(
- const sp<const DisplayDevice>& displayDevice, Dataspace* outHdrDataSpace) const {
- Dataspace bestDataSpace = Dataspace::SRGB;
+Dataspace SurfaceFlinger::getBestDataspace(const sp<const DisplayDevice>& display,
+ Dataspace* outHdrDataSpace) const {
+ Dataspace bestDataSpace = Dataspace::V0_SRGB;
*outHdrDataSpace = Dataspace::UNKNOWN;
- for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+ for (const auto& layer : display->getVisibleLayersSortedByZ()) {
switch (layer->getDataSpace()) {
case Dataspace::V0_SCRGB:
case Dataspace::V0_SCRGB_LINEAR:
+ case Dataspace::BT2020:
+ case Dataspace::BT2020_ITU:
+ case Dataspace::BT2020_LINEAR:
+ case Dataspace::DISPLAY_BT2020:
+ bestDataSpace = Dataspace::DISPLAY_BT2020;
+ break;
case Dataspace::DISPLAY_P3:
bestDataSpace = Dataspace::DISPLAY_P3;
break;
case Dataspace::BT2020_PQ:
case Dataspace::BT2020_ITU_PQ:
+ bestDataSpace = Dataspace::DISPLAY_P3;
*outHdrDataSpace = Dataspace::BT2020_PQ;
break;
case Dataspace::BT2020_HLG:
case Dataspace::BT2020_ITU_HLG:
+ bestDataSpace = Dataspace::DISPLAY_P3;
// When there's mixed PQ content and HLG content, we set the HDR
// data space to be BT2020_PQ and convert HLG to PQ.
if (*outHdrDataSpace == Dataspace::UNKNOWN) {
@@ -2014,9 +2274,8 @@
}
// Pick the ColorMode / Dataspace for the display device.
-void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice,
- ColorMode* outMode, Dataspace* outDataSpace,
- RenderIntent* outRenderIntent) const {
+void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode,
+ Dataspace* outDataSpace, RenderIntent* outRenderIntent) const {
if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
*outMode = ColorMode::NATIVE;
*outDataSpace = Dataspace::UNKNOWN;
@@ -2025,11 +2284,24 @@
}
Dataspace hdrDataSpace;
- Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace);
+ Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace);
+
+ auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
+
+ switch (mForceColorMode) {
+ case ColorMode::SRGB:
+ bestDataSpace = Dataspace::V0_SRGB;
+ break;
+ case ColorMode::DISPLAY_P3:
+ bestDataSpace = Dataspace::DISPLAY_P3;
+ break;
+ default:
+ break;
+ }
// respect hdrDataSpace only when there is no legacy HDR support
- const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
- !displayDevice->hasLegacyHdrSupport(hdrDataSpace);
+ const bool isHdr =
+ hdrDataSpace != Dataspace::UNKNOWN && !profile->hasLegacyHdrSupport(hdrDataSpace);
if (isHdr) {
bestDataSpace = hdrDataSpace;
}
@@ -2048,177 +2320,114 @@
break;
}
- displayDevice->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
+ profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
}
-void SurfaceFlinger::setUpHWComposer() {
- ATRACE_CALL();
- ALOGV("setUpHWComposer");
+void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) {
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- bool dirty = !mDisplays[dpy]->getDirtyRegion(mRepaintEverything).isEmpty();
- bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0;
- bool wasEmpty = !mDisplays[dpy]->lastCompositionHadVisibleLayers;
+ bool dirty = !display->getDirtyRegion(false).isEmpty();
+ bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
+ bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
- // If nothing has changed (!dirty), don't recompose.
- // If something changed, but we don't currently have any visible layers,
- // and didn't when we last did a composition, then skip it this time.
- // The second rule does two things:
- // - When all layers are removed from a display, we'll emit one black
- // frame, then nothing more until we get new layers.
- // - When a display is created with a private layer stack, we won't
- // emit any black frames until a layer is added to the layer stack.
- bool mustRecompose = dirty && !(empty && wasEmpty);
+ // If nothing has changed (!dirty), don't recompose.
+ // If something changed, but we don't currently have any visible layers,
+ // and didn't when we last did a composition, then skip it this time.
+ // The second rule does two things:
+ // - When all layers are removed from a display, we'll emit one black
+ // frame, then nothing more until we get new layers.
+ // - When a display is created with a private layer stack, we won't
+ // emit any black frames until a layer is added to the layer stack.
+ bool mustRecompose = dirty && !(empty && wasEmpty);
- ALOGV_IF(mDisplays[dpy]->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL,
- "dpy[%zu]: %s composition (%sdirty %sempty %swasEmpty)", dpy,
- mustRecompose ? "doing" : "skipping",
- dirty ? "+" : "-",
- empty ? "+" : "-",
- wasEmpty ? "+" : "-");
+ const char flagPrefix[] = {'-', '+'};
+ static_cast<void>(flagPrefix);
+ ALOGV_IF(displayDevice->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
+ __FUNCTION__, mustRecompose ? "doing" : "skipping",
+ displayDevice->getDebugName().c_str(), flagPrefix[dirty], flagPrefix[empty],
+ flagPrefix[wasEmpty]);
- mDisplays[dpy]->beginFrame(mustRecompose);
+ display->getRenderSurface()->beginFrame(mustRecompose);
- if (mustRecompose) {
- mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty;
- }
- }
-
- // build the h/w work list
- if (CC_UNLIKELY(mGeometryInvalid)) {
- mGeometryInvalid = false;
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> displayDevice(mDisplays[dpy]);
- const auto hwcId = displayDevice->getHwcDisplayId();
- if (hwcId >= 0) {
- const Vector<sp<Layer>>& currentLayers(
- displayDevice->getVisibleLayersSortedByZ());
- for (size_t i = 0; i < currentLayers.size(); i++) {
- const auto& layer = currentLayers[i];
- if (!layer->hasHwcLayer(hwcId)) {
- if (!layer->createHwcLayer(getBE().mHwc.get(), hwcId)) {
- layer->forceClientComposition(hwcId);
- continue;
- }
- }
-
- layer->setGeometry(displayDevice, i);
- if (mDebugDisableHWC || mDebugRegion) {
- layer->forceClientComposition(hwcId);
- }
- }
- }
- }
- }
-
- // Set the per-frame data
- for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- auto& displayDevice = mDisplays[displayId];
- const auto hwcId = displayDevice->getHwcDisplayId();
-
- if (hwcId < 0) {
- continue;
- }
- if (mDrawingState.colorMatrixChanged) {
- displayDevice->setColorTransform(mDrawingState.colorMatrix);
- status_t result = getBE().mHwc->setColorTransform(hwcId, mDrawingState.colorMatrix);
- ALOGE_IF(result != NO_ERROR, "Failed to set color transform on "
- "display %zd: %d", displayId, result);
- }
- for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- if (layer->isHdrY410()) {
- layer->forceClientComposition(hwcId);
- } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
- layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
- !displayDevice->hasHDR10Support()) {
- layer->forceClientComposition(hwcId);
- } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
- layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
- !displayDevice->hasHLGSupport()) {
- layer->forceClientComposition(hwcId);
- }
-
- if (layer->getForceClientComposition(hwcId)) {
- ALOGV("[%s] Requesting Client composition", layer->getName().string());
- layer->setCompositionType(hwcId, HWC2::Composition::Client);
- continue;
- }
-
- layer->setPerFrameData(displayDevice);
- }
-
- if (hasWideColorDisplay) {
- ColorMode colorMode;
- Dataspace dataSpace;
- RenderIntent renderIntent;
- pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
- setActiveColorModeInternal(displayDevice, colorMode, dataSpace, renderIntent);
- }
- }
-
- mDrawingState.colorMatrixChanged = false;
-
- for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- auto& displayDevice = mDisplays[displayId];
- if (!displayDevice->isDisplayOn()) {
- continue;
- }
-
- status_t result = displayDevice->prepareFrame(*getBE().mHwc);
- ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:"
- " %d (%s)", displayId, result, strerror(-result));
+ if (mustRecompose) {
+ display->editState().lastCompositionHadVisibleLayers = !empty;
}
}
-void SurfaceFlinger::doComposition() {
+void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& displayDevice) {
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
+
+ if (!displayState.isEnabled) {
+ return;
+ }
+
+ status_t result = display->getRenderSurface()->prepareFrame();
+ ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)",
+ displayDevice->getDebugName().c_str(), result, strerror(-result));
+}
+
+void SurfaceFlinger::doComposition(const sp<DisplayDevice>& displayDevice, bool repaintEverything) {
ATRACE_CALL();
ALOGV("doComposition");
- const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- if (hw->isDisplayOn()) {
- // transform the dirty region into this screen's coordinate space
- const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
- // repaint the framebuffer (if needed)
- doDisplayComposition(hw, dirtyRegion);
+ if (displayState.isEnabled) {
+ // transform the dirty region into this screen's coordinate space
+ const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
- hw->dirtyRegion.clear();
- hw->flip();
- }
+ // repaint the framebuffer (if needed)
+ doDisplayComposition(displayDevice, dirtyRegion);
+
+ display->editState().dirtyRegion.clear();
+ display->getRenderSurface()->flip();
}
- postFramebuffer();
+ postFramebuffer(displayDevice);
}
-void SurfaceFlinger::postFramebuffer()
+void SurfaceFlinger::postFrame()
{
+ // |mStateLock| not needed as we are on the main thread
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (display && getHwComposer().isConnected(*display->getId())) {
+ uint32_t flipCount = display->getPageFlipCount();
+ if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
+ logFrameStats();
+ }
+ }
+}
+
+void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) {
ATRACE_CALL();
ALOGV("postFramebuffer");
- const nsecs_t now = systemTime();
- mDebugInSwapBuffers = now;
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
+ const auto displayId = display->getId();
- for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- auto& displayDevice = mDisplays[displayId];
- if (!displayDevice->isDisplayOn()) {
- continue;
+ mPostFramebufferTime = systemTime();
+
+ if (displayState.isEnabled) {
+ if (displayId) {
+ getHwComposer().presentAndGetReleaseFences(*displayId);
}
- const auto hwcId = displayDevice->getHwcDisplayId();
- if (hwcId >= 0) {
- getBE().mHwc->presentAndGetReleaseFences(hwcId);
- }
- displayDevice->onSwapBuffersCompleted();
- displayDevice->makeCurrent();
- for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+ display->getRenderSurface()->onPresentDisplayCompleted();
+ for (auto& layer : display->getOutputLayersOrderedByZ()) {
sp<Fence> releaseFence = Fence::NO_FENCE;
+ bool usedClientComposition = true;
// The layer buffer from the previous frame (if any) is released
// by HWC only when the release fence from this frame (if any) is
// signaled. Always get the release fence from HWC first.
- auto hwcLayer = layer->getHwcLayer(hwcId);
- if (hwcId >= 0) {
- releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer);
+ if (layer->getState().hwc) {
+ const auto& hwcState = *layer->getState().hwc;
+ releaseFence =
+ getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get());
+ usedClientComposition =
+ hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
}
// If the layer was client composited in the previous frame, we
@@ -2226,37 +2435,28 @@
// Since we do not track that, always merge with the current
// client target acquire fence when it is available, even though
// this is suboptimal.
- if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) {
- releaseFence = Fence::merge("LayerRelease", releaseFence,
- displayDevice->getClientTargetAcquireFence());
+ if (usedClientComposition) {
+ releaseFence =
+ Fence::merge("LayerRelease", releaseFence,
+ display->getRenderSurface()->getClientTargetAcquireFence());
}
- layer->onLayerDisplayed(releaseFence);
+ layer->getLayerFE().onLayerDisplayed(releaseFence);
}
// We've got a list of layers needing fences, that are disjoint with
- // displayDevice->getVisibleLayersSortedByZ. The best we can do is to
+ // display->getVisibleLayersSortedByZ. The best we can do is to
// supply them with the present fence.
if (!displayDevice->getLayersNeedingFences().isEmpty()) {
- sp<Fence> presentFence = getBE().mHwc->getPresentFence(hwcId);
+ sp<Fence> presentFence =
+ displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
for (auto& layer : displayDevice->getLayersNeedingFences()) {
- layer->onLayerDisplayed(presentFence);
+ layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence);
}
}
- if (hwcId >= 0) {
- getBE().mHwc->clearReleaseFences(hwcId);
- }
- }
-
- mLastSwapBufferTime = systemTime() - now;
- mDebugInSwapBuffers = 0;
-
- // |mStateLock| not needed as we are on the main thread
- if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
- uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
- if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
- logFrameStats();
+ if (displayId) {
+ getHwComposer().clearReleaseFences(*displayId);
}
}
}
@@ -2291,68 +2491,36 @@
// here the transaction has been committed
}
-DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display,
- HWC2::Connection connection) const {
- // Figure out whether the event is for the primary display or an
- // external display by matching the Hwc display id against one for a
- // connected display. If we did not find a match, we then check what
- // displays are not already connected to determine the type. If we don't
- // have a connected primary display, we assume the new display is meant to
- // be the primary display, and then if we don't have an external display,
- // we assume it is that.
- const auto primaryDisplayId =
- getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY);
- const auto externalDisplayId =
- getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_EXTERNAL);
- if (primaryDisplayId && primaryDisplayId == display) {
- return DisplayDevice::DISPLAY_PRIMARY;
- } else if (externalDisplayId && externalDisplayId == display) {
- return DisplayDevice::DISPLAY_EXTERNAL;
- } else if (connection == HWC2::Connection::Connected && !primaryDisplayId) {
- return DisplayDevice::DISPLAY_PRIMARY;
- } else if (connection == HWC2::Connection::Connected && !externalDisplayId) {
- return DisplayDevice::DISPLAY_EXTERNAL;
- }
-
- return DisplayDevice::DISPLAY_ID_INVALID;
-}
-
void SurfaceFlinger::processDisplayHotplugEventsLocked() {
for (const auto& event : mPendingHotplugEvents) {
- auto displayType = determineDisplayType(event.display, event.connection);
- if (displayType == DisplayDevice::DISPLAY_ID_INVALID) {
- ALOGW("Unable to determine the display type for display %" PRIu64, event.display);
+ const std::optional<DisplayIdentificationInfo> info =
+ getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
+
+ if (!info) {
continue;
}
- if (getBE().mHwc->isUsingVrComposer() && displayType == DisplayDevice::DISPLAY_EXTERNAL) {
- ALOGE("External displays are not supported by the vr hardware composer.");
- continue;
- }
-
- getBE().mHwc->onHotplug(event.display, displayType, event.connection);
-
if (event.connection == HWC2::Connection::Connected) {
- if (!mBuiltinDisplays[displayType].get()) {
- ALOGV("Creating built in display %d", displayType);
- mBuiltinDisplays[displayType] = new BBinder();
- // All non-virtual displays are currently considered secure.
- DisplayDeviceState info(displayType, true);
- info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
- "Built-in Screen" : "External Screen";
- mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
- mInterceptor->saveDisplayCreation(info);
+ if (!mPhysicalDisplayTokens.count(info->id)) {
+ ALOGV("Creating display %s", to_string(info->id).c_str());
+ mPhysicalDisplayTokens[info->id] = new BBinder();
+ DisplayDeviceState state;
+ state.displayId = info->id;
+ state.isSecure = true; // All physical displays are currently considered secure.
+ state.displayName = info->name;
+ mCurrentState.displays.add(mPhysicalDisplayTokens[info->id], state);
+ mInterceptor->saveDisplayCreation(state);
}
} else {
- ALOGV("Removing built in display %d", displayType);
+ ALOGV("Removing display %s", to_string(info->id).c_str());
- ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]);
- if (idx >= 0) {
- const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
- mInterceptor->saveDisplayDeletion(info.displayId);
- mCurrentState.displays.removeItemsAt(idx);
+ ssize_t index = mCurrentState.displays.indexOfKey(mPhysicalDisplayTokens[info->id]);
+ if (index >= 0) {
+ const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+ mInterceptor->saveDisplayDeletion(state.sequenceId);
+ mCurrentState.displays.removeItemsAt(index);
}
- mBuiltinDisplays[displayType].clear();
+ mPhysicalDisplayTokens.erase(info->id);
}
processDisplayChangesLocked();
@@ -2361,74 +2529,63 @@
mPendingHotplugEvents.clear();
}
-sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
- const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
- const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
- bool hasWideColorGamut = false;
- std::unordered_map<ColorMode, std::vector<RenderIntent>> hwcColorModes;
- HdrCapabilities hdrCapabilities;
- int32_t supportedPerFrameMetadata = 0;
+void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
+ mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected);
+ mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected);
+}
- if (hasWideColorDisplay && hwcId >= 0) {
- std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
+sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
+ const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+ const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
+ const sp<IGraphicBufferProducer>& producer) {
+ DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
+ creationArgs.sequenceId = state.sequenceId;
+ creationArgs.isVirtual = state.isVirtual();
+ creationArgs.isSecure = state.isSecure;
+ creationArgs.displaySurface = dispSurface;
+ creationArgs.hasWideColorGamut = false;
+ creationArgs.supportedPerFrameMetadata = 0;
+
+ const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
+ creationArgs.isPrimary = isInternalDisplay;
+
+ if (useColorManagement && displayId) {
+ std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
for (ColorMode colorMode : modes) {
- switch (colorMode) {
- case ColorMode::DISPLAY_P3:
- case ColorMode::ADOBE_RGB:
- case ColorMode::DCI_P3:
- hasWideColorGamut = true;
- break;
- default:
- break;
+ if (isWideColorMode(colorMode)) {
+ creationArgs.hasWideColorGamut = true;
}
- std::vector<RenderIntent> renderIntents = getHwComposer().getRenderIntents(hwcId,
- colorMode);
- hwcColorModes.emplace(colorMode, renderIntents);
+ std::vector<RenderIntent> renderIntents =
+ getHwComposer().getRenderIntents(*displayId, colorMode);
+ creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
}
}
- if (hwcId >= 0) {
- getHwComposer().getHdrCapabilities(hwcId, &hdrCapabilities);
- supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(hwcId);
+ if (displayId) {
+ getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
+ creationArgs.supportedPerFrameMetadata =
+ getHwComposer().getSupportedPerFrameMetadata(*displayId);
}
- auto nativeWindowSurface = mCreateNativeWindowSurface(producer);
+ auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
auto nativeWindow = nativeWindowSurface->getNativeWindow();
-
- /*
- * Create our display's surface
- */
- std::unique_ptr<RE::Surface> renderSurface = getRenderEngine().createSurface();
- renderSurface->setCritical(state.type == DisplayDevice::DISPLAY_PRIMARY);
- renderSurface->setAsync(state.type >= DisplayDevice::DISPLAY_VIRTUAL);
- renderSurface->setNativeWindow(nativeWindow.get());
- const int displayWidth = renderSurface->queryWidth();
- const int displayHeight = renderSurface->queryHeight();
+ creationArgs.nativeWindow = nativeWindow;
// Make sure that composition can never be stalled by a virtual display
// consumer that isn't processing buffers fast enough. We have to do this
- // in two places:
- // * Here, in case the display is composed entirely by HWC.
- // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
- // window's swap interval in eglMakeCurrent, so they'll override the
- // interval we set here.
- if (state.type >= DisplayDevice::DISPLAY_VIRTUAL) {
+ // here, in case the display is composed entirely by HWC.
+ if (state.isVirtual()) {
nativeWindow->setSwapInterval(nativeWindow.get(), 0);
}
- const int displayInstallOrientation = state.type == DisplayDevice::DISPLAY_PRIMARY ?
- primaryDisplayOrientation : DisplayState::eOrientationDefault;
+ creationArgs.displayInstallOrientation =
+ isInternalDisplay ? primaryDisplayOrientation : DisplayState::eOrientationDefault;
// virtual displays are always considered enabled
- auto initialPowerMode = (state.type >= DisplayDevice::DISPLAY_VIRTUAL) ? HWC_POWER_MODE_NORMAL
- : HWC_POWER_MODE_OFF;
+ creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
- sp<DisplayDevice> hw =
- new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow,
- dispSurface, std::move(renderSurface), displayWidth, displayHeight,
- displayInstallOrientation, hasWideColorGamut, hdrCapabilities,
- supportedPerFrameMetadata, hwcColorModes, initialPowerMode);
+ sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs));
if (maxFrameBufferAcquiredBuffers >= 3) {
nativeWindowSurface->preallocateBuffers();
@@ -2436,20 +2593,22 @@
ColorMode defaultColorMode = ColorMode::NATIVE;
Dataspace defaultDataSpace = Dataspace::UNKNOWN;
- if (hasWideColorGamut) {
+ if (display->hasWideColorGamut()) {
defaultColorMode = ColorMode::SRGB;
- defaultDataSpace = Dataspace::SRGB;
+ defaultDataSpace = Dataspace::V0_SRGB;
}
- setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace,
- RenderIntent::COLORIMETRIC);
- if (state.type < DisplayDevice::DISPLAY_VIRTUAL) {
- hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type));
+ display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
+ RenderIntent::COLORIMETRIC);
+ if (!state.isVirtual()) {
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
}
- hw->setLayerStack(state.layerStack);
- hw->setProjection(state.orientation, state.viewport, state.frame);
- hw->setDisplayName(state.displayName);
- return hw;
+ display->setLayerStack(state.layerStack);
+ display->setProjection(state.orientation, state.viewport, state.frame);
+ display->setDisplayName(state.displayName);
+
+ return display;
}
void SurfaceFlinger::processDisplayChangesLocked() {
@@ -2471,20 +2630,22 @@
const ssize_t j = curr.indexOfKey(draw.keyAt(i));
if (j < 0) {
// in drawing state but not in current state
- // Call makeCurrent() on the primary display so we can
- // be sure that nothing associated with this display
- // is current.
- const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
- if (defaultDisplay != nullptr) defaultDisplay->makeCurrent();
- sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
- if (hw != nullptr) hw->disconnect(getHwComposer());
- if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
- mEventThread->onHotplugReceived(draw[i].type, false);
- mDisplays.removeItem(draw.keyAt(i));
+ if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
+ // Save display ID before disconnecting.
+ const auto displayId = display->getId();
+ display->disconnect();
+
+ if (!display->isVirtual()) {
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, false);
+ }
+ }
+
+ mDisplays.erase(draw.keyAt(i));
} else {
// this display is in both lists. see if something changed.
const DisplayDeviceState& state(curr[j]);
- const wp<IBinder>& display(curr.keyAt(j));
+ const wp<IBinder>& displayToken = curr.keyAt(j);
const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
if (state_binder != draw_binder) {
@@ -2492,26 +2653,26 @@
// recreating the DisplayDevice, so we just remove it
// from the drawing state, so that it get re-added
// below.
- sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
- if (hw != nullptr) hw->disconnect(getHwComposer());
- mDisplays.removeItem(display);
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ display->disconnect();
+ }
+ mDisplays.erase(displayToken);
mDrawingState.displays.removeItemsAt(i);
dc--;
// at this point we must loop to the next item
continue;
}
- const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
- if (disp != nullptr) {
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
if (state.layerStack != draw[i].layerStack) {
- disp->setLayerStack(state.layerStack);
+ display->setLayerStack(state.layerStack);
}
if ((state.orientation != draw[i].orientation) ||
(state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
- disp->setProjection(state.orientation, state.viewport, state.frame);
+ display->setProjection(state.orientation, state.viewport, state.frame);
}
if (state.width != draw[i].width || state.height != draw[i].height) {
- disp->setDisplaySize(state.width, state.height);
+ display->setDisplaySize(state.width, state.height);
}
}
}
@@ -2524,20 +2685,20 @@
if (draw.indexOfKey(curr.keyAt(i)) < 0) {
const DisplayDeviceState& state(curr[i]);
- sp<DisplaySurface> dispSurface;
+ sp<compositionengine::DisplaySurface> dispSurface;
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferProducer> bqProducer;
sp<IGraphicBufferConsumer> bqConsumer;
- mCreateBufferQueue(&bqProducer, &bqConsumer, false);
+ getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
- int32_t hwcId = -1;
- if (state.isVirtualDisplay()) {
+ std::optional<DisplayId> displayId;
+ if (state.isVirtual()) {
// Virtual displays without a surface are dormant:
// they have external state (layer stack, projection,
// etc.) but no internal state (i.e. a DisplayDevice).
if (state.surface != nullptr) {
// Allow VR composer to use virtual displays.
- if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
+ if (mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer()) {
int width = 0;
int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
@@ -2549,13 +2710,14 @@
ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
auto format = static_cast<ui::PixelFormat>(intFormat);
- getBE().mHwc->allocateVirtualDisplay(width, height, &format, &hwcId);
+ displayId =
+ getHwComposer().allocateVirtualDisplay(width, height, &format);
}
// TODO: Plumb requested format back up to consumer
sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(*getBE().mHwc, hwcId, state.surface,
+ new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
bqProducer, bqConsumer,
state.displayName);
@@ -2568,18 +2730,20 @@
"surface is provided (%p), ignoring it",
state.surface.get());
- hwcId = state.type;
- dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
+ displayId = state.displayId;
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
producer = bqProducer;
}
- const wp<IBinder>& display(curr.keyAt(i));
+ const wp<IBinder>& displayToken = curr.keyAt(i);
if (dispSurface != nullptr) {
- mDisplays.add(display,
- setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface,
- producer));
- if (!state.isVirtualDisplay()) {
- mEventThread->onHotplugReceived(state.type, true);
+ mDisplays.emplace(displayToken,
+ setupNewDisplayDeviceInternal(displayToken, displayId, state,
+ dispSurface, producer));
+ if (!state.isVirtual()) {
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, true);
}
}
}
@@ -2609,6 +2773,10 @@
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
mVisibleRegionsDirty = true;
+
+ if (flags & Layer::eInputInfoChanged) {
+ mInputInfoChanged = true;
+ }
});
}
@@ -2641,7 +2809,7 @@
// happened yet, so we must use the current state layer list
// (soon to become the drawing state list).
//
- sp<const DisplayDevice> disp;
+ sp<const DisplayDevice> hintDisplay;
uint32_t currentlayerStack = 0;
bool first = true;
mCurrentState.traverseInZOrder([&](Layer* layer) {
@@ -2654,34 +2822,35 @@
// figure out if this layerstack is mirrored
// (more than one display) if so, pick the default display,
// if not, pick the only display it's on.
- disp.clear();
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- sp<const DisplayDevice> hw(mDisplays[dpy]);
- if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
- if (disp == nullptr) {
- disp = std::move(hw);
- } else {
- disp = nullptr;
+ hintDisplay = nullptr;
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getCompositionDisplay()
+ ->belongsInOutput(layer->getLayerStack(),
+ layer->getPrimaryDisplayOnly())) {
+ if (hintDisplay) {
+ hintDisplay = nullptr;
break;
+ } else {
+ hintDisplay = display;
}
}
}
}
- if (disp == nullptr) {
+ if (!hintDisplay) {
// NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
// redraw after transform hint changes. See bug 8508397.
// could be null when this layer is using a layerStack
// that is not visible on any display. Also can occur at
// screen off/on times.
- disp = getDefaultDisplayDeviceLocked();
+ hintDisplay = getDefaultDisplayDeviceLocked();
}
- // disp can be null if there is no display available at all to get
+ // could be null if there is no display available at all to get
// the transform hint from.
- if (disp != nullptr) {
- layer->updateTransformHint(disp);
+ if (hintDisplay) {
+ layer->updateTransformHint(hintDisplay);
}
first = false;
@@ -2707,35 +2876,90 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (mLayersPendingRemoval.indexOf(layer) >= 0) {
// this layer is not visible anymore
- // TODO: we could traverse the tree from front to back and
- // compute the actual visible region
- // TODO: we could cache the transformed region
Region visibleReg;
- visibleReg.set(layer->computeScreenBounds());
+ visibleReg.set(layer->getScreenBounds());
invalidateLayerStack(layer, visibleReg);
}
});
}
+ commitInputWindowCommands();
commitTransaction();
+}
- updateCursorAsync();
+void SurfaceFlinger::updateInputFlinger() {
+ ATRACE_CALL();
+ if (!mInputFlinger) {
+ return;
+ }
+
+ if (mVisibleRegionsDirty || mInputInfoChanged) {
+ mInputInfoChanged = false;
+ updateInputWindowInfo();
+ } else if (mInputWindowCommands.syncInputWindows) {
+ // If the caller requested to sync input windows, but there are no
+ // changes to input windows, notify immediately.
+ setInputWindowsFinished();
+ }
+
+ executeInputWindowCommands();
+}
+
+void SurfaceFlinger::updateInputWindowInfo() {
+ std::vector<InputWindowInfo> inputHandles;
+
+ mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+ if (layer->hasInput()) {
+ // When calculating the screen bounds we ignore the transparent region since it may
+ // result in an unwanted offset.
+ inputHandles.push_back(layer->fillInputInfo());
+ }
+ });
+
+ mInputFlinger->setInputWindows(inputHandles,
+ mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
+ : nullptr);
+}
+
+void SurfaceFlinger::commitInputWindowCommands() {
+ mInputWindowCommands.merge(mPendingInputWindowCommands);
+ mPendingInputWindowCommands.clear();
+}
+
+void SurfaceFlinger::executeInputWindowCommands() {
+ for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) {
+ if (transferTouchFocusCommand.fromToken != nullptr &&
+ transferTouchFocusCommand.toToken != nullptr &&
+ transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) {
+ mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken,
+ transferTouchFocusCommand.toToken);
+ }
+ }
+
+ mInputWindowCommands.clear();
}
void SurfaceFlinger::updateCursorAsync()
{
- for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- auto& displayDevice = mDisplays[displayId];
- if (displayDevice->getHwcDisplayId() < 0) {
+ for (const auto& [token, display] : mDisplays) {
+ if (!display->getId()) {
continue;
}
- for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- layer->updateCursorPosition(displayDevice);
+ for (auto& layer : display->getVisibleLayersSortedByZ()) {
+ layer->updateCursorPosition(display);
}
}
}
+void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
+ if (layer->hasReadyFrame()) {
+ bool ignored = false;
+ layer->latchBuffer(ignored, systemTime());
+ }
+ layer->releasePendingBuffer(systemTime());
+}
+
void SurfaceFlinger::commitTransaction()
{
if (!mLayersPendingRemoval.isEmpty()) {
@@ -2743,7 +2967,11 @@
for (const auto& l : mLayersPendingRemoval) {
recordBufferingStats(l->getName().string(),
l->getOccupancyHistory(true));
- l->onRemoved();
+
+ // Ensure any buffers set to display on any children are released.
+ if (l->isRemovedFromCurrentState()) {
+ latchAndReleaseBuffer(l);
+ }
}
mLayersPendingRemoval.clear();
}
@@ -2752,24 +2980,46 @@
// we composite should be considered an animation as well.
mAnimCompositionPending = mAnimTransactionPending;
- mDrawingState = mCurrentState;
- // clear the "changed" flags in current state
- mCurrentState.colorMatrixChanged = false;
+ withTracingLock([&]() {
+ mDrawingState = mCurrentState;
+ // clear the "changed" flags in current state
+ mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverseInZOrder([](Layer* layer) {
- layer->commitChildList();
+ mDrawingState.traverseInZOrder([](Layer* layer) { layer->commitChildList(); });
});
+
mTransactionPending = false;
mAnimTransactionPending = false;
mTransactionCV.broadcast();
}
+void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
+ if (mTracingEnabledChanged) {
+ mTracingEnabled = mTracing.isEnabled();
+ mTracingEnabledChanged = false;
+ }
+
+ // Synchronize with Tracing thread
+ std::unique_lock<std::mutex> lock;
+ if (mTracingEnabled) {
+ lock = std::unique_lock<std::mutex>(mDrawingStateLock);
+ }
+
+ lockedOperation();
+
+ // Synchronize with Tracing thread
+ if (mTracingEnabled) {
+ lock.unlock();
+ }
+}
+
void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
- Region& outDirtyRegion, Region& outOpaqueRegion)
-{
+ Region& outDirtyRegion, Region& outOpaqueRegion) {
ATRACE_CALL();
ALOGV("computeVisibleRegions");
+ auto display = displayDevice->getCompositionDisplay();
+
Region aboveOpaqueLayers;
Region aboveCoveredLayers;
Region dirty;
@@ -2781,8 +3031,9 @@
const Layer::State& s(layer->getDrawingState());
// only consider the layers on the given layer stack
- if (!layer->belongsToDisplay(displayDevice->getLayerStack(), displayDevice->isPrimary()))
+ if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
return;
+ }
/*
* opaqueRegion: area of a surface that is fully opaque.
@@ -2817,15 +3068,16 @@
// handle hidden surfaces by setting the visible region to empty
if (CC_LIKELY(layer->isVisible())) {
const bool translucent = !layer->isOpaque(s);
- Rect bounds(layer->computeScreenBounds());
+ Rect bounds(layer->getScreenBounds());
+
visibleRegion.set(bounds);
- Transform tr = layer->getTransform();
+ ui::Transform tr = layer->getTransform();
if (!visibleRegion.isEmpty()) {
// Remove the transparent area from the visible region
if (translucent) {
if (tr.preserveRects()) {
// transform the transparent region
- transparentRegion = tr.transform(s.activeTransparentRegion);
+ transparentRegion = tr.transform(layer->getActiveTransparentRegion(s));
} else {
// transformation too complex, can't do the
// transparent region optimization.
@@ -2836,7 +3088,8 @@
// compute the opaque region
const int32_t layerOrientation = tr.getOrientation();
if (layer->getAlpha() == 1.0f && !translucent &&
- ((layerOrientation & Transform::ROT_INVALID) == false)) {
+ layer->getRoundedCornerState().radius == 0.0f &&
+ ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
opaqueRegion = visibleRegion;
}
@@ -2902,10 +3155,10 @@
}
void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<DisplayDevice>& hw(mDisplays[dpy]);
- if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
- hw->dirtyRegion.orSelf(dirty);
+ for (const auto& [token, displayDevice] : mDisplays) {
+ auto display = displayDevice->getCompositionDisplay();
+ if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+ display->editState().dirtyRegion.orSelf(dirty);
}
}
}
@@ -2930,9 +3183,11 @@
// Display is now waiting on Layer 1's frame, which is behind layer 0's
// second frame. But layer 0's second frame could be waiting on display.
mDrawingState.traverseInZOrder([&](Layer* layer) {
- if (layer->hasQueuedFrame()) {
+ if (layer->hasReadyFrame()) {
frameQueued = true;
- if (layer->shouldPresentNow(mPrimaryDispSync)) {
+ nsecs_t expectedPresentTime;
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.push_back(layer);
} else {
layer->useEmptyDamage();
@@ -2942,12 +3197,19 @@
}
});
- for (auto& layer : mLayersWithQueuedFrames) {
- const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
- layer->useSurfaceDamage();
- invalidateLayerStack(layer, dirty);
- if (layer->isBufferLatched()) {
- newDataLatched = true;
+ if (!mLayersWithQueuedFrames.empty()) {
+ // mStateLock is needed for latchBuffer as LayerRejecter::reject()
+ // writes to Layer current state. See also b/119481871
+ Mutex::Autolock lock(mStateLock);
+
+ for (auto& layer : mLayersWithQueuedFrames) {
+ if (layer->latchBuffer(visibleRegions, latchTime)) {
+ mLayersPendingRefresh.push_back(layer);
+ }
+ layer->useSurfaceDamage();
+ if (layer->isBufferLatched()) {
+ newDataLatched = true;
+ }
}
}
@@ -2975,121 +3237,102 @@
mGeometryInvalid = true;
}
-
-void SurfaceFlinger::doDisplayComposition(
- const sp<const DisplayDevice>& displayDevice,
- const Region& inDirtyRegion)
-{
+void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
+ const Region& inDirtyRegion) {
+ auto display = displayDevice->getCompositionDisplay();
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- bool isHwcDisplay = displayDevice->getHwcDisplayId() >= 0;
- if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
+ if (!displayDevice->getId() && inDirtyRegion.isEmpty()) {
ALOGV("Skipping display composition");
return;
}
ALOGV("doDisplayComposition");
- if (!doComposeSurfaces(displayDevice)) return;
+ base::unique_fd readyFence;
+ if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
// swap buffers (presentation)
- displayDevice->swapBuffers(getHwComposer());
+ display->getRenderSurface()->queueBuffer(std::move(readyFence));
}
-bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDevice)
-{
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
+ const Region& debugRegion, base::unique_fd* readyFence) {
+ ATRACE_CALL();
ALOGV("doComposeSurfaces");
- const Region bounds(displayDevice->bounds());
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
+ const auto displayId = display->getId();
+ auto& renderEngine = getRenderEngine();
+ const bool supportProtectedContent =
+ mDebugEnableProtectedContent && renderEngine.supportsProtectedContent();
+
+ const Region bounds(displayState.bounds);
const DisplayRenderArea renderArea(displayDevice);
- const auto hwcId = displayDevice->getHwcDisplayId();
- const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
+ const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
ATRACE_INT("hasClientComposition", hasClientComposition);
+ mat4 colorMatrix;
bool applyColorMatrix = false;
- bool needsEnhancedColorMatrix = false;
+
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<renderengine::LayerSettings> clientCompositionLayers;
+ sp<GraphicBuffer> buf;
+ base::unique_fd fd;
if (hasClientComposition) {
ALOGV("hasClientComposition");
- Dataspace outputDataspace = Dataspace::UNKNOWN;
- if (displayDevice->hasWideColorGamut()) {
- outputDataspace = displayDevice->getCompositionDataSpace();
- }
- getBE().mRenderEngine->setOutputDataSpace(outputDataspace);
- getBE().mRenderEngine->setDisplayMaxLuminance(
- displayDevice->getHdrCapabilities().getDesiredMaxLuminance());
-
- const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
- const bool skipClientColorTransform = getBE().mHwc->hasCapability(
- HWC2::Capability::SkipClientColorTransform);
-
- mat4 colorMatrix;
- applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
- if (applyColorMatrix) {
- colorMatrix = mDrawingState.colorMatrix;
- }
-
- // The current enhanced saturation matrix is designed to enhance Display P3,
- // thus we only apply this matrix when the render intent is not colorimetric
- // and the output color space is Display P3.
- needsEnhancedColorMatrix =
- (displayDevice->getActiveRenderIntent() >= RenderIntent::ENHANCE &&
- outputDataspace == Dataspace::DISPLAY_P3);
- if (needsEnhancedColorMatrix) {
- colorMatrix *= mEnhancedSaturationMatrix;
- }
-
- getRenderEngine().setupColorTransform(colorMatrix);
-
- if (!displayDevice->makeCurrent()) {
- ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
- displayDevice->getDisplayName().string());
- getRenderEngine().resetCurrentSurface();
-
- // |mStateLock| not needed as we are on the main thread
- if(!getDefaultDisplayDeviceLocked()->makeCurrent()) {
- ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting.");
+ if (displayDevice->isPrimary() && supportProtectedContent) {
+ bool needsProtected = false;
+ for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+ // If the layer is a protected layer, mark protected context is needed.
+ if (layer->isProtected()) {
+ needsProtected = true;
+ break;
+ }
}
+ if (needsProtected != renderEngine.isProtected() &&
+ renderEngine.useProtectedContext(needsProtected)) {
+ display->getRenderSurface()->setProtected(needsProtected);
+ }
+ }
+
+ buf = display->getRenderSurface()->dequeueBuffer(&fd);
+
+ if (buf == nullptr) {
+ ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
+ "client composition for this frame",
+ displayDevice->getDisplayName().c_str());
return false;
}
- // Never touch the framebuffer if we don't have any framebuffer layers
- if (hasDeviceComposition) {
- // when using overlays, we assume a fully transparent framebuffer
- // NOTE: we could reduce how much we need to clear, for instance
- // remove where there are opaque FB layers. however, on some
- // GPUs doing a "clean slate" clear might be more efficient.
- // We'll revisit later if needed.
- getBE().mRenderEngine->clearWithColor(0, 0, 0, 0);
- } else {
- // we start with the whole screen area and remove the scissor part
- // we're left with the letterbox region
- // (common case is that letterbox ends-up being empty)
- const Region letterbox(bounds.subtract(displayDevice->getScissor()));
+ clientCompositionDisplay.physicalDisplay = displayState.scissor;
+ clientCompositionDisplay.clip = displayState.scissor;
+ const ui::Transform& displayTransform = displayState.transform;
+ clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
- // compute the area to clear
- Region region(displayDevice->undefinedRegion.merge(letterbox));
-
- // screen is already cleared here
- if (!region.isEmpty()) {
- // can happen with SurfaceView
- drawWormhole(displayDevice, region);
- }
+ const auto* profile = display->getDisplayColorProfile();
+ Dataspace outputDataspace = Dataspace::UNKNOWN;
+ if (profile->hasWideColorGamut()) {
+ outputDataspace = displayState.dataspace;
}
+ clientCompositionDisplay.outputDataspace = outputDataspace;
+ clientCompositionDisplay.maxLuminance =
+ profile->getHdrCapabilities().getDesiredMaxLuminance();
- const Rect& bounds(displayDevice->getBounds());
- const Rect& scissor(displayDevice->getScissor());
- if (scissor != bounds) {
- // scissor doesn't match the screen's dimensions, so we
- // need to clear everything outside of it and enable
- // the GL scissor so we don't draw anything where we shouldn't
+ const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
+ const bool skipClientColorTransform =
+ getHwComposer()
+ .hasDisplayCapability(displayId,
+ HWC2::DisplayCapability::SkipClientColorTransform);
- // enable scissor for this frame
- const uint32_t height = displayDevice->getHeight();
- getBE().mRenderEngine->setScissor(scissor.left, height - scissor.bottom,
- scissor.getWidth(), scissor.getHeight());
+ // Compute the global color transform matrix.
+ applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
+ if (applyColorMatrix) {
+ clientCompositionDisplay.colorTransform = colorMatrix;
}
}
@@ -3098,32 +3341,50 @@
*/
ALOGV("Rendering client layers");
- const Transform& displayTransform = displayDevice->getTransform();
bool firstLayer = true;
+ Region clearRegion = Region::INVALID_REGION;
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
- const Region clip(bounds.intersect(
- displayTransform.transform(layer->visibleRegion)));
+ const Region viewportRegion(displayState.viewport);
+ const Region clip(viewportRegion.intersect(layer->visibleRegion));
ALOGV("Layer: %s", layer->getName().string());
- ALOGV(" Composition type: %s",
- to_string(layer->getCompositionType(hwcId)).c_str());
+ ALOGV(" Composition type: %s", toString(layer->getCompositionType(displayDevice)).c_str());
if (!clip.isEmpty()) {
- switch (layer->getCompositionType(hwcId)) {
- case HWC2::Composition::Cursor:
- case HWC2::Composition::Device:
- case HWC2::Composition::Sideband:
- case HWC2::Composition::SolidColor: {
+ switch (layer->getCompositionType(displayDevice)) {
+ case Hwc2::IComposerClient::Composition::CURSOR:
+ case Hwc2::IComposerClient::Composition::DEVICE:
+ case Hwc2::IComposerClient::Composition::SIDEBAND:
+ case Hwc2::IComposerClient::Composition::SOLID_COLOR: {
+ LOG_ALWAYS_FATAL_IF(!displayId);
const Layer::State& state(layer->getDrawingState());
- if (layer->getClearClientTarget(hwcId) && !firstLayer &&
- layer->isOpaque(state) && (state.color.a == 1.0f)
- && hasClientComposition) {
+ if (layer->getClearClientTarget(displayDevice) && !firstLayer &&
+ layer->isOpaque(state) && (layer->getAlpha() == 1.0f) &&
+ layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
- layer->clearWithOpenGL(renderArea);
+ renderengine::LayerSettings layerSettings;
+ Region dummyRegion;
+ bool prepared =
+ layer->prepareClientLayer(renderArea, clip, dummyRegion,
+ supportProtectedContent, layerSettings);
+
+ if (prepared) {
+ layerSettings.source.buffer.buffer = nullptr;
+ layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
+ layerSettings.alpha = half(0.0);
+ layerSettings.disableBlending = true;
+ clientCompositionLayers.push_back(layerSettings);
+ }
}
break;
}
- case HWC2::Composition::Client: {
- layer->draw(renderArea, clip);
+ case Hwc2::IComposerClient::Composition::CLIENT: {
+ renderengine::LayerSettings layerSettings;
+ bool prepared =
+ layer->prepareClientLayer(renderArea, clip, clearRegion,
+ supportProtectedContent, layerSettings);
+ if (prepared) {
+ clientCompositionLayers.push_back(layerSettings);
+ }
break;
}
default:
@@ -3135,26 +3396,52 @@
firstLayer = false;
}
- if (applyColorMatrix || needsEnhancedColorMatrix) {
- getRenderEngine().setupColorTransform(mat4());
- }
+ // Perform some cleanup steps if we used client composition.
+ if (hasClientComposition) {
+ clientCompositionDisplay.clearRegion = clearRegion;
- // disable scissor at the end of the frame
- getBE().mRenderEngine->disableScissor();
+ // We boost GPU frequency here because there will be color spaces conversion
+ // and it's expensive. We boost the GPU frequency so that GPU composition can
+ // finish in time. We must reset GPU frequency afterwards, because high frequency
+ // consumes extra battery.
+ const bool expensiveRenderingExpected =
+ clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3;
+ if (expensiveRenderingExpected && displayId) {
+ mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true);
+ }
+ if (!debugRegion.isEmpty()) {
+ Region::const_iterator it = debugRegion.begin();
+ Region::const_iterator end = debugRegion.end();
+ while (it != end) {
+ const Rect& rect = *it++;
+ renderengine::LayerSettings layerSettings;
+ layerSettings.source.buffer.buffer = nullptr;
+ layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
+ layerSettings.geometry.boundaries = rect.toFloatRect();
+ layerSettings.alpha = half(1.0);
+ clientCompositionLayers.push_back(layerSettings);
+ }
+ }
+ renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
+ buf->getNativeBuffer(), std::move(fd), readyFence);
+ if (expensiveRenderingExpected && displayId) {
+ mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false);
+ }
+ }
return true;
}
-void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const {
- const int32_t height = displayDevice->getHeight();
+void SurfaceFlinger::drawWormhole(const Region& region) const {
auto& engine(getRenderEngine());
- engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
+ engine.fillRegionWithColor(region, 0, 0, 0, 0);
}
status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc,
const sp<Layer>& lbc,
- const sp<Layer>& parent)
+ const sp<Layer>& parent,
+ bool addToCurrentState)
{
// add this layer to the current state list
{
@@ -3164,13 +3451,14 @@
MAX_LAYERS);
return NO_MEMORY;
}
- if (parent == nullptr) {
+ if (parent == nullptr && addToCurrentState) {
mCurrentState.layersSortedByZ.add(lbc);
+ } else if (parent == nullptr) {
+ lbc->onRemovedFromCurrentState();
+ } else if (parent->isRemovedFromCurrentState()) {
+ parent->addChild(lbc);
+ lbc->onRemovedFromCurrentState();
} else {
- if (parent->isPendingRemoval()) {
- ALOGE("addClientLayer called with a removed parent");
- return NAME_NOT_FOUND;
- }
parent->addChild(lbc);
}
@@ -3183,7 +3471,6 @@
mMaxGraphicBufferProducerListSize, mNumLayers);
}
mLayersAdded = true;
- mNumLayers++;
}
// attach this layer to the client
@@ -3192,75 +3479,21 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) {
- Mutex::Autolock _l(mStateLock);
- return removeLayerLocked(mStateLock, layer, topLevelOnly);
-}
-
-status_t SurfaceFlinger::removeLayerLocked(const Mutex&, const sp<Layer>& layer,
- bool topLevelOnly) {
- if (layer->isPendingRemoval()) {
- return NO_ERROR;
- }
-
- const auto& p = layer->getParent();
- ssize_t index;
- if (p != nullptr) {
- if (topLevelOnly) {
- return NO_ERROR;
- }
-
- sp<Layer> ancestor = p;
- while (ancestor->getParent() != nullptr) {
- ancestor = ancestor->getParent();
- }
- if (mCurrentState.layersSortedByZ.indexOf(ancestor) < 0) {
- ALOGE("removeLayer called with a layer whose parent has been removed");
- return NAME_NOT_FOUND;
- }
-
- index = p->removeChild(layer);
- } else {
- index = mCurrentState.layersSortedByZ.remove(layer);
- }
-
- // As a matter of normal operation, the LayerCleaner will produce a second
- // attempt to remove the surface. The Layer will be kept alive in mDrawingState
- // so we will succeed in promoting it, but it's already been removed
- // from mCurrentState. As long as we can find it in mDrawingState we have no problem
- // otherwise something has gone wrong and we are leaking the layer.
- if (index < 0 && mDrawingState.layersSortedByZ.indexOf(layer) < 0) {
- ALOGE("Failed to find layer (%s) in layer parent (%s).",
- layer->getName().string(),
- (p != nullptr) ? p->getName().string() : "no-parent");
- return BAD_VALUE;
- } else if (index < 0) {
- return NO_ERROR;
- }
-
- layer->onRemovedFromCurrentState();
- mLayersPendingRemoval.add(layer);
- mLayersRemoved = true;
- mNumLayers -= 1 + layer->getChildrenCount();
- setTransactionFlags(eTransactionNeeded);
- return NO_ERROR;
-}
-
uint32_t SurfaceFlinger::peekTransactionFlags() {
- return android_atomic_release_load(&mTransactionFlags);
+ return mTransactionFlags;
}
uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) {
- return android_atomic_and(~flags, &mTransactionFlags) & flags;
+ return mTransactionFlags.fetch_and(~flags) & flags;
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, VSyncModulator::TransactionStart::NORMAL);
+ return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
- VSyncModulator::TransactionStart transactionStart) {
- uint32_t old = android_atomic_or(flags, &mTransactionFlags);
+ Scheduler::TransactionStart transactionStart) {
+ uint32_t old = mTransactionFlags.fetch_or(flags);
mVsyncModulator.setTransactionStart(transactionStart);
if ((old & flags)==0) { // wake the server up
signalTransaction();
@@ -3268,6 +3501,28 @@
return old;
}
+bool SurfaceFlinger::flushTransactionQueues() {
+ Mutex::Autolock _l(mStateLock);
+ auto it = mTransactionQueues.begin();
+ while (it != mTransactionQueues.end()) {
+ auto& [applyToken, transactionQueue] = *it;
+
+ while (!transactionQueue.empty()) {
+ const auto& [states, displays, flags, desiredPresentTime, postTime, privileged] =
+ transactionQueue.front();
+ if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+ break;
+ }
+ applyTransactionState(states, displays, flags, mPendingInputWindowCommands,
+ desiredPresentTime, postTime, privileged);
+ transactionQueue.pop();
+ }
+
+ it = (transactionQueue.empty()) ? mTransactionQueues.erase(it) : std::next(it, 1);
+ }
+ return mTransactionQueues.empty();
+}
+
bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
for (const ComposerState& state : states) {
// Here we need to check that the interface we're given is indeed
@@ -3290,19 +3545,65 @@
return false;
}
-void SurfaceFlinger::setTransactionState(
- const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays,
- uint32_t flags)
-{
+bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+ const Vector<ComposerState>& states) {
+ nsecs_t expectedPresentTime = mScheduler->expectedPresentTime();
+ // Do not present if the desiredPresentTime has not passed unless it is more than one second
+ // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
+ if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+ desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ return false;
+ }
+
+ for (const ComposerState& state : states) {
+ const layer_state_t& s = state.state;
+ if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
+ continue;
+ }
+ if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime) {
ATRACE_CALL();
+
+ const int64_t postTime = systemTime();
+
+ bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
+
Mutex::Autolock _l(mStateLock);
- uint32_t transactionFlags = 0;
if (containsAnyInvalidClientState(states)) {
return;
}
+ // If its TransactionQueue already has a pending TransactionState or if it is pending
+ if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
+ !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+ mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
+ postTime, privileged);
+ setTransactionFlags(eTransactionNeeded);
+ return;
+ }
+
+ applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
+ postTime, privileged);
+}
+
+void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands,
+ const int64_t desiredPresentTime, const int64_t postTime,
+ bool privileged) {
+ uint32_t transactionFlags = 0;
+
if (flags & eAnimation) {
// For window updates that are part of an animation we must wait for
// previous animation "frames" to be handled.
@@ -3323,17 +3624,17 @@
transactionFlags |= setDisplayStateLocked(display);
}
+ uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- transactionFlags |= setClientStateLocked(state);
+ clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged);
}
+ // If the state doesn't require a traversal and there are callbacks, send them now
+ if (!(clientStateFlags & eTraversalNeeded)) {
+ mTransactionCompletedThread.sendCallbacks();
+ }
+ transactionFlags |= clientStateFlags;
- // Iterate through all layers again to determine if any need to be destroyed. Marking layers
- // as destroyed should only occur after setting all other states. This is to allow for a
- // child re-parent to happen before marking its original parent as destroyed (which would
- // then mark the child as destroyed).
- for (const ComposerState& state : states) {
- setDestroyStateLocked(state);
- }
+ transactionFlags |= addInputWindowCommands(inputWindowCommands);
// If a synchronous transaction is explicitly requested without any changes, force a transaction
// anyway. This can be used as a flush mechanism for previous async transactions.
@@ -3350,9 +3651,8 @@
}
// this triggers the transaction
- const auto start = (flags & eEarlyWakeup)
- ? VSyncModulator::TransactionStart::EARLY
- : VSyncModulator::TransactionStart::NORMAL;
+ const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
+ : Scheduler::TransactionStart::NORMAL;
setTransactionFlags(transactionFlags, start);
// if this is a synchronous transaction, wait for it to take effect
@@ -3363,82 +3663,84 @@
if (flags & eAnimation) {
mAnimTransactionPending = true;
}
- while (mTransactionPending) {
+
+ mPendingSyncInputWindows = mPendingInputWindowCommands.syncInputWindows;
+ while (mTransactionPending || mPendingSyncInputWindows) {
status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
if (CC_UNLIKELY(err != NO_ERROR)) {
// just in case something goes wrong in SF, return to the
// called after a few seconds.
ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
mTransactionPending = false;
+ mPendingSyncInputWindows = false;
break;
}
}
}
}
-uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s)
-{
- ssize_t dpyIdx = mCurrentState.displays.indexOfKey(s.token);
- if (dpyIdx < 0)
- return 0;
+uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
+ const ssize_t index = mCurrentState.displays.indexOfKey(s.token);
+ if (index < 0) return 0;
uint32_t flags = 0;
- DisplayDeviceState& disp(mCurrentState.displays.editValueAt(dpyIdx));
- if (disp.isValid()) {
- const uint32_t what = s.what;
- if (what & DisplayState::eSurfaceChanged) {
- if (IInterface::asBinder(disp.surface) != IInterface::asBinder(s.surface)) {
- disp.surface = s.surface;
- flags |= eDisplayTransactionNeeded;
- }
- }
- if (what & DisplayState::eLayerStackChanged) {
- if (disp.layerStack != s.layerStack) {
- disp.layerStack = s.layerStack;
- flags |= eDisplayTransactionNeeded;
- }
- }
- if (what & DisplayState::eDisplayProjectionChanged) {
- if (disp.orientation != s.orientation) {
- disp.orientation = s.orientation;
- flags |= eDisplayTransactionNeeded;
- }
- if (disp.frame != s.frame) {
- disp.frame = s.frame;
- flags |= eDisplayTransactionNeeded;
- }
- if (disp.viewport != s.viewport) {
- disp.viewport = s.viewport;
- flags |= eDisplayTransactionNeeded;
- }
- }
- if (what & DisplayState::eDisplaySizeChanged) {
- if (disp.width != s.width) {
- disp.width = s.width;
- flags |= eDisplayTransactionNeeded;
- }
- if (disp.height != s.height) {
- disp.height = s.height;
- flags |= eDisplayTransactionNeeded;
- }
+ DisplayDeviceState& state = mCurrentState.displays.editValueAt(index);
+
+ const uint32_t what = s.what;
+ if (what & DisplayState::eSurfaceChanged) {
+ if (IInterface::asBinder(state.surface) != IInterface::asBinder(s.surface)) {
+ state.surface = s.surface;
+ flags |= eDisplayTransactionNeeded;
}
}
+ if (what & DisplayState::eLayerStackChanged) {
+ if (state.layerStack != s.layerStack) {
+ state.layerStack = s.layerStack;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+ if (what & DisplayState::eDisplayProjectionChanged) {
+ if (state.orientation != s.orientation) {
+ state.orientation = s.orientation;
+ flags |= eDisplayTransactionNeeded;
+ }
+ if (state.frame != s.frame) {
+ state.frame = s.frame;
+ flags |= eDisplayTransactionNeeded;
+ }
+ if (state.viewport != s.viewport) {
+ state.viewport = s.viewport;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+ if (what & DisplayState::eDisplaySizeChanged) {
+ if (state.width != s.width) {
+ state.width = s.width;
+ flags |= eDisplayTransactionNeeded;
+ }
+ if (state.height != s.height) {
+ state.height = s.height;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+
return flags;
}
-bool callingThreadHasUnscopedSurfaceFlingerAccess() {
+bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess() {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
-
if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
- !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
+ !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
return false;
}
return true;
}
-uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState) {
+uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState,
+ int64_t desiredPresentTime, int64_t postTime,
+ bool privileged) {
const layer_state_t& s = composerState.state;
sp<Client> client(static_cast<Client*>(composerState.client.get()));
@@ -3447,20 +3749,15 @@
return 0;
}
- if (layer->isPendingRemoval()) {
- ALOGW("Attempting to set client state on removed layer: %s", layer->getName().string());
- return 0;
- }
-
uint32_t flags = 0;
- const uint32_t what = s.what;
+ const uint64_t what = s.what;
bool geometryAppliesWithResize =
what & layer_state_t::eGeometryAppliesWithResize;
// If we are deferring transaction, make sure to push the pending state, as otherwise the
// pending state will also be deferred.
- if (what & layer_state_t::eDeferTransaction) {
+ if (what & layer_state_t::eDeferTransaction_legacy) {
layer->pushPendingState();
}
@@ -3518,6 +3815,16 @@
if (layer->setColor(s.color))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eColorTransformChanged) {
+ if (layer->setColorTransform(s.colorTransform)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ if (what & layer_state_t::eBackgroundColorChanged) {
+ if (layer->setBackgroundColor(s.color, s.bgColorAlpha, s.bgColorDataspace)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eMatrixChanged) {
// TODO: b/109894387
//
@@ -3534,7 +3841,7 @@
// of cropped areas, we need to prevent non-root clients without permission ACCESS_SURFACE_FLINGER
// (a.k.a. everyone except WindowManager and tests) from setting non rectangle preserving
// transformations.
- if (layer->setMatrix(s.matrix, callingThreadHasUnscopedSurfaceFlingerAccess()))
+ if (layer->setMatrix(s.matrix, privileged))
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eTransparentRegionChanged) {
@@ -3545,12 +3852,12 @@
if (layer->setFlags(s.flags, s.mask))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eCropChanged) {
- if (layer->setCrop(s.crop, !geometryAppliesWithResize))
+ if (what & layer_state_t::eCropChanged_legacy) {
+ if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eFinalCropChanged) {
- if (layer->setFinalCrop(s.finalCrop, !geometryAppliesWithResize))
+ if (what & layer_state_t::eCornerRadiusChanged) {
+ if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eLayerStackChanged) {
@@ -3572,15 +3879,15 @@
flags |= eTransactionNeeded|eTraversalNeeded|eDisplayLayerStackChanged;
}
}
- if (what & layer_state_t::eDeferTransaction) {
- if (s.barrierHandle != nullptr) {
- layer->deferTransactionUntil(s.barrierHandle, s.frameNumber);
- } else if (s.barrierGbp != nullptr) {
- const sp<IGraphicBufferProducer>& gbp = s.barrierGbp;
+ if (what & layer_state_t::eDeferTransaction_legacy) {
+ if (s.barrierHandle_legacy != nullptr) {
+ layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy);
+ } else if (s.barrierGbp_legacy != nullptr) {
+ const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy;
if (authenticateSurfaceTextureLocked(gbp)) {
const auto& otherLayer =
(static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- layer->deferTransactionUntil(otherLayer, s.frameNumber);
+ layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy);
} else {
ALOGE("Attempt to defer transaction to to an"
" unrecognized GraphicBufferProducer");
@@ -3611,35 +3918,100 @@
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
+ if (what & layer_state_t::eTransformChanged) {
+ if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eTransformToDisplayInverseChanged) {
+ if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
+ flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eCropChanged) {
+ if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eFrameChanged) {
+ if (layer->setFrame(s.frame)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eAcquireFenceChanged) {
+ if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eDataspaceChanged) {
+ if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eHdrMetadataChanged) {
+ if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eSurfaceDamageRegionChanged) {
+ if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eApiChanged) {
+ if (layer->setApi(s.api)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eSidebandStreamChanged) {
+ if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eInputInfoChanged) {
+ if (privileged) {
+ layer->setInputInfo(s.inputInfo);
+ flags |= eTraversalNeeded;
+ } else {
+ ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
+ }
+ }
+ if (what & layer_state_t::eMetadataChanged) {
+ if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eColorSpaceAgnosticChanged) {
+ if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ std::vector<sp<CallbackHandle>> callbackHandles;
+ if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
+ mTransactionCompletedThread.run();
+ for (const auto& [listener, callbackIds] : s.listenerCallbacks) {
+ callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
+ }
+ }
+
+ if (what & layer_state_t::eBufferChanged) {
+ // Add the new buffer to the cache. This should always come before eCachedBufferChanged.
+ BufferStateLayerCache::getInstance().add(s.cachedBuffer.token, s.cachedBuffer.bufferId,
+ s.buffer);
+ }
+ if (what & layer_state_t::eCachedBufferChanged) {
+ sp<GraphicBuffer> buffer =
+ BufferStateLayerCache::getInstance().get(s.cachedBuffer.token,
+ s.cachedBuffer.bufferId);
+ if (layer->setBuffer(buffer)) {
+ flags |= eTraversalNeeded;
+ layer->setPostTime(postTime);
+ layer->setDesiredPresentTime(desiredPresentTime);
+ }
+ }
+ if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+ // Do not put anything that updates layer state or modifies flags after
+ // setTransactionCompletedListener
return flags;
}
-void SurfaceFlinger::setDestroyStateLocked(const ComposerState& composerState) {
- const layer_state_t& state = composerState.state;
- sp<Client> client(static_cast<Client*>(composerState.client.get()));
-
- sp<Layer> layer(client->getLayerUser(state.surface));
- if (layer == nullptr) {
- return;
+uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
+ uint32_t flags = 0;
+ if (!inputWindowCommands.transferTouchFocusCommands.empty()) {
+ flags |= eTraversalNeeded;
}
- if (layer->isPendingRemoval()) {
- ALOGW("Attempting to destroy on removed layer: %s", layer->getName().string());
- return;
+ if (inputWindowCommands.syncInputWindows) {
+ flags |= eTraversalNeeded;
}
- if (state.what & layer_state_t::eDestroySurface) {
- removeLayerLocked(mStateLock, layer);
- }
+ mPendingInputWindowCommands.merge(inputWindowCommands);
+ return flags;
}
-status_t SurfaceFlinger::createLayer(
- const String8& name,
- const sp<Client>& client,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
-{
+status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
+ uint32_t h, PixelFormat format, uint32_t flags,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -3652,22 +4024,48 @@
String8 uniqueName = getUniqueLayerName(name);
+ bool primaryDisplayOnly = false;
+
+ // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
+ // TODO b/64227542
+ if (metadata.has(METADATA_WINDOW_TYPE)) {
+ int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
+ if (windowType == 441731) {
+ metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL
+ primaryDisplayOnly = true;
+ }
+ }
+
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
- case ISurfaceComposerClient::eFXSurfaceNormal:
- result = createBufferLayer(client,
- uniqueName, w, h, flags, format,
- handle, gbp, &layer);
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ result = createBufferQueueLayer(client, uniqueName, w, h, flags, std::move(metadata),
+ format, handle, gbp, &layer);
break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ result = createBufferStateLayer(client, uniqueName, w, h, flags, std::move(metadata),
+ handle, &layer);
+ break;
case ISurfaceComposerClient::eFXSurfaceColor:
- result = createColorLayer(client,
- uniqueName, w, h, flags,
- handle, &layer);
+ // check if buffer size is set for color layer.
+ if (w > 0 || h > 0) {
+ ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
+ int(w), int(h));
+ return BAD_VALUE;
+ }
+
+ result = createColorLayer(client, uniqueName, w, h, flags, std::move(metadata), handle,
+ &layer);
break;
case ISurfaceComposerClient::eFXSurfaceContainer:
- result = createContainerLayer(client,
- uniqueName, w, h, flags,
- handle, &layer);
+ // check if buffer size is set for container layer.
+ if (w > 0 || h > 0) {
+ ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
+ int(w), int(h));
+ return BAD_VALUE;
+ }
+ result = createContainerLayer(client, uniqueName, w, h, flags, std::move(metadata),
+ handle, &layer);
break;
default:
result = BAD_VALUE;
@@ -3678,16 +4076,13 @@
return result;
}
- // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
- // TODO b/64227542
- if (windowType == 441731) {
- windowType = 2024; // TYPE_NAVIGATION_BAR_PANEL
+ if (primaryDisplayOnly) {
layer->setPrimaryDisplayOnly();
}
- layer->setInfo(windowType, ownerUid);
-
- result = addClientLayer(client, *handle, *gbp, layer, *parent);
+ bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
+ result = addClientLayer(client, *handle, *gbp, layer, *parent,
+ addToCurrentState);
if (result != NO_ERROR) {
return result;
}
@@ -3719,15 +4114,18 @@
});
}
- ALOGD_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(), uniqueName.c_str());
+ ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(),
+ uniqueName.c_str());
return uniqueName;
}
-status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client,
- const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
-{
+status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name,
+ uint32_t w, uint32_t h, uint32_t flags,
+ LayerMetadata metadata, PixelFormat& format,
+ sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp,
+ sp<Layer>* outLayer) {
// initialize the surfaces
switch (format) {
case PIXEL_FORMAT_TRANSPARENT:
@@ -3739,74 +4137,87 @@
break;
}
- sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags);
- status_t err = layer->setBuffers(w, h, format, flags);
+ sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer(
+ LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+ status_t err = layer->setDefaultBufferProperties(w, h, format);
if (err == NO_ERROR) {
*handle = layer->getHandle();
*gbp = layer->getProducer();
*outLayer = layer;
}
- ALOGE_IF(err, "createBufferLayer() failed (%s)", strerror(-err));
+ ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err));
return err;
}
-status_t SurfaceFlinger::createColorLayer(const sp<Client>& client,
- const String8& name, uint32_t w, uint32_t h, uint32_t flags,
- sp<IBinder>* handle, sp<Layer>* outLayer)
-{
- *outLayer = new ColorLayer(this, client, name, w, h, flags);
+status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name,
+ uint32_t w, uint32_t h, uint32_t flags,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<Layer>* outLayer) {
+ sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(
+ LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+ *handle = layer->getHandle();
+ *outLayer = layer;
+
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, const String8& name, uint32_t w,
+ uint32_t h, uint32_t flags, LayerMetadata metadata,
+ sp<IBinder>* handle, sp<Layer>* outLayer) {
+ *outLayer = getFactory().createColorLayer(
+ LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
-status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client,
- const String8& name, uint32_t w, uint32_t h, uint32_t flags,
- sp<IBinder>* handle, sp<Layer>* outLayer)
-{
- *outLayer = new ContainerLayer(this, client, name, w, h, flags);
+status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, const String8& name,
+ uint32_t w, uint32_t h, uint32_t flags,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<Layer>* outLayer) {
+ *outLayer = getFactory().createContainerLayer(
+ LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
-status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
-{
- // called by a client when it wants to remove a Layer
- status_t err = NO_ERROR;
- sp<Layer> l(client->getLayerUser(handle));
- if (l != nullptr) {
- mInterceptor->saveSurfaceDeletion(l);
- err = removeLayer(l);
- ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
- "error removing layer=%p (%s)", l.get(), strerror(-err));
- }
- return err;
+void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) {
+ mLayersPendingRemoval.add(layer);
+ mLayersRemoved = true;
+ setTransactionFlags(eTransactionNeeded);
}
-status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer)
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer)
{
- // called by ~LayerCleaner() when all references to the IBinder (handle)
- // are gone
- sp<Layer> l = layer.promote();
- if (l == nullptr) {
- // The layer has already been removed, carry on
- return NO_ERROR;
+ Mutex::Autolock lock(mStateLock);
+ // If a layer has a parent, we allow it to out-live it's handle
+ // with the idea that the parent holds a reference and will eventually
+ // be cleaned up. However no one cleans up the top-level so we do so
+ // here.
+ if (layer->getParent() == nullptr) {
+ mCurrentState.layersSortedByZ.remove(layer);
}
- // If we have a parent, then we can continue to live as long as it does.
- return removeLayer(l, true);
+ markLayerPendingRemovalLocked(layer);
+ layer.clear();
}
// ---------------------------------------------------------------------------
void SurfaceFlinger::onInitializeDisplays() {
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display) return;
+
+ const sp<IBinder> token = display->getDisplayToken().promote();
+ LOG_ALWAYS_FATAL_IF(token == nullptr);
+
// reset screen orientation and use primary layer stack
Vector<ComposerState> state;
Vector<DisplayState> displays;
DisplayState d;
d.what = DisplayState::eDisplayProjectionChanged |
DisplayState::eLayerStackChanged;
- d.token = mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY];
+ d.token = token;
d.layerStack = 0;
d.orientation = DisplayState::eOrientationDefault;
d.frame.makeInvalid();
@@ -3814,68 +4225,53 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0);
- setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL,
- /*stateLockHeld*/ false);
+ setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1);
- const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
- const nsecs_t period = activeConfig->getVsyncPeriod();
- mAnimFrameTracker.setDisplayRefreshPeriod(period);
+ setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
+
+ const nsecs_t vsyncPeriod = getVsyncPeriod();
+ mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
// Use phase of 0 since phase is not known.
// Use latency of 0, which will snap to the ideal latency.
- setCompositorTimingSnapped(0, period, 0);
+ DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
+ setCompositorTimingSnapped(stats, 0);
}
void SurfaceFlinger::initializeDisplays() {
- class MessageScreenInitialized : public MessageBase {
- SurfaceFlinger* flinger;
- public:
- explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
- virtual bool handler() {
- flinger->onInitializeDisplays();
- return true;
- }
- };
- sp<MessageBase> msg = new MessageScreenInitialized(this);
- postMessageAsync(msg); // we may be called from main thread, use async message
+ // Async since we may be called from the main thread.
+ postMessageAsync(
+ new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
}
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw,
- int mode, bool stateLockHeld) {
- ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
- this);
- int32_t type = hw->getDisplayType();
- int currentMode = hw->getPowerMode();
+void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
+ if (display->isVirtual()) {
+ ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
+ return;
+ }
+ const auto displayId = display->getId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
+ ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+
+ int currentMode = display->getPowerMode();
if (mode == currentMode) {
return;
}
- hw->setPowerMode(mode);
- if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGW("Trying to set power mode for virtual display");
- return;
- }
+ display->setPowerMode(mode);
if (mInterceptor->isEnabled()) {
- ConditionalLock lock(mStateLock, !stateLockHeld);
- ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
- if (idx < 0) {
- ALOGW("Surface Interceptor SavePowerMode: invalid display token");
- return;
- }
- mInterceptor->savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+ mInterceptor->savePowerModeUpdate(display->getSequenceId(), mode);
}
if (currentMode == HWC_POWER_MODE_OFF) {
// Turn on the display
- getHwComposer().setPowerMode(type, mode);
- if (type == DisplayDevice::DISPLAY_PRIMARY &&
- mode != HWC_POWER_MODE_DOZE_SUSPEND) {
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenAcquired();
- resyncToHardwareVsync(true);
+ getHwComposer().setPowerMode(*displayId, mode);
+ if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+ mScheduler->onScreenAcquired(mAppConnectionHandle);
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
mVisibleRegionsDirty = true;
@@ -3894,75 +4290,64 @@
ALOGW("Couldn't set SCHED_OTHER on display off");
}
- if (type == DisplayDevice::DISPLAY_PRIMARY &&
- currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
- disableHardwareVsync(true); // also cancels any in-progress resync
-
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenReleased();
+ if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
+ mScheduler->disableHardwareVsync(true);
+ mScheduler->onScreenReleased(mAppConnectionHandle);
}
- getHwComposer().setPowerMode(type, mode);
+ getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
// from this point on, SF will stop drawing on this display
} else if (mode == HWC_POWER_MODE_DOZE ||
mode == HWC_POWER_MODE_NORMAL) {
// Update display while dozing
- getHwComposer().setPowerMode(type, mode);
- if (type == DisplayDevice::DISPLAY_PRIMARY &&
- currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenAcquired();
- resyncToHardwareVsync(true);
+ getHwComposer().setPowerMode(*displayId, mode);
+ if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
+ mScheduler->onScreenAcquired(mAppConnectionHandle);
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
} else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
// Leave display going to doze
- if (type == DisplayDevice::DISPLAY_PRIMARY) {
- disableHardwareVsync(true); // also cancels any in-progress resync
- // FIXME: eventthread only knows about the main display right now
- mEventThread->onScreenReleased();
+ if (display->isPrimary()) {
+ mScheduler->disableHardwareVsync(true);
+ mScheduler->onScreenReleased(mAppConnectionHandle);
}
- getHwComposer().setPowerMode(type, mode);
+ getHwComposer().setPowerMode(*displayId, mode);
} else {
ALOGE("Attempting to set unknown power mode: %d\n", mode);
- getHwComposer().setPowerMode(type, mode);
+ getHwComposer().setPowerMode(*displayId, mode);
}
- ALOGD("Finished set power mode=%d, type=%d", mode, hw->getDisplayType());
+
+ if (display->isPrimary()) {
+ mTimeStats->setPowerMode(mode);
+ if (mRefreshRateStats) {
+ // Update refresh rate stats.
+ mRefreshRateStats->setPowerMode(mode);
+ }
+ }
+
+ ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
}
-void SurfaceFlinger::setPowerMode(const sp<IBinder>& display, int mode) {
- class MessageSetPowerMode: public MessageBase {
- SurfaceFlinger& mFlinger;
- sp<IBinder> mDisplay;
- int mMode;
- public:
- MessageSetPowerMode(SurfaceFlinger& flinger,
- const sp<IBinder>& disp, int mode) : mFlinger(flinger),
- mDisplay(disp) { mMode = mode; }
- virtual bool handler() {
- sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
- if (hw == nullptr) {
- ALOGE("Attempt to set power mode = %d for null display %p",
- mMode, mDisplay.get());
- } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
- ALOGW("Attempt to set power mode = %d for virtual display",
- mMode);
- } else {
- mFlinger.setPowerModeInternal(
- hw, mMode, /*stateLockHeld*/ false);
- }
- return true;
+void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
+ postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+ const auto display = getDisplayDevice(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
+ displayToken.get());
+ } else if (display->isVirtual()) {
+ ALOGW("Attempt to set power mode %d for virtual display", mode);
+ } else {
+ setPowerModeInternal(display, mode);
}
- };
- sp<MessageBase> msg = new MessageSetPowerMode(*this, display, mode);
- postMessageSync(msg);
+ }));
}
// ---------------------------------------------------------------------------
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto)
- NO_THREAD_SAFETY_ANALYSIS {
- String8 result;
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
+ bool asProto) NO_THREAD_SAFETY_ANALYSIS {
+ std::string result;
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -3970,8 +4355,8 @@
if ((uid != AID_SHELL) &&
!PermissionCache::checkPermission(sDump, pid, uid)) {
- result.appendFormat("Permission Denial: "
- "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid);
+ 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
@@ -3979,105 +4364,43 @@
status_t err = mStateLock.timedLock(s2ns(1));
bool locked = (err == NO_ERROR);
if (!locked) {
- result.appendFormat(
- "SurfaceFlinger appears to be unresponsive (%s [%d]), "
- "dumping anyways (no locks held)\n", strerror(-err), err);
+ StringAppendF(&result,
+ "SurfaceFlinger appears to be unresponsive (%s [%d]), dumping anyways "
+ "(no locks held)\n",
+ strerror(-err), err);
}
- bool dumpAll = true;
- size_t index = 0;
- size_t numArgs = args.size();
+ using namespace std::string_literals;
- if (numArgs) {
- if ((index < numArgs) &&
- (args[index] == String16("--list"))) {
- index++;
- listLayersLocked(args, index, result);
- dumpAll = false;
- }
+ static const std::unordered_map<std::string, Dumper> dumpers = {
+ {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
+ {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
+ {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+ {"--dispsync"s, dumper([this](std::string& s) {
+ mScheduler->dumpPrimaryDispSync(s);
+ })},
+ {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
+ {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+ {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
+ {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+ {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+ {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
+ {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+ {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ };
- if ((index < numArgs) &&
- (args[index] == String16("--latency"))) {
- index++;
- dumpStatsLocked(args, index, result);
- dumpAll = false;
- }
+ const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
- if ((index < numArgs) &&
- (args[index] == String16("--latency-clear"))) {
- index++;
- clearStatsLocked(args, index, result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dispsync"))) {
- index++;
- mPrimaryDispSync.dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--static-screen"))) {
- index++;
- dumpStaticScreenStats(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--frame-events"))) {
- index++;
- dumpFrameEventsLocked(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) && (args[index] == String16("--wide-color"))) {
- index++;
- dumpWideColorInfo(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--enable-layer-stats"))) {
- index++;
- mLayerStats.enable();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--disable-layer-stats"))) {
- index++;
- mLayerStats.disable();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--clear-layer-stats"))) {
- index++;
- mLayerStats.clear();
- dumpAll = false;
- }
-
- if ((index < numArgs) &&
- (args[index] == String16("--dump-layer-stats"))) {
- index++;
- mLayerStats.dump(result);
- dumpAll = false;
- }
-
- if ((index < numArgs) && (args[index] == String16("--timestats"))) {
- index++;
- mTimeStats.parseArgs(asProto, args, index, result);
- dumpAll = false;
- }
- }
-
- if (dumpAll) {
+ if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+ (it->second)(args, asProto, result);
+ } else {
if (asProto) {
LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
} else {
- dumpAllLocked(args, index, result);
+ dumpAllLocked(args, result);
}
}
@@ -4085,53 +4408,41 @@
mStateLock.unlock();
}
}
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return NO_ERROR;
}
-void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */,
- size_t& /* index */, String8& result) const
-{
- mCurrentState.traverseInZOrder([&](Layer* layer) {
- result.appendFormat("%s\n", layer->getName().string());
- });
-}
-
-void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
- String8& result) const
-{
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
+status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
+ if (asProto && mTracing.isEnabled()) {
+ mTracing.writeToFileAsync();
}
- const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
- const nsecs_t period = activeConfig->getVsyncPeriod();
- result.appendFormat("%" PRId64 "\n", period);
+ return doDump(fd, DumpArgs(), asProto);
+}
- if (name.isEmpty()) {
- mAnimFrameTracker.dumpStats(result);
- } else {
+void SurfaceFlinger::listLayersLocked(std::string& result) const {
+ mCurrentState.traverseInZOrder(
+ [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
+}
+
+void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
+ StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
+
+ if (args.size() > 1) {
+ const auto name = String8(args[1]);
mCurrentState.traverseInZOrder([&](Layer* layer) {
if (name == layer->getName()) {
layer->dumpFrameStats(result);
}
});
+ } else {
+ mAnimFrameTracker.dumpStats(result);
}
}
-void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
- String8& /* result */)
-{
- String8 name;
- if (index < args.size()) {
- name = String8(args[index]);
- index++;
- }
-
+void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
- if (name.isEmpty() || (name == layer->getName())) {
+ if (args.size() < 2 || String8(args[1]) == layer->getName()) {
layer->clearFrameStats();
}
});
@@ -4139,6 +4450,10 @@
mAnimFrameTracker.clearStats();
}
+void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const {
+ mTimeStats->parseArgs(asProto, args, result);
+}
+
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
@@ -4149,37 +4464,46 @@
mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
}
-void SurfaceFlinger::appendSfConfigString(String8& result) const
-{
+void SurfaceFlinger::appendSfConfigString(std::string& result) const {
result.append(" [sf");
if (isLayerTripleBufferingDisabled())
result.append(" DISABLE_TRIPLE_BUFFERING");
- result.appendFormat(" PRESENT_TIME_OFFSET=%" PRId64 , dispSyncPresentTimeOffset);
- result.appendFormat(" FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
- result.appendFormat(" MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
- result.appendFormat(" RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
- result.appendFormat(" NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
- maxFrameBufferAcquiredBuffers);
+ StringAppendF(&result, " PRESENT_TIME_OFFSET=%" PRId64, dispSyncPresentTimeOffset);
+ StringAppendF(&result, " FORCE_HWC_FOR_RBG_TO_YUV=%d", useHwcForRgbToYuv);
+ StringAppendF(&result, " MAX_VIRT_DISPLAY_DIM=%" PRIu64, maxVirtualDisplaySize);
+ StringAppendF(&result, " RUNNING_WITHOUT_SYNC_FRAMEWORK=%d", !hasSyncFramework);
+ StringAppendF(&result, " NUM_FRAMEBUFFER_SURFACE_BUFFERS=%" PRId64,
+ maxFrameBufferAcquiredBuffers);
result.append("]");
}
-void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
-{
- result.appendFormat("Static screen stats:\n");
+void SurfaceFlinger::dumpVSync(std::string& result) const {
+ mPhaseOffsets->dump(result);
+ StringAppendF(&result,
+ " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
+ dispSyncPresentTimeOffset, getVsyncPeriod());
+
+ StringAppendF(&result, "Scheduler enabled.");
+ StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n",
+ mUseSmart90ForVideo ? "on" : "off");
+ mScheduler->dump(mAppConnectionHandle, result);
+}
+
+void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
+ result.append("Static screen stats:\n");
for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
float bucketTimeSec = getBE().mFrameBuckets[b] / 1e9;
float percent = 100.0f *
static_cast<float>(getBE().mFrameBuckets[b]) / getBE().mTotalTime;
- result.appendFormat(" < %zd frames: %.3f s (%.1f%%)\n",
- b + 1, bucketTimeSec, percent);
+ StringAppendF(&result, " < %zd frames: %.3f s (%.1f%%)\n", b + 1, bucketTimeSec, percent);
}
float bucketTimeSec = getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] / 1e9;
float percent = 100.0f *
static_cast<float>(getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1]) / getBE().mTotalTime;
- result.appendFormat(" %zd+ frames: %.3f s (%.1f%%)\n",
- SurfaceFlingerBE::NUM_BUCKETS - 1, bucketTimeSec, percent);
+ StringAppendF(&result, " %zd+ frames: %.3f s (%.1f%%)\n", SurfaceFlingerBE::NUM_BUCKETS - 1,
+ bucketTimeSec, percent);
}
void SurfaceFlinger::recordBufferingStats(const char* layerName,
@@ -4200,8 +4524,8 @@
}
}
-void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
- result.appendFormat("Layer frame timestamps:\n");
+void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
+ result.append("Layer frame timestamps:\n");
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
const size_t count = currentLayers.size();
@@ -4210,7 +4534,7 @@
}
}
-void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
result.append("Buffering stats:\n");
result.append(" [Layer name] <Active time> <Two buffer> "
"<Double buffered> <Triple buffered>\n");
@@ -4236,37 +4560,81 @@
for (const auto& sortedPair : sorted) {
float activeTime = sortedPair.first;
const BufferTuple& values = sortedPair.second;
- result.appendFormat(" [%s] %.2f %.3f %.3f %.3f\n",
- std::get<0>(values).c_str(), activeTime,
- std::get<1>(values), std::get<2>(values),
- std::get<3>(values));
+ StringAppendF(&result, " [%s] %.2f %.3f %.3f %.3f\n", std::get<0>(values).c_str(),
+ activeTime, std::get<1>(values), std::get<2>(values), std::get<3>(values));
}
result.append("\n");
}
-void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
- result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
- result.appendFormat("DisplayColorSetting: %s\n",
- decodeDisplayColorSetting(mDisplayColorSetting).c_str());
-
- // TODO: print out if wide-color mode is active or not
-
- for (size_t d = 0; d < mDisplays.size(); d++) {
- const sp<const DisplayDevice>& displayDevice(mDisplays[d]);
- int32_t hwcId = displayDevice->getHwcDisplayId();
- if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
+ for (const auto& [token, display] : mDisplays) {
+ const auto displayId = display->getId();
+ if (!displayId) {
+ continue;
+ }
+ const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
+ if (!hwcDisplayId) {
continue;
}
- result.appendFormat("Display %d color modes:\n", hwcId);
- std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
- for (auto&& mode : modes) {
- result.appendFormat(" %s (%d)\n", decodeColorMode(mode).c_str(), mode);
+ StringAppendF(&result,
+ "Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(),
+ *hwcDisplayId);
+ uint8_t port;
+ DisplayIdentificationData data;
+ if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
+ result.append("no identification data\n");
+ continue;
}
- ColorMode currentMode = displayDevice->getActiveColorMode();
- result.appendFormat(" Current color mode: %s (%d)\n",
- decodeColorMode(currentMode).c_str(), currentMode);
+ if (!isEdid(data)) {
+ result.append("unknown identification data: ");
+ for (uint8_t byte : data) {
+ StringAppendF(&result, "%x ", byte);
+ }
+ result.append("\n");
+ continue;
+ }
+
+ const auto edid = parseEdid(data);
+ if (!edid) {
+ result.append("invalid EDID: ");
+ for (uint8_t byte : data) {
+ StringAppendF(&result, "%x ", byte);
+ }
+ result.append("\n");
+ continue;
+ }
+
+ StringAppendF(&result, "port=%u pnpId=%s displayName=\"", port, edid->pnpId.data());
+ result.append(edid->displayName.data(), edid->displayName.length());
+ result.append("\"\n");
+ }
+}
+
+void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
+ StringAppendF(&result, "Device has wide color display: %d\n", hasWideColorDisplay);
+ StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
+ StringAppendF(&result, "DisplayColorSetting: %s\n",
+ decodeDisplayColorSetting(mDisplayColorSetting).c_str());
+
+ // TODO: print out if wide-color mode is active or not
+
+ for (const auto& [token, display] : mDisplays) {
+ const auto displayId = display->getId();
+ if (!displayId) {
+ continue;
+ }
+
+ StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str());
+ std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
+ for (auto&& mode : modes) {
+ StringAppendF(&result, " %s (%d)\n", decodeColorMode(mode).c_str(), mode);
+ }
+
+ ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
+ StringAppendF(&result, " Current color mode: %s (%d)\n",
+ decodeColorMode(currentMode).c_str(), currentMode);
}
result.append("\n");
}
@@ -4283,46 +4651,40 @@
return layersProto;
}
-LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(int32_t hwcId) const {
+LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(
+ const sp<DisplayDevice>& displayDevice) const {
LayersProto layersProto;
- const sp<DisplayDevice>& displayDevice(mDisplays[hwcId]);
SizeProto* resolution = layersProto.mutable_resolution();
resolution->set_w(displayDevice->getWidth());
resolution->set_h(displayDevice->getHeight());
- layersProto.set_color_mode(decodeColorMode(displayDevice->getActiveColorMode()));
- layersProto.set_color_transform(decodeColorTransform(displayDevice->getColorTransform()));
- layersProto.set_global_transform(
- static_cast<int32_t>(displayDevice->getOrientationTransform()));
+ auto display = displayDevice->getCompositionDisplay();
+ const auto& displayState = display->getState();
+ layersProto.set_color_mode(decodeColorMode(displayState.colorMode));
+ layersProto.set_color_transform(decodeColorTransform(displayState.colorTransform));
+ layersProto.set_global_transform(displayState.orientation);
+
+ const auto displayId = displayDevice->getId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
mDrawingState.traverseInZOrder([&](Layer* layer) {
- if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(hwcId)) {
+ if (!layer->visibleRegion.isEmpty() && !display->getOutputLayersOrderedByZ().empty()) {
LayerProto* layerProto = layersProto.add_layers();
- layer->writeToProto(layerProto, hwcId);
+ layer->writeToProto(layerProto, displayDevice);
}
});
return layersProto;
}
-void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
- String8& result) const
-{
- bool colorize = false;
- if (index < args.size()
- && (args[index] == String16("--color"))) {
- colorize = true;
- index++;
- }
-
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+ const bool colorize = !args.empty() && args[0] == String16("--color");
Colorizer colorizer(colorize);
// figure out if we're stuck somewhere
const nsecs_t now = systemTime();
- const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
const nsecs_t inTransaction(mDebugInTransaction);
- nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
/*
@@ -4337,6 +4699,9 @@
appendGuiConfigString(result);
result.append("\n");
+ result.append("\nDisplay identification data:\n");
+ dumpDisplayIdentificationData(result);
+
result.append("\nWide-Color information:\n");
dumpWideColorInfo(result);
@@ -4344,63 +4709,56 @@
result.append("Sync configuration: ");
colorizer.reset(result);
result.append(SyncFeatures::getInstance().toString());
- result.append("\n");
-
- const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
+ result.append("\n\n");
colorizer.bold(result);
- result.append("DispSync configuration: ");
+ result.append("VSYNC configuration:\n");
colorizer.reset(result);
- const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
- const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
- result.appendFormat(
- "app phase %" PRId64 " ns, "
- "sf phase %" PRId64 " ns, "
- "early app phase %" PRId64 " ns, "
- "early sf phase %" PRId64 " ns, "
- "early app gl phase %" PRId64 " ns, "
- "early sf gl phase %" PRId64 " ns, "
- "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
- vsyncPhaseOffsetNs,
- sfVsyncPhaseOffsetNs,
- appEarlyOffset,
- sfEarlyOffset,
- appEarlyGlOffset,
- sfEarlyOffset,
- dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
+ dumpVSync(result);
result.append("\n");
- // Dump static screen stats
- result.append("\n");
dumpStaticScreenStats(result);
result.append("\n");
+ StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
+ StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
+ StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
+
dumpBufferingStats(result);
/*
* Dump the visible layer list
*/
colorizer.bold(result);
- result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
- result.appendFormat("GraphicBufferProducers: %zu, max %zu\n",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
+ StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers);
+ StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
+ mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
colorizer.reset(result);
- LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
- auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
- result.append(LayerProtoParser::layersToString(std::move(layerTree)).c_str());
- result.append("\n");
+ {
+ LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
+ auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+ result.append(LayerProtoParser::layerTreeToString(layerTree));
+ result.append("\n");
+ }
+
+ {
+ StringAppendF(&result, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto compositionLayer = layer->getCompositionLayer();
+ if (compositionLayer) compositionLayer->dump(result);
+ });
+ }
/*
* Dump Display state
*/
colorizer.bold(result);
- result.appendFormat("Displays (%zu entries)\n", mDisplays.size());
+ StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size());
colorizer.reset(result);
- for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- const sp<const DisplayDevice>& hw(mDisplays[dpy]);
- hw->dump(result);
+ for (const auto& [token, display] : mDisplays) {
+ display->dump(result);
}
result.append("\n");
@@ -4412,44 +4770,31 @@
result.append("SurfaceFlinger global state:\n");
colorizer.reset(result);
- HWComposer& hwc(getHwComposer());
- sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
+ getRenderEngine().dump(result);
- getBE().mRenderEngine->dump(result);
-
- if (hw) {
- hw->undefinedRegion.dump(result, "undefinedRegion");
- result.appendFormat(" orientation=%d, isDisplayOn=%d\n",
- hw->getOrientation(), hw->isDisplayOn());
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
+ "undefinedRegion");
+ StringAppendF(&result, " orientation=%d, isPoweredOn=%d\n", display->getOrientation(),
+ display->isPoweredOn());
}
- result.appendFormat(
- " last eglSwapBuffers() time: %f us\n"
- " last transaction time : %f us\n"
- " transaction-flags : %08x\n"
- " refresh-rate : %f fps\n"
- " x-dpi : %f\n"
- " y-dpi : %f\n"
- " gpu_to_cpu_unsupported : %d\n"
- ,
- mLastSwapBufferTime/1000.0,
- mLastTransactionTime/1000.0,
- mTransactionFlags,
- 1e9 / activeConfig->getVsyncPeriod(),
- activeConfig->getDpiX(),
- activeConfig->getDpiY(),
- !mGpuToCpuSupported);
+ StringAppendF(&result,
+ " transaction-flags : %08x\n"
+ " gpu_to_cpu_unsupported : %d\n",
+ mTransactionFlags.load(), !mGpuToCpuSupported);
- result.appendFormat(" eglSwapBuffers time: %f us\n",
- inSwapBuffersDuration/1000.0);
+ if (const auto displayId = getInternalDisplayIdLocked();
+ displayId && getHwComposer().isConnected(*displayId)) {
+ const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+ StringAppendF(&result,
+ " refresh-rate : %f fps\n"
+ " x-dpi : %f\n"
+ " y-dpi : %f\n",
+ 1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
+ activeConfig->getDpiY());
+ }
- result.appendFormat(" transaction time: %f us\n",
- inTransactionDuration/1000.0);
-
- /*
- * VSYNC state
- */
- mEventThread->dump(result);
- result.append("\n");
+ StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
/*
* Tracing state
@@ -4460,18 +4805,17 @@
/*
* HWC layer minidump
*/
- for (size_t d = 0; d < mDisplays.size(); d++) {
- const sp<const DisplayDevice>& displayDevice(mDisplays[d]);
- int32_t hwcId = displayDevice->getHwcDisplayId();
- if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+ for (const auto& [token, display] : mDisplays) {
+ const auto displayId = display->getId();
+ if (!displayId) {
continue;
}
- result.appendFormat("Display %d HWC layers:\n", hwcId);
+ StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
Layer::miniDumpHeader(result);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
- layer->miniDump(result, hwcId);
- });
+ const sp<DisplayDevice> displayDevice = display;
+ mCurrentState.traverseInZOrder(
+ [&](Layer* layer) { layer->miniDump(result, displayDevice); });
result.append("\n");
}
@@ -4482,9 +4826,8 @@
result.append("h/w composer state:\n");
colorizer.reset(result);
bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
- result.appendFormat(" h/w composer %s\n",
- hwcDisabled ? "disabled" : "enabled");
- hwc.dump(result);
+ StringAppendF(&result, " h/w composer %s\n", hwcDisabled ? "disabled" : "enabled");
+ getHwComposer().dump(result);
/*
* Dump gralloc state
@@ -4497,27 +4840,30 @@
*/
if (mVrFlingerRequestsDisplay && mVrFlinger) {
result.append("VrFlinger state:\n");
- result.append(mVrFlinger->Dump().c_str());
+ result.append(mVrFlinger->Dump());
result.append("\n");
}
+
+ /**
+ * Scheduler dump state.
+ */
+ result.append("\nScheduler state:\n");
+ result.append(mScheduler->doDump() + "\n");
+ StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
+ result.append(mRefreshRateStats->doDump() + "\n");
}
-const Vector< sp<Layer> >&
-SurfaceFlinger::getLayerSortedByZForHwcDisplay(int id) {
+const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) {
// Note: mStateLock is held here
- wp<IBinder> dpy;
- for (size_t i=0 ; i<mDisplays.size() ; i++) {
- if (mDisplays.valueAt(i)->getHwcDisplayId() == id) {
- dpy = mDisplays.keyAt(i);
- break;
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getId() == displayId) {
+ return getDisplayDeviceLocked(token)->getVisibleLayersSortedByZ();
}
}
- if (dpy == nullptr) {
- ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id);
- // Just use the primary display so we have something to return
- dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
- }
- return getDisplayDeviceLocked(dpy)->getVisibleLayersSortedByZ();
+
+ ALOGE("%s: Invalid display %s", __FUNCTION__, to_string(displayId).c_str());
+ static const Vector<sp<Layer>> empty;
+ return empty;
}
void SurfaceFlinger::updateColorMatrixLocked() {
@@ -4545,51 +4891,77 @@
}
status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
- switch (code) {
- case CREATE_CONNECTION:
- case CREATE_DISPLAY:
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wswitch-enum"
+ switch (static_cast<ISurfaceComposerTag>(code)) {
+ // These methods should at minimum make sure that the client requested
+ // access to SF.
case BOOT_FINISHED:
case CLEAR_ANIMATION_FRAME_STATS:
- case GET_ANIMATION_FRAME_STATS:
- case SET_POWER_MODE:
- case GET_HDR_CAPABILITIES:
+ case CREATE_DISPLAY:
+ case DESTROY_DISPLAY:
case ENABLE_VSYNC_INJECTIONS:
+ case GET_ANIMATION_FRAME_STATS:
+ case GET_HDR_CAPABILITIES:
+ case SET_ACTIVE_CONFIG:
+ case SET_ALLOWED_DISPLAY_CONFIGS:
+ case GET_ALLOWED_DISPLAY_CONFIGS:
+ case SET_ACTIVE_COLOR_MODE:
case INJECT_VSYNC:
- {
- // codes that require permission check
+ case SET_POWER_MODE:
+ case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
+ case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
+ case GET_DISPLAYED_CONTENT_SAMPLE: {
if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
ipc->getCallingPid(), ipc->getCallingUid());
return PERMISSION_DENIED;
}
- break;
- }
- /*
- * Calling setTransactionState is safe, because you need to have been
- * granted a reference to Client* and Handle* to do anything with it.
- *
- * Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h
- */
- case SET_TRANSACTION_STATE:
- case CREATE_SCOPED_CONNECTION:
- {
return OK;
}
- case CAPTURE_SCREEN:
- {
- // codes that require permission check
+ case GET_LAYER_DEBUG_INFO: {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) &&
- !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
- ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
+ if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
+ ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
- break;
+ return OK;
}
- case CAPTURE_LAYERS: {
+ // Used by apps to hook Choreographer to SurfaceFlinger.
+ case CREATE_DISPLAY_EVENT_CONNECTION:
+ // The following calls are currently used by clients that do not
+ // request necessary permissions. However, they do not expose any secret
+ // information, so it is OK to pass them.
+ case AUTHENTICATE_SURFACE:
+ case GET_ACTIVE_COLOR_MODE:
+ case GET_ACTIVE_CONFIG:
+ case GET_PHYSICAL_DISPLAY_IDS:
+ case GET_PHYSICAL_DISPLAY_TOKEN:
+ case GET_DISPLAY_COLOR_MODES:
+ case GET_DISPLAY_NATIVE_PRIMARIES:
+ case GET_DISPLAY_CONFIGS:
+ case GET_DISPLAY_STATS:
+ case GET_SUPPORTED_FRAME_TIMESTAMPS:
+ // Calling setTransactionState is safe, because you need to have been
+ // granted a reference to Client* and Handle* to do anything with it.
+ case SET_TRANSACTION_STATE:
+ case CREATE_CONNECTION:
+ case GET_COLOR_MANAGEMENT:
+ case GET_COMPOSITION_PREFERENCE:
+ case GET_PROTECTED_CONTENT_SUPPORT:
+ case IS_WIDE_COLOR_DISPLAY:
+ case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+ case SET_DISPLAY_BRIGHTNESS: {
+ return OK;
+ }
+ case CAPTURE_LAYERS:
+ case CAPTURE_SCREEN:
+ case ADD_REGION_SAMPLING_LISTENER:
+ case REMOVE_REGION_SAMPLING_LISTENER: {
+ // codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
@@ -4598,15 +4970,37 @@
ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
- break;
+ return OK;
+ }
+ // The following codes are deprecated and should never be allowed to access SF.
+ case CONNECT_DISPLAY_UNUSED:
+ case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: {
+ ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code);
+ return PERMISSION_DENIED;
}
}
- return OK;
+
+ // These codes are used for the IBinder protocol to either interrogate the recipient
+ // side of the transaction for its canonical interface descriptor or to dump its state.
+ // We let them pass by default.
+ if (code == IBinder::INTERFACE_TRANSACTION || code == IBinder::DUMP_TRANSACTION ||
+ code == IBinder::PING_TRANSACTION || code == IBinder::SHELL_COMMAND_TRANSACTION ||
+ code == IBinder::SYSPROPS_TRANSACTION) {
+ return OK;
+ }
+ // Numbers from 1000 to 1032 are currently use for backdoors. The code
+ // in onTransact verifies that the user is root, and has access to use SF.
+ if (code >= 1000 && code <= 1032) {
+ ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
+ return OK;
+ }
+ ALOGE("Permission Denial: SurfaceFlinger did not recognize request code: %u", code);
+ return PERMISSION_DENIED;
+#pragma clang diagnostic pop
}
-status_t SurfaceFlinger::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
status_t credentialCheck = CheckTransactCodeCredentials(code);
if (credentialCheck != OK) {
return credentialCheck;
@@ -4671,8 +5065,12 @@
reply->writeInt32(mDebugDisableHWC);
return NO_ERROR;
case 1013: {
- sp<const DisplayDevice> hw(getDefaultDisplayDevice());
- reply->writeInt32(hw->getPageFlipCount());
+ const auto display = getDefaultDisplayDevice();
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ reply->writeInt32(display->getPageFlipCount());
return NO_ERROR;
}
case 1014: {
@@ -4731,7 +5129,8 @@
// Needs to be shifted to proper binder interface when we productize
case 1016: {
n = data.readInt32();
- mPrimaryDispSync.setRefreshSkipCount(n);
+ // TODO(b/113612090): Evaluate if this can be removed.
+ mScheduler->setRefreshSkipCount(n);
return NO_ERROR;
}
case 1017: {
@@ -4741,12 +5140,12 @@
}
case 1018: { // Modify Choreographer's phase offset
n = data.readInt32();
- mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+ mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n));
return NO_ERROR;
}
case 1019: { // Modify SurfaceFlinger's phase offset
n = data.readInt32();
- mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+ mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n));
return NO_ERROR;
}
case 1020: { // Layer updates interceptor
@@ -4779,21 +5178,32 @@
repaintEverything();
return NO_ERROR;
}
- case 1024: { // Is wide color gamut rendering/color management supported?
- reply->writeBool(hasWideColorDisplay);
- return NO_ERROR;
+ // Deprecate, use 1030 to check whether the device is color managed.
+ case 1024: {
+ return NAME_NOT_FOUND;
}
case 1025: { // Set layer tracing
n = data.readInt32();
if (n) {
ALOGD("LayerTracing enabled");
+ Mutex::Autolock lock(mStateLock);
+ mTracingEnabledChanged = true;
mTracing.enable();
- doTracing("tracing.enable");
reply->writeInt32(NO_ERROR);
} else {
ALOGD("LayerTracing disabled");
- status_t err = mTracing.disable();
- reply->writeInt32(err);
+ bool writeFile = false;
+ {
+ Mutex::Autolock lock(mStateLock);
+ mTracingEnabledChanged = true;
+ writeFile = mTracing.disable();
+ }
+
+ if (writeFile) {
+ reply->writeInt32(mTracing.writeToFile());
+ } else {
+ reply->writeInt32(NO_ERROR);
+ }
}
return NO_ERROR;
}
@@ -4803,38 +5213,106 @@
}
// Is a DisplayColorSetting supported?
case 1027: {
- sp<const DisplayDevice> hw(getDefaultDisplayDevice());
- if (!hw) {
+ const auto display = getDefaultDisplayDevice();
+ if (!display) {
return NAME_NOT_FOUND;
}
DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
switch (setting) {
case DisplayColorSetting::MANAGED:
- reply->writeBool(hasWideColorDisplay);
+ reply->writeBool(useColorManagement);
break;
case DisplayColorSetting::UNMANAGED:
reply->writeBool(true);
break;
case DisplayColorSetting::ENHANCED:
- reply->writeBool(hw->hasRenderIntent(RenderIntent::ENHANCE));
+ reply->writeBool(display->hasRenderIntent(RenderIntent::ENHANCE));
break;
default: // vendor display color setting
- reply->writeBool(hw->hasRenderIntent(static_cast<RenderIntent>(setting)));
+ reply->writeBool(
+ display->hasRenderIntent(static_cast<RenderIntent>(setting)));
break;
}
return NO_ERROR;
}
+ // Is VrFlinger active?
+ case 1028: {
+ Mutex::Autolock _l(mStateLock);
+ reply->writeBool(getHwComposer().isUsingVrComposer());
+ return NO_ERROR;
+ }
+ // Set buffer size for SF tracing (value in KB)
+ case 1029: {
+ n = data.readInt32();
+ if (n <= 0 || n > MAX_TRACING_MEMORY) {
+ ALOGW("Invalid buffer size: %d KB", n);
+ reply->writeInt32(BAD_VALUE);
+ return BAD_VALUE;
+ }
+
+ ALOGD("Updating trace buffer to %d KB", n);
+ mTracing.setBufferSize(n * 1024);
+ reply->writeInt32(NO_ERROR);
+ return NO_ERROR;
+ }
+ // Is device color managed?
+ case 1030: {
+ reply->writeBool(useColorManagement);
+ return NO_ERROR;
+ }
+ // Override default composition data space
+ // adb shell service call SurfaceFlinger 1031 i32 1 DATASPACE_NUMBER DATASPACE_NUMBER \
+ // && adb shell stop zygote && adb shell start zygote
+ // to restore: adb shell service call SurfaceFlinger 1031 i32 0 && \
+ // adb shell stop zygote && adb shell start zygote
+ case 1031: {
+ Mutex::Autolock _l(mStateLock);
+ n = data.readInt32();
+ if (n) {
+ n = data.readInt32();
+ if (n) {
+ Dataspace dataspace = static_cast<Dataspace>(n);
+ if (!validateCompositionDataspace(dataspace)) {
+ return BAD_VALUE;
+ }
+ mDefaultCompositionDataspace = dataspace;
+ }
+ n = data.readInt32();
+ if (n) {
+ Dataspace dataspace = static_cast<Dataspace>(n);
+ if (!validateCompositionDataspace(dataspace)) {
+ return BAD_VALUE;
+ }
+ mWideColorGamutCompositionDataspace = dataspace;
+ }
+ } else {
+ // restore composition data space.
+ mDefaultCompositionDataspace = defaultCompositionDataspace;
+ mWideColorGamutCompositionDataspace = wideColorGamutCompositionDataspace;
+ }
+ return NO_ERROR;
+ }
+ case 1032: {
+ n = data.readInt32();
+ mDebugEnableProtectedContent = n;
+ return NO_ERROR;
+ }
}
}
return err;
}
void SurfaceFlinger::repaintEverything() {
- android_atomic_or(1, &mRepaintEverything);
+ mRepaintEverything = true;
signalTransaction();
}
+void SurfaceFlinger::repaintEverythingForHWC() {
+ mRepaintEverything = true;
+ mEventQueue->invalidateForHWC();
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -4848,63 +5326,74 @@
const int mApi;
};
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ,
+status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
+ sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight,
bool useIdentityTransform,
- ISurfaceComposer::Rotation rotation) {
+ ISurfaceComposer::Rotation rotation,
+ bool captureSecureLayers) {
ATRACE_CALL();
- if (CC_UNLIKELY(display == 0)) return BAD_VALUE;
+ if (!displayToken) return BAD_VALUE;
auto renderAreaRotation = fromSurfaceComposerRotation(rotation);
- sp<DisplayDevice> device;
+ sp<DisplayDevice> display;
{
Mutex::Autolock _l(mStateLock);
- device = getDisplayDeviceLocked(display);
- if (!device) return BAD_VALUE;
+ display = getDisplayDeviceLocked(displayToken);
+ if (!display) return BAD_VALUE;
// set the requested width/height to the logical display viewport size
// by default
if (reqWidth == 0 || reqHeight == 0) {
- reqWidth = uint32_t(device->getViewport().width());
- reqHeight = uint32_t(device->getViewport().height());
+ reqWidth = uint32_t(display->getViewport().width());
+ reqHeight = uint32_t(display->getViewport().height());
}
}
- DisplayRenderArea renderArea(device, sourceCrop, reqWidth, reqHeight, renderAreaRotation);
+ DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
+ renderAreaRotation, captureSecureLayers);
- auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
- device, minLayerZ, maxLayerZ, std::placeholders::_1);
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, useIdentityTransform);
+ auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
+ std::placeholders::_1);
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
+ useIdentityTransform);
}
status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
- sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+ sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
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, bool childrenOnly)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR),
+ int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
+ bool childrenOnly)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
mLayer(layer),
mCrop(crop),
mNeedsFiltering(false),
mFlinger(flinger),
mChildrenOnly(childrenOnly) {}
- const Transform& getTransform() const override { return mTransform; }
+ const ui::Transform& getTransform() const override { return mTransform; }
Rect getBounds() const override {
const Layer::State& layerState(mLayer->getDrawingState());
- return Rect(layerState.active.w, layerState.active.h);
+ return mLayer->getBufferSize(layerState);
}
- int getHeight() const override { return mLayer->getDrawingState().active.h; }
- int getWidth() const override { return mLayer->getDrawingState().active.w; }
+ 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; }
+ const sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
Rect getSourceCrop() const override {
if (mCrop.isEmpty()) {
return getBounds();
@@ -4917,8 +5406,11 @@
const sp<Layer>& oldParent;
const sp<Layer>& newParent;
- ReparentForDrawing(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());
oldParent->setChildrenDrawingParent(newParent);
}
~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
@@ -4935,11 +5427,12 @@
drawLayers();
} else {
Rect bounds = getBounds();
- screenshotParentLayer =
- new ContainerLayer(mFlinger, nullptr, String8("Screenshot Parent"),
- bounds.getWidth(), bounds.getHeight(), 0);
+ screenshotParentLayer = mFlinger->getFactory().createContainerLayer(
+ LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
+ bounds.getWidth(), bounds.getHeight(), 0,
+ LayerMetadata()));
- ReparentForDrawing reparent(mLayer, screenshotParentLayer);
+ ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
drawLayers();
}
}
@@ -4951,7 +5444,7 @@
// 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;
- Transform mTransform;
+ ui::Transform mTransform;
bool mNeedsFiltering;
SurfaceFlinger* mFlinger;
@@ -4961,7 +5454,7 @@
auto layerHandle = reinterpret_cast<Layer::Handle*>(layerHandleBinder.get());
auto parent = layerHandle->owner.promote();
- if (parent == nullptr || parent->isPendingRemoval()) {
+ if (parent == nullptr || parent->isRemovedFromCurrentState()) {
ALOGE("captureLayers called with a removed parent");
return NAME_NOT_FOUND;
}
@@ -4976,12 +5469,12 @@
Rect crop(sourceCrop);
if (sourceCrop.width() <= 0) {
crop.left = 0;
- crop.right = parent->getCurrentState().active.w;
+ crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
}
if (sourceCrop.height() <= 0) {
crop.top = 0;
- crop.bottom = parent->getCurrentState().active.h;
+ crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
}
int32_t reqWidth = crop.width() * frameScale;
@@ -4995,7 +5488,7 @@
reqHeight = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly);
+ LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5007,20 +5500,31 @@
visitor(layer);
});
};
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, false);
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false);
}
status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
sp<GraphicBuffer>* outBuffer,
+ const ui::PixelFormat reqPixelFormat,
bool useIdentityTransform) {
ATRACE_CALL();
+ // 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 = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+ *outBuffer =
+ getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat), 1,
+ usage, "screenshot");
+ return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform);
+}
+
+status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer,
+ bool useIdentityTransform) {
// This mutex protects syncFd and captureResult for communication of the return values from the
// main thread back to this Binder thread
std::mutex captureMutex;
@@ -5032,7 +5536,7 @@
const int uid = IPCThreadState::self()->getCallingUid();
const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- sp<LambdaMessage> message = new LambdaMessage([&]() {
+ sp<LambdaMessage> message = new LambdaMessage([&] {
// If there is a refresh pending, bug out early and tell the binder thread to try again
// after the refresh.
if (mRefreshPending) {
@@ -5047,8 +5551,8 @@
int fd = -1;
{
Mutex::Autolock _l(mStateLock);
- renderArea.render([&]() {
- result = captureScreenImplLocked(renderArea, traverseLayers, (*outBuffer).get(),
+ renderArea.render([&] {
+ result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
useIdentityTransform, forSystem, &fd);
});
}
@@ -5063,14 +5567,14 @@
status_t result = postMessageAsync(message);
if (result == NO_ERROR) {
- captureCondition.wait(captureLock, [&]() { return captureResult; });
+ captureCondition.wait(captureLock, [&] { return captureResult; });
while (*captureResult == EAGAIN) {
captureResult.reset();
result = postMessageAsync(message);
if (result != NO_ERROR) {
return result;
}
- captureCondition.wait(captureLock, [&]() { return captureResult; });
+ captureCondition.wait(captureLock, [&] { return captureResult; });
}
result = *captureResult;
}
@@ -5084,39 +5588,103 @@
}
void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers, bool yswap,
- bool useIdentityTransform) {
+ TraverseLayersFunction traverseLayers,
+ ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ int* outSyncFd) {
ATRACE_CALL();
- auto& engine(getRenderEngine());
-
- // get screen geometry
- const auto raHeight = renderArea.getHeight();
-
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
const auto sourceCrop = renderArea.getSourceCrop();
const auto rotation = renderArea.getRotationFlags();
- // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC
- engine.setOutputDataSpace(Dataspace::SRGB);
- engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
+ renderengine::DisplaySettings clientCompositionDisplay;
+ std::vector<renderengine::LayerSettings> clientCompositionLayers;
- // make sure to clear all GL error flags
- engine.checkErrors();
+ // assume that bounds are never offset, and that they are the same as the
+ // buffer bounds.
+ clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
+ ui::Transform transform = renderArea.getTransform();
+ clientCompositionDisplay.globalTransform = transform.asMatrix4();
+ mat4 rotMatrix;
+ // Displacement for repositioning the clipping rectangle after rotating it
+ // with the rotation hint.
+ int displacementX = 0;
+ int displacementY = 0;
+ float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
+ displacementX = reqWidth;
+ break;
+ case ui::Transform::ROT_180:
+ rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+ displacementX = reqWidth;
+ displacementY = reqHeight;
+ break;
+ case ui::Transform::ROT_270:
+ rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+ displacementY = reqHeight;
+ break;
+ default:
+ break;
+ }
+ // We need to transform the clipping window into the right spot.
+ // First, rotate the clipping rectangle by the rotation hint to get the
+ // right orientation
+ const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
+ const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
+ const vec4 rotClipTL = rotMatrix * clipTL;
+ const vec4 rotClipBR = rotMatrix * clipBR;
+ const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
+ const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
+ const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
+ const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
- // set-up our viewport
- engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap,
- rotation);
- engine.disableTexturing();
+ // Now reposition the clipping rectangle with the displacement vector
+ // computed above.
+ const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
+
+ clientCompositionDisplay.clip =
+ Rect(newClipLeft + displacementX, newClipTop + displacementY,
+ newClipRight + displacementX, newClipBottom + displacementY);
+
+ // We need to perform the same transformation in layer space, so propagate
+ // it to the global transform.
+ mat4 clipTransform = displacementMat * rotMatrix;
+ clientCompositionDisplay.globalTransform *= clipTransform;
+ clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
+ clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
- // redraw the screen entirely...
- engine.clearWithColor(0, 0, 0, alpha);
+ renderengine::LayerSettings fillLayer;
+ fillLayer.source.buffer.buffer = nullptr;
+ fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
+ fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
+ fillLayer.alpha = half(alpha);
+ clientCompositionLayers.push_back(fillLayer);
+
+ Region clearRegion = Region::INVALID_REGION;
traverseLayers([&](Layer* layer) {
- layer->draw(renderArea, useIdentityTransform);
+ renderengine::LayerSettings layerSettings;
+ bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
+ false, layerSettings);
+ if (prepared) {
+ clientCompositionLayers.push_back(layerSettings);
+ }
});
+
+ clientCompositionDisplay.clearRegion = clearRegion;
+ // Use an empty fence for the buffer fence, since we just created the buffer so
+ // there is no need for synchronization with the GPU.
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+ getRenderEngine().useProtectedContext(false);
+ getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+ std::move(bufferFence), &drawFence);
+
+ *outSyncFd = drawFence.release();
}
status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
@@ -5140,64 +5708,15 @@
ALOGW("FB is protected: PERMISSION_DENIED");
return PERMISSION_DENIED;
}
-
- // this binds the given EGLImage as a framebuffer for the
- // duration of this scope.
- RE::BindNativeBufferAsFramebuffer bufferBond(getRenderEngine(), buffer);
- if (bufferBond.getStatus() != NO_ERROR) {
- ALOGE("got ANWB binding error while taking screenshot");
- return INVALID_OPERATION;
- }
-
- // this will in fact render into our dequeued buffer
- // via an FBO, which means we didn't have to create
- // an EGLSurface and therefore we're not
- // dependent on the context's EGLConfig.
- renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
-
- if (DEBUG_SCREENSHOTS) {
- getRenderEngine().finish();
- *outSyncFd = -1;
-
- const auto reqWidth = renderArea.getReqWidth();
- const auto reqHeight = renderArea.getReqHeight();
-
- uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
- getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
- checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
- delete [] pixels;
- } else {
- base::unique_fd syncFd = getRenderEngine().flush();
- if (syncFd < 0) {
- getRenderEngine().finish();
- }
- *outSyncFd = syncFd.release();
- }
-
+ renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, outSyncFd);
return NO_ERROR;
}
-void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
- TraverseLayersFunction traverseLayers) {
- if (DEBUG_SCREENSHOTS) {
- for (size_t y = 0; y < h; y++) {
- uint32_t const* p = (uint32_t const*)vaddr + y * s;
- for (size_t x = 0; x < w; x++) {
- if (p[x] != 0xFF000000) return;
- }
- }
- ALOGE("*** we just took a black screenshot ***");
+void SurfaceFlinger::setInputWindowsFinished() {
+ Mutex::Autolock _l(mStateLock);
- size_t i = 0;
- traverseLayers([&](Layer* layer) {
- const Layer::State& state(layer->getDrawingState());
- ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f",
- layer->isVisible() ? '+' : '-', i, layer->getName().string(),
- layer->getLayerStack(), state.z, layer->isVisible(), state.flags,
- static_cast<float>(state.color.a));
- i++;
- });
- }
+ mPendingSyncInputWindows = false;
+ mTransactionCV.broadcast();
}
// ---------------------------------------------------------------------------
@@ -5210,22 +5729,17 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& hw, int32_t minLayerZ,
- int32_t maxLayerZ,
+void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
- // as we need to interpret min/max layer Z in the top level Z space.
+ // as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
continue;
}
- const Layer::State& state(layer->getDrawingState());
// relative layers are traversed in Layer::traverseInZOrder
- if (state.zOrderRelativeOf != nullptr || state.z < minLayerZ || state.z > maxLayerZ) {
- continue;
- }
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(hw->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
return;
}
if (!layer->isVisible()) {
@@ -5236,6 +5750,119 @@
}
}
+void SurfaceFlinger::setAllowedDisplayConfigsInternal(
+ const android::sp<android::IBinder>& displayToken,
+ std::unique_ptr<const AllowedDisplayConfigs>&& allowedConfigs) {
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAllowedDisplayConfigsInternal: getPhysicalDisplayId failed");
+ return;
+ }
+
+ ALOGV("Updating allowed configs");
+ {
+ std::lock_guard lock(mAllowedConfigsLock);
+ mAllowedConfigs[*displayId] = std::move(allowedConfigs);
+ }
+
+ // make sure that the current config is still allowed
+ int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
+ if (!isConfigAllowed(*displayId, currentConfigIndex)) {
+ for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
+ if (isConfigAllowed(*displayId, config.configId)) {
+ // TODO: we switch to the first allowed config. In the future
+ // we may want to enhance this logic to pick a similar config
+ // to the current one
+ ALOGV("Old config is not allowed - switching to config %d", config.configId);
+ setDesiredActiveConfig(displayToken, config.configId,
+ Scheduler::ConfigEvent::Changed);
+ break;
+ }
+ }
+ }
+
+ // If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
+ // there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
+ if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
+ const auto performanceRefreshRate =
+ mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
+ if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
+ }
+ }
+}
+
+status_t SurfaceFlinger::setAllowedDisplayConfigs(const android::sp<android::IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) {
+ ATRACE_CALL();
+
+ if (!displayToken) {
+ ALOGE("setAllowedDisplayConfigs: displayToken is null");
+ return BAD_VALUE;
+ }
+
+ if (!allowedConfigs.size()) {
+ ALOGE("setAllowedDisplayConfigs: empty config set provided");
+ return BAD_VALUE;
+ }
+
+ {
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAllowedDisplayConfigs: display not found");
+ return NAME_NOT_FOUND;
+ }
+ }
+
+ auto allowedDisplayConfigsBuilder = AllowedDisplayConfigs::Builder();
+ for (int config : allowedConfigs) {
+ ALOGV("setAllowedDisplayConfigs: Adding config to the allowed configs = %d", config);
+ allowedDisplayConfigsBuilder.addConfig(config);
+ }
+ auto allowedDisplayConfigs = allowedDisplayConfigsBuilder.build();
+ postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+ setAllowedDisplayConfigsInternal(displayToken, std::move(allowedDisplayConfigs));
+ }));
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getAllowedDisplayConfigs(const android::sp<android::IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs) {
+ ATRACE_CALL();
+
+ if (!displayToken) {
+ ALOGE("getAllowedDisplayConfigs: displayToken is null");
+ return BAD_VALUE;
+ }
+
+ if (!outAllowedConfigs) {
+ ALOGE("getAllowedDisplayConfigs: outAllowedConfigs is null");
+ return BAD_VALUE;
+ }
+
+ ConditionalLock stateLock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getAllowedDisplayConfigs: display not found");
+ return NAME_NOT_FOUND;
+ }
+
+ std::lock_guard allowedConfigLock(mAllowedConfigsLock);
+ auto allowedConfigIterator = mAllowedConfigs.find(displayId.value());
+ if (allowedConfigIterator != mAllowedConfigs.end()) {
+ allowedConfigIterator->second->getAllowedConfigs(outAllowedConfigs);
+ }
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+void SetInputWindowsListener::onSetInputWindowsFinished() {
+ mFlinger->setInputWindowsFinished();
+}
+
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0914a09..0d39cb5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -17,69 +17,74 @@
#ifndef ANDROID_SURFACE_FLINGER_H
#define ANDROID_SURFACE_FLINGER_H
-#include <memory>
-#include <stdint.h>
#include <sys/types.h>
/*
* NOTE: Make sure this file doesn't include anything from <gl/ > or <gl2/ >
*/
-#include <cutils/compiler.h>
+#include <android-base/thread_annotations.h>
#include <cutils/atomic.h>
-
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-#include <utils/Trace.h>
-
-#include <ui/FenceTime.h>
-#include <ui/PixelFormat.h>
-#include <math/mat4.h>
-
+#include <cutils/compiler.h>
+#include <gui/BufferQueue.h>
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
-
#include <gui/OccupancyTracker.h>
-
#include <hardware/hwcomposer_defs.h>
-
+#include <input/ISetInputWindowsListener.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <math/mat4.h>
#include <serviceutils/PriorityDumper.h>
-
#include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/PixelFormat.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/Trace.h>
+#include <utils/threads.h>
+#include "AllowedDisplayConfigs.h"
#include "Barrier.h"
+#include "BufferStateLayerCache.h"
#include "DisplayDevice.h"
-#include "DispSync.h"
-#include "EventThread.h"
+#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/PowerAdvisor.h"
+#include "Effects/Daltonizer.h"
#include "FrameTracker.h"
#include "LayerStats.h"
#include "LayerVector.h"
-#include "MessageQueue.h"
+#include "RegionSamplingThread.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateStats.h"
+#include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncModulator.h"
+#include "SurfaceFlingerFactory.h"
#include "SurfaceInterceptor.h"
#include "SurfaceTracing.h"
-#include "StartPropertySetThread.h"
-#include "TimeStats/TimeStats.h"
-#include "VSyncModulator.h"
+#include "TransactionCompletedThread.h"
-#include "DisplayHardware/HWC2.h"
-#include "DisplayHardware/HWComposer.h"
-
-#include "Effects/Daltonizer.h"
-
+#include <atomic>
+#include <cstdint>
+#include <functional>
#include <map>
+#include <memory>
#include <mutex>
#include <queue>
+#include <set>
#include <string>
#include <thread>
+#include <type_traits>
+#include <unordered_map>
#include <utility>
-#include "RenderArea.h"
-
-#include <layerproto/LayerProtoHeader.h>
using namespace android::surfaceflinger;
@@ -94,17 +99,23 @@
class EventThread;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
+class IInputFlinger;
class InjectVSyncSource;
class Layer;
class Surface;
class SurfaceFlingerBE;
+class TimeStats;
class VSyncSource;
+namespace compositionengine {
+class DisplaySurface;
+} // namespace compositionengine
+
namespace impl {
class EventThread;
} // namespace impl
-namespace RE {
+namespace renderengine {
class RenderEngine;
}
@@ -114,6 +125,10 @@
class VrFlinger;
} // namespace dvr
+namespace surfaceflinger {
+class NativeWindowSurface;
+} // namespace surfaceflinger
+
// ---------------------------------------------------------------------------
enum {
@@ -130,53 +145,13 @@
ENHANCED = 2,
};
-// A thin interface to abstract creating instances of Surface (gui/Surface.h) to
-// use as a NativeWindow.
-class NativeWindowSurface {
-public:
- virtual ~NativeWindowSurface();
-
- // Gets the NativeWindow to use for the surface.
- virtual sp<ANativeWindow> getNativeWindow() const = 0;
-
- // Indicates that the surface should allocate its buffers now.
- virtual void preallocateBuffers() = 0;
-};
-
class SurfaceFlingerBE
{
public:
SurfaceFlingerBE();
- // The current hardware composer interface.
- //
- // The following thread safety rules apply when accessing mHwc, either
- // directly or via getHwComposer():
- //
- // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
- // only when switching into and out of vr. Recreating mHwc must only be
- // done on the main thread.
- //
- // 2. When accessing mHwc on the main thread, it's not necessary to acquire
- // mStateLock.
- //
- // 3. When accessing mHwc on a thread other than the main thread, we always
- // need to acquire mStateLock. This is because the main thread could be
- // in the process of destroying the current mHwc instance.
- //
- // The above thread safety rules only apply to SurfaceFlinger.cpp. In
- // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
- // destroy it, so it's always safe to access mHwc from any thread without
- // acquiring mStateLock.
- std::unique_ptr<HWComposer> mHwc;
-
const std::string mHwcServiceName; // "default" for real use, something else for testing.
- // constant members (no synchronization needed for access)
- std::unique_ptr<RE::RenderEngine> mRenderEngine;
- EGLContext mEGLContext;
- EGLDisplay mEGLDisplay;
-
FenceTimeline mGlCompositionDoneTimeline;
FenceTimeline mDisplayTimeline;
@@ -225,6 +200,14 @@
int32_t mComposerSequenceId;
};
+class SetInputWindowsListener : public BnSetInputWindowsListener {
+public:
+ SetInputWindowsListener(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger) {}
+ void onSetInputWindowsFinished() override;
+
+private:
+ const sp<SurfaceFlinger> mFlinger;
+};
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
@@ -279,23 +262,37 @@
// FramebufferSurface
static int64_t maxFrameBufferAcquiredBuffers;
- // Indicate if platform supports color management on its
- // wide-color display. This is typically found on devices
- // with wide gamut (e.g. Display-P3) display.
- // This also allows devices with wide-color displays that don't
- // want to support color management to disable color management.
+ // Indicate if a device has wide color gamut display. This is typically
+ // found on devices with wide color gamut (e.g. Display-P3) display.
static bool hasWideColorDisplay;
static int primaryDisplayOrientation;
+ // Indicate if device wants color management on its display.
+ static bool useColorManagement;
+
+ static bool useContextPriority;
+
+ // The data space and pixel format that SurfaceFlinger expects hardware composer
+ // to composite efficiently. Meaning under most scenarios, hardware composer
+ // will accept layers with the data space and pixel format.
+ static ui::Dataspace defaultCompositionDataspace;
+ static ui::PixelFormat defaultCompositionPixelFormat;
+
+ // The data space and pixel format that SurfaceFlinger expects hardware composer
+ // to composite efficiently for wide color gamut surfaces. Meaning under most scenarios,
+ // hardware composer will accept layers with the data space and pixel format.
+ static ui::Dataspace wideColorGamutCompositionDataspace;
+ static ui::PixelFormat wideColorGamutCompositionPixelFormat;
+
static char const* getServiceName() ANDROID_API {
return "SurfaceFlinger";
}
struct SkipInitializationTag {};
static constexpr SkipInitializationTag SkipInitialization;
- explicit SurfaceFlinger(SkipInitializationTag) ANDROID_API;
- SurfaceFlinger() ANDROID_API;
+ SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
+ explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
// must be called before clients can connect
void init() ANDROID_API;
@@ -303,10 +300,6 @@
// starts SurfaceFlinger main loop in the current thread
void run() ANDROID_API;
- enum {
- EVENT_VSYNC = HWC_EVENT_VSYNC
- };
-
// post an asynchronous message to the main thread
status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0);
@@ -316,8 +309,16 @@
// force full composition on all displays
void repaintEverything();
+ // force full composition on all displays without resetting the scheduler idle timer.
+ void repaintEverythingForHWC();
+
+ surfaceflinger::Factory& getFactory() { return mFactory; }
+
+ // The CompositionEngine encapsulates all composition related interfaces and actions.
+ compositionengine::CompositionEngine& getCompositionEngine() const;
+
// returns the default Display
- sp<const DisplayDevice> getDefaultDisplayDevice() const {
+ sp<const DisplayDevice> getDefaultDisplayDevice() {
Mutex::Autolock _l(mStateLock);
return getDefaultDisplayDeviceLocked();
}
@@ -331,7 +332,7 @@
// enable/disable h/w composer event
// TODO: this should be made accessible only to EventThread
- void setVsyncEnabled(int disp, int enabled);
+ void setPrimaryVsyncEnabled(bool enabled);
// called on the main thread by MessageQueue when an internal message
// is received
@@ -340,20 +341,33 @@
// for debugging only
// TODO: this should be made accessible only to HWComposer
- const Vector< sp<Layer> >& getLayerSortedByZForHwcDisplay(int id);
+ const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
- RE::RenderEngine& getRenderEngine() const { return *getBE().mRenderEngine; }
+ renderengine::RenderEngine& getRenderEngine() const;
bool authenticateSurfaceTextureLocked(
const sp<IGraphicBufferProducer>& bufferProducer) const;
+ inline void onLayerCreated() { mNumLayers++; }
+ inline void onLayerDestroyed() { mNumLayers--; }
+
+ TransactionCompletedThread& getTransactionCompletedThread() {
+ return mTransactionCompletedThread;
+ }
+
+ void setInputWindowsFinished();
+
private:
friend class Client;
friend class DisplayEventConnection;
friend class impl::EventThread;
friend class Layer;
friend class BufferLayer;
+ friend class BufferQueueLayer;
+ friend class BufferStateLayer;
friend class MonitoredProducer;
+ friend class RegionSamplingThread;
+ friend class SurfaceTracing;
// For unit tests
friend class TestableSurfaceFlinger;
@@ -363,6 +377,7 @@
enum { LOG_FRAME_STATS_PERIOD = 30*60*60 };
static const size_t MAX_LAYERS = 4096;
+ static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
@@ -400,72 +415,107 @@
/* ------------------------------------------------------------------------
* IBinder interface
*/
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
- virtual status_t dump(int fd, const Vector<String16>& args) { return priorityDump(fd, args); }
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
+ status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
+ bool callingThreadHasUnscopedSurfaceFlingerAccess() EXCLUDES(mStateLock);
/* ------------------------------------------------------------------------
* ISurfaceComposer interface
*/
- virtual sp<ISurfaceComposerClient> createConnection();
- virtual sp<ISurfaceComposerClient> createScopedConnection(const sp<IGraphicBufferProducer>& gbp);
- virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
- virtual void destroyDisplay(const sp<IBinder>& display);
- virtual sp<IBinder> getBuiltInDisplay(int32_t id);
- virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags);
- virtual void bootFinished();
- virtual bool authenticateSurfaceTexture(
- const sp<IGraphicBufferProducer>& bufferProducer) const;
- virtual status_t getSupportedFrameTimestamps(
- std::vector<FrameEvent>* outSupported) const;
- virtual sp<IDisplayEventConnection> createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp);
- virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
- ISurfaceComposer::Rotation rotation);
- virtual status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
- const Rect& sourceCrop, float frameScale, bool childrenOnly);
- virtual status_t getDisplayStats(const sp<IBinder>& display,
- DisplayStatInfo* stats);
- virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport);
- virtual status_t getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs);
- virtual int getActiveConfig(const sp<IBinder>& display);
- virtual status_t getDisplayColorModes(const sp<IBinder>& display,
- Vector<ui::ColorMode>* configs);
- virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display);
- virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode);
- virtual void setPowerMode(const sp<IBinder>& display, int mode);
- virtual status_t setActiveConfig(const sp<IBinder>& display, int id);
- virtual status_t clearAnimationFrameStats();
- virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
- virtual status_t getHdrCapabilities(const sp<IBinder>& display,
- HdrCapabilities* outCapabilities) const;
- virtual status_t enableVSyncInjections(bool enable);
- virtual status_t injectVSync(nsecs_t when);
- virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
-
+ sp<ISurfaceComposerClient> createConnection() override;
+ sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
+ void destroyDisplay(const sp<IBinder>& displayToken) override;
+ std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
+ sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
+ void setTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime) override;
+ void bootFinished() override;
+ bool authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& bufferProducer) const override;
+ status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp) override;
+ status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
+ const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ bool useIdentityTransform, ISurfaceComposer::Rotation rotation,
+ bool captureSecureLayers) override;
+ status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
+ const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+ const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
+ status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
+ status_t getDisplayConfigs(const sp<IBinder>& displayToken,
+ Vector<DisplayInfo>* configs) override {
+ Mutex::Autolock _l(mStateLock);
+ return getDisplayConfigsLocked(displayToken, configs);
+ }
+ status_t getDisplayConfigsLocked(const sp<IBinder>& displayToken, Vector<DisplayInfo>* configs)
+ REQUIRES(mStateLock);
+ int getActiveConfig(const sp<IBinder>& displayToken) override;
+ status_t getDisplayColorModes(const sp<IBinder>& displayToken,
+ Vector<ui::ColorMode>* configs) override;
+ status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+ ui::DisplayPrimaries &primaries);
+ ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
+ status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+ void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
+ status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
+ status_t clearAnimationFrameStats() override;
+ status_t getAnimationFrameStats(FrameStats* outStats) const override;
+ status_t getHdrCapabilities(const sp<IBinder>& displayToken,
+ 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 getColorManagement(bool* outGetColorManagement) const override;
+ status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
+ ui::Dataspace* outWideColorGamutDataspace,
+ ui::PixelFormat* outWideColorGamutPixelFormat) const override;
+ status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const override;
+ status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+ uint8_t componentMask,
+ uint64_t maxFrames) const override;
+ status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+ uint64_t timestamp,
+ DisplayedFrameStats* outStats) const override;
+ status_t getProtectedContentSupport(bool* outSupported) const override;
+ status_t isWideColorDisplay(const sp<IBinder>& displayToken,
+ bool* outIsWideColorDisplay) const override;
+ status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) override;
+ status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
+ status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) override;
+ status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs) override;
+ status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const override;
+ status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
- virtual void binderDied(const wp<IBinder>& who);
+ void binderDied(const wp<IBinder>& who) override;
/* ------------------------------------------------------------------------
* RefBase interface
*/
- virtual void onFirstRef();
+ void onFirstRef() override;
/* ------------------------------------------------------------------------
* HWC2::ComposerCallback / HWComposer::EventHandler interface
*/
- void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
+ void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
int64_t timestamp) override;
- void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+ void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
HWC2::Connection connection) override;
- void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override;
+ void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override;
/* ------------------------------------------------------------------------
* Message handling
@@ -478,18 +528,25 @@
void signalRefresh();
// called on the main thread in response to initializeDisplays()
- void onInitializeDisplays();
- // called on the main thread in response to setActiveConfig()
- void setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode);
+ void onInitializeDisplays() REQUIRES(mStateLock);
+ // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
+ void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
+ Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+ // Once HWC has returned the present fence, this sets the active config and a new refresh
+ // rate in SF. It also triggers HWC vsync.
+ void setActiveConfigInternal() REQUIRES(mStateLock);
+ // Active config is updated on INVALIDATE call in a state machine-like manner. When the
+ // desired config was set, HWC needs to update the panel on the next refresh, and when
+ // we receive the fence back, we know that the process was complete. It returns whether
+ // we need to wait for the next invalidate
+ bool performSetActiveConfig();
// called on the main thread in response to setPowerMode()
- void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode,
- bool stateLockHeld);
+ void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
- // Called on the main thread in response to setActiveColorMode()
- void setActiveColorModeInternal(const sp<DisplayDevice>& hw,
- ui::ColorMode colorMode,
- ui::Dataspace dataSpace,
- ui::RenderIntent renderIntent);
+ // called on the main thread in response to setAllowedDisplayConfigs()
+ void setAllowedDisplayConfigsInternal(
+ const sp<IBinder>& displayToken,
+ std::unique_ptr<const AllowedDisplayConfigs>&& allowedConfigs) REQUIRES(mStateLock);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -500,8 +557,12 @@
void handleMessageRefresh();
void handleTransaction(uint32_t transactionFlags);
- void handleTransactionLocked(uint32_t transactionFlags);
+ void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+ void updateInputFlinger();
+ void updateInputWindowInfo();
+ void commitInputWindowCommands() REQUIRES(mStateLock);
+ void executeInputWindowCommands();
void updateCursorAsync();
/* handlePageFlip - latch a new buffer if available and compute the dirty
@@ -513,59 +574,70 @@
/* ------------------------------------------------------------------------
* Transactions
*/
+ void applyTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands,
+ const int64_t desiredPresentTime, const int64_t postTime,
+ bool privileged) REQUIRES(mStateLock);
+ bool flushTransactionQueues();
uint32_t getTransactionFlags(uint32_t flags);
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
- uint32_t setTransactionFlags(uint32_t flags, VSyncModulator::TransactionStart transactionStart);
- void commitTransaction();
+ uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+ void latchAndReleaseBuffer(const sp<Layer>& layer);
+ void commitTransaction() REQUIRES(mStateLock);
bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
- uint32_t setClientStateLocked(const ComposerState& composerState);
- uint32_t setDisplayStateLocked(const DisplayState& s);
- void setDestroyStateLocked(const ComposerState& composerState);
+ bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+ const Vector<ComposerState>& states);
+ uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
+ int64_t postTime, bool privileged) REQUIRES(mStateLock);
+ uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
+ uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
+ REQUIRES(mStateLock);
/* ------------------------------------------------------------------------
* Layer management
*/
- status_t createLayer(const String8& name, const sp<Client>& client,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
+ status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags, LayerMetadata metadata,
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
- status_t createBufferLayer(const sp<Client>& client, const String8& name,
- uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
- sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
- sp<Layer>* outLayer);
+ status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w,
+ uint32_t h, uint32_t flags, LayerMetadata metadata,
+ PixelFormat& format, sp<IBinder>* outHandle,
+ sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
- status_t createColorLayer(const sp<Client>& client, const String8& name,
- uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle,
- sp<Layer>* outLayer);
+ status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w,
+ uint32_t h, uint32_t flags, LayerMetadata metadata,
+ sp<IBinder>* outHandle, sp<Layer>* outLayer);
- status_t createContainerLayer(const sp<Client>& client, const String8& name,
- uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle,
- sp<Layer>* outLayer);
+ status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h,
+ uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
+ sp<Layer>* outLayer);
+
+ status_t createContainerLayer(const sp<Client>& client, const String8& name, uint32_t w,
+ uint32_t h, uint32_t flags, LayerMetadata metadata,
+ sp<IBinder>* outHandle, sp<Layer>* outLayer);
String8 getUniqueLayerName(const String8& name);
- // called in response to the window-manager calling
- // ISurfaceComposerClient::destroySurface()
- status_t onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle);
-
// called when all clients have released all their references to
// this layer meaning it is entirely safe to destroy all
// resources associated to this layer.
- status_t onLayerDestroyed(const wp<Layer>& layer);
-
- // remove a layer from SurfaceFlinger immediately
- status_t removeLayer(const sp<Layer>& layer, bool topLevelOnly = false);
- status_t removeLayerLocked(const Mutex&, const sp<Layer>& layer, bool topLevelOnly = false);
+ void onHandleDestroyed(sp<Layer>& layer);
+ void markLayerPendingRemovalLocked(const sp<Layer>& layer);
// add a layer to SurfaceFlinger
status_t addClientLayer(const sp<Client>& client,
const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc,
const sp<Layer>& lbc,
- const sp<Layer>& parent);
+ const sp<Layer>& parent,
+ bool addToCurrentState);
+
+ // Traverse through all the layers and compute and cache its bounds.
+ void computeLayerBounds();
/* ------------------------------------------------------------------------
* Boot animation, on/off animations and screen capture
@@ -574,18 +646,21 @@
void startBootAnim();
void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- bool yswap, bool useIdentityTransform);
+ ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ int* outSyncFd);
status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer,
+ sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
bool useIdentityTransform);
+ status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
bool forSystem, int* outSyncFd);
- void traverseLayersInDisplay(const sp<const DisplayDevice>& display, int32_t minLayerZ,
- int32_t maxLayerZ, const LayerVector::Visitor& visitor);
+ void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
+ const LayerVector::Visitor& visitor);
- sp<StartPropertySetThread> mStartPropertySetThread = nullptr;
+ sp<StartPropertySetThread> mStartPropertySetThread;
/* ------------------------------------------------------------------------
* Properties
@@ -604,38 +679,36 @@
// called when starting, or restarting after system_server death
void initializeDisplays();
- sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const {
- Mutex::Autolock _l(mStateLock);
- return getDisplayDeviceLocked(dpy);
+ sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) const {
+ Mutex::Autolock _l(mStateLock);
+ return getDisplayDeviceLocked(displayToken);
}
- sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) {
- Mutex::Autolock _l(mStateLock);
- return getDisplayDeviceLocked(dpy);
+ sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) {
+ Mutex::Autolock _l(mStateLock);
+ return getDisplayDeviceLocked(displayToken);
}
// NOTE: can only be called from the main thread or with mStateLock held
- sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) const {
- return mDisplays.valueFor(dpy);
+ sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const {
+ 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>& dpy) {
- return mDisplays.valueFor(dpy);
+ sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) {
+ const auto it = mDisplays.find(displayToken);
+ return it == mDisplays.end() ? nullptr : it->second;
}
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const {
- return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
+ return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
}
- int32_t getDisplayType(const sp<IBinder>& display) {
- if (!display.get()) return NAME_NOT_FOUND;
- for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
- if (display == mBuiltinDisplays[i]) {
- return i;
- }
+ sp<DisplayDevice> getDefaultDisplayDeviceLocked() {
+ if (const auto token = getInternalDisplayTokenLocked()) {
+ return getDisplayDeviceLocked(token);
}
- return NAME_NOT_FOUND;
+ return nullptr;
}
// mark a region of a layer stack dirty. this updates the dirty
@@ -646,112 +719,197 @@
* H/W composer
*/
- HWComposer& getHwComposer() const { return *getBE().mHwc; }
+ // The current hardware composer interface.
+ //
+ // The following thread safety rules apply when accessing mHwc, either
+ // directly or via getHwComposer():
+ //
+ // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
+ // only when switching into and out of vr. Recreating mHwc must only be
+ // done on the main thread.
+ //
+ // 2. When accessing mHwc on the main thread, it's not necessary to acquire
+ // mStateLock.
+ //
+ // 3. When accessing mHwc on a thread other than the main thread, we always
+ // need to acquire mStateLock. This is because the main thread could be
+ // in the process of destroying the current mHwc instance.
+ //
+ // The above thread safety rules only apply to SurfaceFlinger.cpp. In
+ // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
+ // destroy it, so it's always safe to access mHwc from any thread without
+ // acquiring mStateLock.
+ HWComposer& getHwComposer() const;
/* ------------------------------------------------------------------------
* Compositing
*/
void invalidateHwcGeometry();
- void computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
- Region& dirtyRegion, Region& opaqueRegion);
+ void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion,
+ Region& opaqueRegion);
- void preComposition(nsecs_t refreshStartTime);
- void postComposition(nsecs_t refreshStartTime);
- void updateCompositorTiming(
- nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
- std::shared_ptr<FenceTime>& presentFenceTime);
- void setCompositorTimingSnapped(
- nsecs_t vsyncPhase, nsecs_t vsyncInterval,
- nsecs_t compositeToPresentLatency);
+ void preComposition();
+ void postComposition();
+ void getCompositorTiming(CompositorTiming* compositorTiming);
+ void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
+ std::shared_ptr<FenceTime>& presentFenceTime);
+ void setCompositorTimingSnapped(const DisplayStatInfo& stats,
+ nsecs_t compositeToPresentLatency);
void rebuildLayerStacks();
- ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& displayDevice,
+ ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& display,
ui::Dataspace* outHdrDataSpace) const;
// Returns the appropriate ColorMode, Dataspace and RenderIntent for the
// DisplayDevice. The function only returns the supported ColorMode,
// Dataspace and RenderIntent.
- void pickColorMode(const sp<DisplayDevice>& displayDevice,
- ui::ColorMode* outMode,
- ui::Dataspace* outDataSpace,
- ui::RenderIntent* outRenderIntent) const;
+ void pickColorMode(const sp<DisplayDevice>& display, ui::ColorMode* outMode,
+ ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const;
- void setUpHWComposer();
- void doComposition();
- void doDebugFlashRegions();
- void doTracing(const char* where);
+ void calculateWorkingSet();
+ /*
+ * beginFrame - This function handles any pre-frame processing that needs to be
+ * prior to any CompositionInfo handling and is not dependent on data in
+ * CompositionInfo
+ */
+ void beginFrame(const sp<DisplayDevice>& display);
+ /* prepareFrame - This function will call into the DisplayDevice to prepare a
+ * frame after CompositionInfo has been programmed. This provides a mechanism
+ * to prepare the hardware composer
+ */
+ void prepareFrame(const sp<DisplayDevice>& display);
+ void doComposition(const sp<DisplayDevice>& display, bool repainEverything);
+ void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
void logLayerStats();
- void doDisplayComposition(const sp<const DisplayDevice>& displayDevice, const Region& dirtyRegion);
+ void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
- // compose surfaces for display hw. this fails if using GL and the surface
- // has been destroyed and is no longer valid.
- bool doComposeSurfaces(const sp<const DisplayDevice>& displayDevice);
+ // This fails if using GL and the surface has been destroyed. readyFence
+ // will be populated if using GL and native fence sync is supported, to
+ // signal when drawing has completed.
+ bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm,
+ base::unique_fd* readyFence);
- void postFramebuffer();
- void drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const;
+ void postFramebuffer(const sp<DisplayDevice>& display);
+ void postFrame();
+ void drawWormhole(const Region& region) const;
/* ------------------------------------------------------------------------
* Display management
*/
- DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display,
- HWC2::Connection connection) const;
- sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId,
- const DisplayDeviceState& state,
- const sp<DisplaySurface>& dispSurface,
- const sp<IGraphicBufferProducer>& producer);
+ sp<DisplayDevice> setupNewDisplayDeviceInternal(
+ const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+ const DisplayDeviceState& state,
+ const sp<compositionengine::DisplaySurface>& dispSurface,
+ const sp<IGraphicBufferProducer>& producer);
void processDisplayChangesLocked();
void processDisplayHotplugEventsLocked();
+ void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
+
/* ------------------------------------------------------------------------
* VSync
*/
- void enableHardwareVsync();
- void resyncToHardwareVsync(bool makeAvailable);
- void disableHardwareVsync(bool makeUnavailable);
+ nsecs_t getVsyncPeriod() const REQUIRES(mStateLock);
-public:
- void resyncWithRateLimit();
- void getCompositorTiming(CompositorTiming* compositorTiming);
-private:
+ // Sets the refresh rate by switching active configs, if they are available for
+ // the desired refresh rate.
+ void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType,
+ Scheduler::ConfigEvent event) REQUIRES(mStateLock);
- /* ------------------------------------------------------------------------
+ bool isConfigAllowed(const DisplayId& displayId, int32_t config);
+
+ /*
+ * Display identification
+ */
+ sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const {
+ const auto it = mPhysicalDisplayTokens.find(displayId);
+ return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
+ }
+
+ std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const {
+ for (const auto& [id, token] : mPhysicalDisplayTokens) {
+ if (token == displayToken) {
+ return id;
+ }
+ }
+ return {};
+ }
+
+ // TODO(b/74619554): Remove special cases for primary display.
+ sp<IBinder> getInternalDisplayTokenLocked() const {
+ const auto displayId = getInternalDisplayIdLocked();
+ return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
+ }
+
+ std::optional<DisplayId> getInternalDisplayIdLocked() const {
+ const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
+ return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+ }
+
+ /*
* Debugging & dumpsys
*/
-public:
- status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) {
- return doDump(fd, Vector<String16>(), asProto);
+ using DumpArgs = Vector<String16>;
+ using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
+ template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+ static Dumper dumper(F&& dump) {
+ using namespace std::placeholders;
+ return std::bind(std::forward<F>(dump), _3);
}
- status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) {
- return doDump(fd, args, asProto);
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper dumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _3);
}
-private:
- void listLayersLocked(const Vector<String16>& args, size_t& index, String8& result) const;
- void dumpStatsLocked(const Vector<String16>& args, size_t& index, String8& result) const;
- void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result);
- void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
- void appendSfConfigString(String8& result) const;
- void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
- TraverseLayersFunction traverseLayers);
+ template <typename F>
+ Dumper argsDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _3);
+ }
+ template <typename F>
+ Dumper protoDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _2, _3);
+ }
+
+ void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+
+ void appendSfConfigString(std::string& result) const;
+ void listLayersLocked(std::string& result) const;
+ void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+ void clearStatsLocked(const DumpArgs& args, std::string& result);
+ void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void logFrameStats();
- void dumpStaticScreenStats(String8& result) const;
+ void dumpVSync(std::string& result) const REQUIRES(mStateLock);
+ void dumpStaticScreenStats(std::string& result) const;
// Not const because each Layer needs to query Fences and cache timestamps.
- void dumpFrameEventsLocked(String8& result);
+ void dumpFrameEventsLocked(std::string& result);
void recordBufferingStats(const char* layerName,
std::vector<OccupancyTracker::Segment>&& history);
- void dumpBufferingStats(String8& result) const;
- void dumpWideColorInfo(String8& result) const;
+ void dumpBufferingStats(std::string& result) const;
+ void dumpDisplayIdentificationData(std::string& result) const;
+ void dumpWideColorInfo(std::string& result) const;
LayersProto dumpProtoInfo(LayerVector::StateSet stateSet) const;
- LayersProto dumpVisibleLayersProtoInfo(int32_t hwcId) const;
+ void withTracingLock(std::function<void()> operation) REQUIRES(mStateLock);
+ LayersProto dumpVisibleLayersProtoInfo(const sp<DisplayDevice>& display) const;
bool isLayerTripleBufferingDisabled() const {
return this->mLayerTripleBufferingDisabled;
}
- status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+
+ status_t doDump(int fd, const DumpArgs& args, bool asProto);
+
+ status_t dumpCritical(int fd, const DumpArgs&, bool asProto);
+
+ status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
+ return doDump(fd, args, asProto);
+ }
/* ------------------------------------------------------------------------
* VrFlinger
@@ -767,15 +925,20 @@
* Attributes
*/
+ surfaceflinger::Factory& mFactory;
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
- volatile int32_t mTransactionFlags;
+ std::atomic<int32_t> mTransactionFlags{0};
Condition mTransactionCV;
bool mTransactionPending;
bool mAnimTransactionPending;
SortedVector< sp<Layer> > mLayersPendingRemoval;
+ // guards access to the mDrawing state if tracing is enabled.
+ mutable std::mutex mDrawingStateLock;
+
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
@@ -789,32 +952,38 @@
bool mLayersRemoved;
bool mLayersAdded;
- // access must be protected by mInvalidateLock
- volatile int32_t mRepaintEverything;
+ std::atomic<bool> mRepaintEverything{false};
// constant members (no synchronization needed for access)
nsecs_t mBootTime;
bool mGpuToCpuSupported;
- std::unique_ptr<EventThread> mEventThread;
- std::unique_ptr<EventThread> mSFEventThread;
std::unique_ptr<EventThread> mInjectorEventThread;
- std::unique_ptr<VSyncSource> mEventThreadSource;
- std::unique_ptr<VSyncSource> mSfEventThreadSource;
std::unique_ptr<InjectVSyncSource> mVSyncInjector;
std::unique_ptr<EventControlThread> mEventControlThread;
- sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
+ // Calculates correct offsets.
VSyncModulator mVsyncModulator;
+ // Keeps track of all available phase offsets for different refresh types.
+ std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
// Can only accessed from the main thread, these members
// don't need synchronization
State mDrawingState{LayerVector::StateSet::Drawing};
bool mVisibleRegionsDirty;
+ // Set during transaction commit stage to track if the input info for a layer has changed.
+ bool mInputInfoChanged{false};
bool mGeometryInvalid;
bool mAnimCompositionPending;
std::vector<sp<Layer>> mLayersWithQueuedFrames;
+ // Tracks layers that need to update a display's dirty region.
+ std::vector<sp<Layer>> mLayersPendingRefresh;
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+ // True if in the previous frame at least one layer was composed via the GPU.
bool mHadClientComposition = false;
+ // True if in the previous frame at least one layer was composed via HW Composer.
+ // Note that it is possible for a frame to be composed via both client and device
+ // composition, for example in the case of overlays.
+ bool mHadDeviceComposition = false;
enum class BootStage {
BOOTLOADER,
@@ -824,7 +993,7 @@
BootStage mBootStage;
struct HotplugEvent {
- hwc2_display_t display;
+ hwc2_display_t hwcDisplayId;
HWC2::Connection connection = HWC2::Connection::Invalid;
};
// protected by mStateLock
@@ -832,41 +1001,45 @@
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
- DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays;
+ std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
+ std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
// don't use a lock for these, we don't care
int mDebugRegion;
int mDebugDisableHWC;
int mDebugDisableTransformHint;
+ bool mDebugEnableProtectedContent;
volatile nsecs_t mDebugInSwapBuffers;
- nsecs_t mLastSwapBufferTime;
volatile nsecs_t mDebugInTransaction;
nsecs_t mLastTransactionTime;
+ nsecs_t mPostFramebufferTime;
bool mForceFullDamage;
bool mPropagateBackpressure = true;
- std::unique_ptr<SurfaceInterceptor> mInterceptor =
- std::make_unique<impl::SurfaceInterceptor>(this);
+ std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
SurfaceTracing mTracing;
+ bool mTracingEnabled = false;
+ bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false;
LayerStats mLayerStats;
- TimeStats& mTimeStats = TimeStats::getInstance();
+ std::shared_ptr<TimeStats> mTimeStats;
bool mUseHwcVirtualDisplays = false;
+ std::atomic<uint32_t> mFrameMissedCount{0};
+ std::atomic<uint32_t> mHwcFrameMissedCount{0};
+ std::atomic<uint32_t> mGpuFrameMissedCount{0};
+
+ TransactionCompletedThread mTransactionCompletedThread;
// Restrict layers to use two buffers in their bufferqueues.
bool mLayerTripleBufferingDisabled = false;
// these are thread safe
- mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()};
+ mutable std::unique_ptr<MessageQueue> mEventQueue{mFactory.createMessageQueue()};
FrameTracker mAnimFrameTracker;
- DispSync mPrimaryDispSync;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
Vector<Layer const *> mDestroyedLayers;
- // protected by mHWVsyncLock
- Mutex mHWVsyncLock;
- bool mPrimaryHWVsyncEnabled;
- bool mHWVsyncAvailable;
+ nsecs_t mRefreshStartTime;
std::atomic<bool> mRefreshPending{false};
@@ -877,6 +1050,31 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& strongPointer) const {
+ return std::hash<IBinder*>{}(strongPointer.get());
+ }
+ };
+ struct TransactionState {
+ TransactionState(const Vector<ComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ int64_t desiredPresentTime, int64_t postTime, bool privileged)
+ : states(composerStates),
+ displays(displayStates),
+ flags(transactionFlags),
+ desiredPresentTime(desiredPresentTime),
+ postTime(postTime),
+ privileged(privileged) {}
+
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags;
+ const int64_t desiredPresentTime;
+ const int64_t postTime;
+ bool privileged;
+ };
+ std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+
/* ------------------------------------------------------------------------
* Feature prototyping
*/
@@ -898,20 +1096,76 @@
std::thread::id mMainThreadId;
DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED;
- // Applied on Display P3 layers when the render intent is non-colorimetric.
- mat4 mEnhancedSaturationMatrix;
- using CreateBufferQueueFunction =
- std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
- sp<IGraphicBufferConsumer>* /* outConsumer */,
- bool /* consumerIsSurfaceFlinger */)>;
- CreateBufferQueueFunction mCreateBufferQueue;
+ // Color mode forced by setting persist.sys.sf.color_mode, it must:
+ // 1. not be NATIVE color mode, NATIVE color mode means no forced color mode;
+ // 2. be one of the supported color modes returned by hardware composer, otherwise
+ // it will not be respected.
+ // persist.sys.sf.color_mode will only take effect when persist.sys.sf.native_mode
+ // is not set to 1.
+ // This property can be used to force SurfaceFlinger to always pick a certain color mode.
+ ui::ColorMode mForceColorMode = ui::ColorMode::NATIVE;
- using CreateNativeWindowSurfaceFunction =
- std::function<std::unique_ptr<NativeWindowSurface>(const sp<IGraphicBufferProducer>&)>;
- CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
+ ui::Dataspace mDefaultCompositionDataspace;
+ ui::Dataspace mWideColorGamutCompositionDataspace;
SurfaceFlingerBE mBE;
+ std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+
+ /* ------------------------------------------------------------------------
+ * Scheduler
+ */
+ bool mUseSmart90ForVideo = false;
+ std::unique_ptr<Scheduler> mScheduler;
+ sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
+ sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+ std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+
+ std::unordered_map<DisplayId, std::shared_ptr<scheduler::RefreshRateConfigs>>
+ mRefreshRateConfigs;
+
+ std::mutex mAllowedConfigsLock;
+ std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs
+ GUARDED_BY(mAllowedConfigsLock);
+
+ struct ActiveConfigInfo {
+ int configId;
+ sp<IBinder> displayToken;
+ Scheduler::ConfigEvent event;
+
+ bool operator!=(const ActiveConfigInfo& other) const {
+ if (configId != other.configId) {
+ return true;
+ }
+ return (displayToken != other.displayToken);
+ }
+ };
+ 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
+ // the process.
+ ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
+ // This bit can be set at any point in time when the system wants the new config.
+ ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+
+ // below flags are set by main thread only
+ bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
+ bool mCheckPendingFence = false;
+
+ /* ------------------------------------------------------------------------ */
+ bool mLumaSampling = true;
+ sp<RegionSamplingThread> mRegionSamplingThread;
+
+ sp<IInputFlinger> mInputFlinger;
+
+ InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
+ // Should only be accessed by the main thread.
+ InputWindowCommands mInputWindowCommands;
+ ui::DisplayPrimaries mInternalDisplayPrimaries;
+
+ sp<SetInputWindowsListener> mSetInputWindowsListener;
+ bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+ Hwc2::impl::PowerAdvisor mPowerAdvisor;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
new file mode 100644
index 0000000..26d2c21
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2018 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 <compositionengine/impl/CompositionEngine.h>
+#include <ui/GraphicBuffer.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "Layer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceInterceptor.h"
+
+#include "DisplayHardware/ComposerHal.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
+#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
+
+namespace android::surfaceflinger {
+
+sp<SurfaceFlinger> createSurfaceFlinger() {
+ class Factory final : public surfaceflinger::Factory {
+ public:
+ Factory() = default;
+ ~Factory() = default;
+
+ std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework,
+ int64_t dispSyncPresentTimeOffset) override {
+ // Note: We create a local temporary with the real DispSync implementation
+ // type temporarily so we can initialize it with the configured values,
+ // before storing it for more generic use using the interface type.
+ auto primaryDispSync = std::make_unique<android::impl::DispSync>(name);
+ primaryDispSync->init(hasSyncFramework, dispSyncPresentTimeOffset);
+ return primaryDispSync;
+ }
+
+ std::unique_ptr<EventControlThread> createEventControlThread(
+ std::function<void(bool)> setVSyncEnabled) override {
+ return std::make_unique<android::impl::EventControlThread>(setVSyncEnabled);
+ }
+
+ std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override {
+ return std::make_unique<android::impl::HWComposer>(
+ std::make_unique<Hwc2::impl::Composer>(serviceName));
+ }
+
+ std::unique_ptr<MessageQueue> createMessageQueue() override {
+ return std::make_unique<android::impl::MessageQueue>();
+ }
+
+ std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ return std::make_unique<scheduler::impl::PhaseOffsets>();
+ }
+
+ std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) override {
+ return std::make_unique<Scheduler>(callback);
+ }
+
+ std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(
+ SurfaceFlinger* flinger) override {
+ return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+ }
+
+ sp<StartPropertySetThread> createStartPropertySetThread(
+ bool timestampPropertyValue) override {
+ return new StartPropertySetThread(timestampPropertyValue);
+ }
+
+ sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
+ return new DisplayDevice(std::move(creationArgs));
+ }
+
+ sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::string requestorName) override {
+ return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+ }
+
+ void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer,
+ bool consumerIsSurfaceFlinger) override {
+ BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+ }
+
+ std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+ const sp<IGraphicBufferProducer>& producer) override {
+ return surfaceflinger::impl::createNativeWindowSurface(producer);
+ }
+
+ std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
+ return compositionengine::impl::createCompositionEngine();
+ }
+
+ sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override {
+ return new ContainerLayer(args);
+ }
+
+ sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override {
+ return new BufferQueueLayer(args);
+ }
+
+ sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override {
+ return new BufferStateLayer(args);
+ }
+
+ sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
+ return new ColorLayer(args);
+ }
+
+ std::shared_ptr<TimeStats> createTimeStats() override {
+ return std::make_shared<android::impl::TimeStats>();
+ }
+ };
+ static Factory factory;
+
+ return new SurfaceFlinger(factory);
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
new file mode 100644
index 0000000..fc1d0f8
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 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 <cinttypes>
+#include <functional>
+#include <memory>
+#include <string>
+
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+typedef int32_t PixelFormat;
+
+class BufferQueueLayer;
+class BufferStateLayer;
+class ColorLayer;
+class ContainerLayer;
+class DisplayDevice;
+class DispSync;
+class EventControlThread;
+class GraphicBuffer;
+class HWComposer;
+class IGraphicBufferConsumer;
+class IGraphicBufferProducer;
+class MessageQueue;
+class Scheduler;
+class StartPropertySetThread;
+class SurfaceFlinger;
+class SurfaceInterceptor;
+class TimeStats;
+
+struct DisplayDeviceCreationArgs;
+struct LayerCreationArgs;
+
+namespace compositionengine {
+class CompositionEngine;
+} // namespace compositionengine
+
+namespace scheduler {
+class PhaseOffsets;
+} // namespace scheduler
+namespace surfaceflinger {
+
+class NativeWindowSurface;
+
+// The interface that SurfaceFlinger uses to create all of the implementations
+// of each interface.
+class Factory {
+public:
+ virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework,
+ int64_t dispSyncPresentTimeOffset) = 0;
+ virtual std::unique_ptr<EventControlThread> createEventControlThread(
+ std::function<void(bool)> setVSyncEnabled) = 0;
+ virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
+ virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
+ virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
+ virtual std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) = 0;
+ virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
+
+ virtual sp<StartPropertySetThread> createStartPropertySetThread(
+ bool timestampPropertyValue) = 0;
+ virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0;
+ virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, std::string requestorName) = 0;
+ virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer,
+ bool consumerIsSurfaceFlinger) = 0;
+ virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+ const sp<IGraphicBufferProducer>&) = 0;
+
+ virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0;
+
+ virtual sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) = 0;
+ virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
+ virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
+ virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+
+ virtual std::shared_ptr<TimeStats> createTimeStats() = 0;
+
+protected:
+ ~Factory() = default;
+};
+
+ANDROID_API sp<SurfaceFlinger> createSurfaceFlinger();
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
new file mode 100644
index 0000000..e130511
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -0,0 +1,271 @@
+
+#include <sysprop/SurfaceFlingerProperties.sysprop.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 <configstore/Utils.h>
+
+#include <cstdlib>
+#include <tuple>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+namespace sysprop {
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+using android::hardware::graphics::common::V1_2::Dataspace;
+using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::ui::DisplayPrimaries;
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(
+ defaultValue);
+}
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::vsync_sf_event_phase_offset_ns();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(
+ defaultValue);
+}
+
+bool use_context_priority(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_context_priority();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useContextPriority>(
+ defaultValue);
+}
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::max_frame_buffer_acquired_buffers();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(
+ defaultValue);
+}
+
+bool has_wide_color_display(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::has_wide_color_display();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(
+ defaultValue);
+}
+
+bool running_without_sync_framework(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::running_without_sync_framework();
+ if (temp.has_value()) {
+ return !(*temp);
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(defaultValue);
+}
+
+bool has_HDR_display(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::has_HDR_display();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(defaultValue);
+}
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::present_time_offset_from_vsync_ns();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(
+ defaultValue);
+}
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::force_hwc_copy_for_virtual_displays();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(
+ defaultValue);
+}
+
+int64_t max_virtual_display_dimension(int64_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::max_virtual_display_dimension();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getUInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(
+ defaultValue);
+}
+
+bool use_vr_flinger(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_vr_flinger();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(defaultValue);
+}
+
+bool start_graphics_allocator_service(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::start_graphics_allocator_service();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(
+ defaultValue);
+}
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+ SurfaceFlingerProperties::primary_display_orientation_values defaultValue) {
+ auto temp = SurfaceFlingerProperties::primary_display_orientation();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ auto configDefault = DisplayOrientation::ORIENTATION_0;
+ switch (defaultValue) {
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
+ configDefault = DisplayOrientation::ORIENTATION_90;
+ break;
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
+ configDefault = DisplayOrientation::ORIENTATION_180;
+ break;
+ case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
+ configDefault = DisplayOrientation::ORIENTATION_270;
+ break;
+ default:
+ configDefault = DisplayOrientation::ORIENTATION_0;
+ break;
+ }
+ DisplayOrientation result =
+ getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
+ &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
+ configDefault);
+ switch (result) {
+ case DisplayOrientation::ORIENTATION_90:
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90;
+ case DisplayOrientation::ORIENTATION_180:
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180;
+ case DisplayOrientation::ORIENTATION_270:
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270;
+ default:
+ break;
+ }
+ return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
+}
+
+bool use_color_management(bool defaultValue) {
+ auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
+ auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display();
+ auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display();
+
+ auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement :
+ defaultValue;
+ auto tmpHasHDRDisplayVal = tmpHasHDRDisplay.has_value() ? *tmpHasHDRDisplay :
+ defaultValue;
+ auto tmpHasWideColorDisplayVal = tmpHasWideColorDisplay.has_value() ? *tmpHasWideColorDisplay :
+ defaultValue;
+
+ return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal;
+}
+
+int64_t default_composition_dataspace(Dataspace defaultValue) {
+ auto temp = SurfaceFlingerProperties::default_composition_dataspace();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return static_cast<int64_t>(defaultValue);
+}
+
+int32_t default_composition_pixel_format(PixelFormat defaultValue) {
+ auto temp = SurfaceFlingerProperties::default_composition_pixel_format();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return static_cast<int32_t>(defaultValue);
+}
+
+int64_t wcg_composition_dataspace(Dataspace defaultValue) {
+ auto temp = SurfaceFlingerProperties::wcg_composition_dataspace();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return static_cast<int64_t>(defaultValue);
+}
+
+int32_t wcg_composition_pixel_format(PixelFormat defaultValue) {
+ auto temp = SurfaceFlingerProperties::wcg_composition_pixel_format();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return static_cast<int32_t>(defaultValue);
+}
+
+int32_t set_idle_timer_ms(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
+bool use_smart_90_for_video(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
+#define DISPLAY_PRIMARY_SIZE 3
+
+constexpr float kSrgbRedX = 0.4123f;
+constexpr float kSrgbRedY = 0.2126f;
+constexpr float kSrgbRedZ = 0.0193f;
+constexpr float kSrgbGreenX = 0.3576f;
+constexpr float kSrgbGreenY = 0.7152f;
+constexpr float kSrgbGreenZ = 0.1192f;
+constexpr float kSrgbBlueX = 0.1805f;
+constexpr float kSrgbBlueY = 0.0722f;
+constexpr float kSrgbBlueZ = 0.9506f;
+constexpr float kSrgbWhiteX = 0.9505f;
+constexpr float kSrgbWhiteY = 1.0000f;
+constexpr float kSrgbWhiteZ = 1.0891f;
+
+DisplayPrimaries getDisplayNativePrimaries() {
+ auto mDisplay_primary_red = SurfaceFlingerProperties::display_primary_red();
+ auto mDisplay_primary_green = SurfaceFlingerProperties::display_primary_green();
+ auto mDisplay_primary_blue = SurfaceFlingerProperties::display_primary_blue();
+ auto mDisplay_primary_white = SurfaceFlingerProperties::display_primary_white();
+ // To avoid null point exception.
+ mDisplay_primary_red.resize(DISPLAY_PRIMARY_SIZE);
+ mDisplay_primary_green.resize(DISPLAY_PRIMARY_SIZE);
+ mDisplay_primary_blue.resize(DISPLAY_PRIMARY_SIZE);
+ mDisplay_primary_white.resize(DISPLAY_PRIMARY_SIZE);
+ DisplayPrimaries primaries =
+ {{static_cast<float>(mDisplay_primary_red[0].value_or(kSrgbRedX)),
+ static_cast<float>(mDisplay_primary_red[1].value_or(kSrgbRedY)),
+ static_cast<float>(mDisplay_primary_red[2].value_or(kSrgbRedZ))},
+ {static_cast<float>(mDisplay_primary_green[0].value_or(kSrgbGreenX)),
+ static_cast<float>(mDisplay_primary_green[1].value_or(kSrgbGreenY)),
+ static_cast<float>(mDisplay_primary_green[2].value_or(kSrgbGreenZ))},
+ {static_cast<float>(mDisplay_primary_blue[0].value_or(kSrgbBlueX)),
+ static_cast<float>(mDisplay_primary_blue[1].value_or(kSrgbBlueY)),
+ static_cast<float>(mDisplay_primary_blue[2].value_or(kSrgbBlueZ))},
+ {static_cast<float>(mDisplay_primary_white[0].value_or(kSrgbWhiteX)),
+ static_cast<float>(mDisplay_primary_white[1].value_or(kSrgbWhiteY)),
+ static_cast<float>(mDisplay_primary_white[2].value_or(kSrgbWhiteZ))}};
+
+ return primaries;
+}
+
+} // namespace sysprop
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
new file mode 100644
index 0000000..6f90117
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -0,0 +1,65 @@
+
+#ifndef SURFACEFLINGERPROPERTIES_H_
+#define SURFACEFLINGERPROPERTIES_H_
+
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+#include <ui/ConfigStoreTypes.h>
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+namespace android {
+namespace sysprop {
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue);
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue);
+
+bool use_context_priority(bool defaultValue);
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
+
+bool has_wide_color_display(bool defaultValue);
+
+bool running_without_sync_framework(bool defaultValue);
+
+bool has_HDR_display(bool defaultValue);
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue);
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue);
+
+int64_t max_virtual_display_dimension(int64_t defaultValue);
+
+bool use_vr_flinger(bool defaultValue);
+
+bool start_graphics_allocator_service(bool defaultValue);
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+ SurfaceFlingerProperties::primary_display_orientation_values defaultValue);
+
+bool use_color_management(bool defaultValue);
+
+int64_t default_composition_dataspace(
+ android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t default_composition_pixel_format(
+ android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+
+int64_t wcg_composition_dataspace(
+ android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t wcg_composition_pixel_format(
+ android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+
+int32_t set_idle_timer_ms(int32_t defaultValue);
+
+bool use_smart_90_for_video(bool defaultValue);
+
+android::ui::DisplayPrimaries getDisplayNativePrimaries();
+} // namespace sysprop
+} // namespace android
+#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 4596a21..7bfe033 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -30,6 +30,7 @@
namespace android {
// ----------------------------------------------------------------------------
+// TODO(marissaw): add new layer state values to SurfaceInterceptor
SurfaceInterceptor::~SurfaceInterceptor() = default;
@@ -95,22 +96,25 @@
const sp<const Layer>& layer)
{
Transaction* transaction(increment->mutable_transaction());
- transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous);
- transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation);
+ const uint32_t layerFlags = layer->getTransactionFlags();
+ transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous);
+ transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
const int32_t layerId(getLayerId(layer));
- addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
- layer->mCurrentState.active.transform.ty());
+ addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(),
+ layer->mCurrentState.active_legacy.transform.ty());
addDepthLocked(transaction, layerId, layer->mCurrentState.z);
addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
- addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
+ addTransparentRegionLocked(transaction, layerId,
+ layer->mCurrentState.activeTransparentRegion_legacy);
addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
- addCropLocked(transaction, layerId, layer->mCurrentState.crop);
- if (layer->mCurrentState.barrierLayer != nullptr) {
- addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer.promote(),
- layer->mCurrentState.frameNumber);
+ addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+ addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
+ if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
+ addDeferTransactionLocked(transaction, layerId,
+ layer->mCurrentState.barrierLayer_legacy.promote(),
+ layer->mCurrentState.frameNumber_legacy);
}
- addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop);
addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
}
@@ -122,10 +126,10 @@
transaction->set_synchronous(false);
transaction->set_animation(false);
- addDisplaySurfaceLocked(transaction, display.displayId, display.surface);
- addDisplayLayerStackLocked(transaction, display.displayId, display.layerStack);
- addDisplaySizeLocked(transaction, display.displayId, display.width, display.height);
- addDisplayProjectionLocked(transaction, display.displayId, display.orientation,
+ addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
+ addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
+ addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
+ addDisplayProjectionLocked(transaction, display.sequenceId, display.orientation,
display.viewport, display.frame);
}
@@ -177,10 +181,10 @@
}
DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
- int32_t displayId)
+ int32_t sequenceId)
{
DisplayChange* dispChange(transaction->add_display_change());
- dispChange->set_id(displayId);
+ dispChange->set_id(sequenceId);
return dispChange;
}
@@ -286,13 +290,12 @@
setProtoRectLocked(protoRect, rect);
}
-void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId,
- const Rect& rect)
+void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
+ float cornerRadius)
{
SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- FinalCropChange* finalCropChange(change->mutable_final_crop());
- Rectangle* protoRect(finalCropChange->mutable_rectangle());
- setProtoRectLocked(protoRect, rect);
+ CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
+ cornerRadiusChange->set_corner_radius(cornerRadius);
}
void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
@@ -353,25 +356,26 @@
if (state.what & layer_state_t::eLayerStackChanged) {
addLayerStackLocked(transaction, layerId, state.layerStack);
}
- if (state.what & layer_state_t::eCropChanged) {
- addCropLocked(transaction, layerId, state.crop);
+ if (state.what & layer_state_t::eCropChanged_legacy) {
+ addCropLocked(transaction, layerId, state.crop_legacy);
}
- if (state.what & layer_state_t::eDeferTransaction) {
+ if (state.what & layer_state_t::eCornerRadiusChanged) {
+ addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
+ }
+ if (state.what & layer_state_t::eDeferTransaction_legacy) {
sp<Layer> otherLayer = nullptr;
- if (state.barrierHandle != nullptr) {
- otherLayer = static_cast<Layer::Handle*>(state.barrierHandle.get())->owner.promote();
- } else if (state.barrierGbp != nullptr) {
- auto const& gbp = state.barrierGbp;
+ if (state.barrierHandle_legacy != nullptr) {
+ otherLayer =
+ static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote();
+ } else if (state.barrierGbp_legacy != nullptr) {
+ auto const& gbp = state.barrierGbp_legacy;
if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
} else {
ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
}
}
- addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber);
- }
- if (state.what & layer_state_t::eFinalCropChanged) {
- addFinalCropLocked(transaction, layerId, state.finalCrop);
+ addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy);
}
if (state.what & layer_state_t::eOverrideScalingModeChanged) {
addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
@@ -379,19 +383,19 @@
}
void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
- const DisplayState& state, int32_t displayId)
+ const DisplayState& state, int32_t sequenceId)
{
if (state.what & DisplayState::eSurfaceChanged) {
- addDisplaySurfaceLocked(transaction, displayId, state.surface);
+ addDisplaySurfaceLocked(transaction, sequenceId, state.surface);
}
if (state.what & DisplayState::eLayerStackChanged) {
- addDisplayLayerStackLocked(transaction, displayId, state.layerStack);
+ addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
}
if (state.what & DisplayState::eDisplaySizeChanged) {
- addDisplaySizeLocked(transaction, displayId, state.width, state.height);
+ addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
}
if (state.what & DisplayState::eDisplayProjectionChanged) {
- addDisplayProjectionLocked(transaction, displayId, state.orientation, state.viewport,
+ addDisplayProjectionLocked(transaction, sequenceId, state.orientation, state.viewport,
state.frame);
}
}
@@ -411,7 +415,7 @@
ssize_t dpyIdx = displays.indexOfKey(disp.token);
if (dpyIdx >= 0) {
const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
- addDisplayChangesLocked(transaction, disp, dispState.displayId);
+ addDisplayChangesLocked(transaction, disp, dispState.sequenceId);
}
}
}
@@ -422,8 +426,8 @@
SurfaceCreation* creation(increment->mutable_surface_creation());
creation->set_id(getLayerId(layer));
creation->set_name(getLayerName(layer));
- creation->set_w(layer->mCurrentState.active.w);
- creation->set_h(layer->mCurrentState.active.h);
+ creation->set_w(layer->mCurrentState.active_legacy.w);
+ creation->set_h(layer->mCurrentState.active_legacy.h);
}
void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
@@ -448,7 +452,7 @@
event->set_when(timestamp);
}
-void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
const sp<const IGraphicBufferProducer>& surface)
{
if (surface == nullptr) {
@@ -457,7 +461,7 @@
uint64_t bufferQueueId = 0;
status_t err(surface->getUniqueId(&bufferQueueId));
if (err == NO_ERROR) {
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
surfaceChange->set_buffer_queue_id(bufferQueueId);
surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
@@ -469,26 +473,26 @@
}
void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
- int32_t displayId, uint32_t layerStack)
+ int32_t sequenceId, uint32_t layerStack)
{
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
layerStackChange->set_layer_stack(layerStack);
}
-void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t displayId,
+void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
uint32_t w, uint32_t h)
{
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
SizeChange* sizeChange(dispChange->mutable_size());
sizeChange->set_w(w);
sizeChange->set_h(h);
}
void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
- int32_t displayId, int32_t orientation, const Rect& viewport, const Rect& frame)
+ int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame)
{
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+ DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
ProjectionChange* projectionChange(dispChange->mutable_projection());
projectionChange->set_orientation(orientation);
Rectangle* viewportRect(projectionChange->mutable_viewport());
@@ -501,22 +505,24 @@
const DisplayDeviceState& info)
{
DisplayCreation* creation(increment->mutable_display_creation());
- creation->set_id(info.displayId);
+ creation->set_id(info.sequenceId);
creation->set_name(info.displayName);
- creation->set_type(info.type);
creation->set_is_secure(info.isSecure);
+ if (info.displayId) {
+ creation->set_display_id(info.displayId->value);
+ }
}
-void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t displayId) {
+void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) {
DisplayDeletion* deletion(increment->mutable_display_deletion());
- deletion->set_id(displayId);
+ deletion->set_id(sequenceId);
}
-void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t displayId,
+void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId,
int32_t mode)
{
PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
- powerModeUpdate->set_id(displayId);
+ powerModeUpdate->set_id(sequenceId);
powerModeUpdate->set_mode(mode);
}
@@ -579,22 +585,22 @@
addDisplayCreationLocked(createTraceIncrementLocked(), info);
}
-void SurfaceInterceptor::saveDisplayDeletion(int32_t displayId) {
+void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) {
if (!mEnabled) {
return;
}
ATRACE_CALL();
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addDisplayDeletionLocked(createTraceIncrementLocked(), displayId);
+ addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId);
}
-void SurfaceInterceptor::savePowerModeUpdate(int32_t displayId, int32_t mode) {
+void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) {
if (!mEnabled) {
return;
}
ATRACE_CALL();
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode);
+ addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode);
}
} // namespace impl
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 96defcc..563a44c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -66,8 +66,8 @@
// Intercept display data
virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
- virtual void saveDisplayDeletion(int32_t displayId) = 0;
- virtual void savePowerModeUpdate(int32_t displayId, int32_t mode) = 0;
+ virtual void saveDisplayDeletion(int32_t sequenceId) = 0;
+ virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0;
virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
};
@@ -101,8 +101,8 @@
// Intercept display data
void saveDisplayCreation(const DisplayDeviceState& info) override;
- void saveDisplayDeletion(int32_t displayId) override;
- void savePowerModeUpdate(int32_t displayId, int32_t mode) override;
+ void saveDisplayDeletion(int32_t sequenceId) override;
+ void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override;
void saveVSyncEvent(nsecs_t timestamp) override;
private:
@@ -127,8 +127,8 @@
uint32_t height, uint64_t frameNumber);
void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
- void addDisplayDeletionLocked(Increment* increment, int32_t displayId);
- void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode);
+ void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId);
+ void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode);
// Add surface transactions to the trace
SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
@@ -144,9 +144,9 @@
void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber);
- void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
int32_t overrideScalingMode);
void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
@@ -155,17 +155,17 @@
const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
// Add display transactions to the trace
- DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId);
- void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+ DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
+ void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
const sp<const IGraphicBufferProducer>& surface);
- void addDisplayLayerStackLocked(Transaction* transaction, int32_t displayId,
+ void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
uint32_t layerStack);
- void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w,
+ void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
uint32_t h);
- void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId,
+ void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
int32_t orientation, const Rect& viewport, const Rect& frame);
void addDisplayChangesLocked(Transaction* transaction,
- const DisplayState& state, int32_t displayId);
+ const DisplayState& state, int32_t sequenceId);
bool mEnabled {false};
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 67dcd06..db78f1d 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -18,90 +18,173 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceTracing.h"
+#include <SurfaceFlinger.h>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include <utils/SystemClock.h>
#include <utils/Trace.h>
namespace android {
+void SurfaceTracing::mainLoop() {
+ bool enabled = true;
+ // Upon activation, logs the first frame
+ traceLayers("tracing.enable");
+ do {
+ std::unique_lock<std::mutex> sfLock(mFlinger.mDrawingStateLock);
+ mConditionalVariable.wait(sfLock);
+ LayersTraceProto entry = traceLayersLocked(mWhere);
+ sfLock.unlock();
+ {
+ std::scoped_lock bufferLock(mTraceLock);
+ mBuffer.emplace(std::move(entry));
+ if (mWriteToFile) {
+ writeProtoFileLocked();
+ mWriteToFile = false;
+ }
+
+ enabled = mEnabled;
+ }
+ } while (enabled);
+}
+
+void SurfaceTracing::traceLayers(const char* where) {
+ std::unique_lock<std::mutex> sfLock(mFlinger.mDrawingStateLock);
+ LayersTraceProto entry = traceLayersLocked(where);
+ sfLock.unlock();
+ std::scoped_lock bufferLock(mTraceLock);
+ mBuffer.emplace(std::move(entry));
+}
+
+void SurfaceTracing::notify(const char* where) {
+ std::lock_guard<std::mutex> sfLock(mFlinger.mDrawingStateLock);
+ mWhere = where;
+ mConditionalVariable.notify_one();
+}
+
+void SurfaceTracing::writeToFileAsync() {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
+ mWriteToFile = true;
+ mConditionalVariable.notify_one();
+}
+
+void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
+ // use the swap trick to make sure memory is released
+ std::queue<LayersTraceProto>().swap(mStorage);
+ mSizeInBytes = newSize;
+ mUsedInBytes = 0U;
+}
+
+void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
+ auto protoSize = proto.ByteSize();
+ while (mUsedInBytes + protoSize > mSizeInBytes) {
+ if (mStorage.empty()) {
+ return;
+ }
+ mUsedInBytes -= mStorage.front().ByteSize();
+ mStorage.pop();
+ }
+ mUsedInBytes += protoSize;
+ mStorage.emplace();
+ mStorage.back().Swap(&proto);
+}
+
+void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
+ fileProto->mutable_entry()->Reserve(mStorage.size());
+
+ while (!mStorage.empty()) {
+ auto entry = fileProto->add_entry();
+ entry->Swap(&mStorage.front());
+ mStorage.pop();
+ }
+}
+
void SurfaceTracing::enable() {
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
if (mEnabled) {
return;
}
- mEnabled = true;
- mTrace = std::make_unique<LayersTraceFileProto>();
- mTrace->set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
- LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+ mBuffer.reset(mBufferSize);
+ mEnabled = true;
+ mThread = std::thread(&SurfaceTracing::mainLoop, this);
}
-status_t SurfaceTracing::disable() {
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+status_t SurfaceTracing::writeToFile() {
+ mThread.join();
+ return mLastErr;
+}
+
+bool SurfaceTracing::disable() {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
if (!mEnabled) {
- return NO_ERROR;
+ return false;
}
+
mEnabled = false;
- status_t err(writeProtoFileLocked());
- ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
- ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
- mTrace.reset();
- return err;
+ mWriteToFile = true;
+ mConditionalVariable.notify_all();
+ return true;
}
bool SurfaceTracing::isEnabled() const {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
return mEnabled;
}
-void SurfaceTracing::traceLayers(const char* where, LayersProto layers) {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- if (!mEnabled) {
- return;
- }
- LayersTraceProto* entry = mTrace->add_entry();
- entry->set_elapsed_realtime_nanos(elapsedRealtimeNano());
- entry->set_where(where);
- entry->mutable_layers()->Swap(&layers);
-
- constexpr int maxBufferedEntryCount = 3600;
- if (mTrace->entry_size() >= maxBufferedEntryCount) {
- // TODO: flush buffered entries without disabling tracing
- ALOGE("too many buffered frames; force disable tracing");
- mEnabled = false;
- writeProtoFileLocked();
- mTrace.reset();
- }
+void SurfaceTracing::setBufferSize(size_t bufferSizeInByte) {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
+ mBufferSize = bufferSizeInByte;
+ mBuffer.setSize(bufferSizeInByte);
}
-status_t SurfaceTracing::writeProtoFileLocked() {
+LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
ATRACE_CALL();
- if (!mTrace->IsInitialized()) {
- return NOT_ENOUGH_DATA;
- }
- std::string output;
- if (!mTrace->SerializeToString(&output)) {
- return PERMISSION_DENIED;
- }
- if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
- return PERMISSION_DENIED;
- }
+ LayersTraceProto entry;
+ entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+ entry.set_where(where);
+ LayersProto layers(mFlinger.dumpProtoInfo(LayerVector::StateSet::Drawing));
+ entry.mutable_layers()->Swap(&layers);
- return NO_ERROR;
+ return entry;
}
-void SurfaceTracing::dump(String8& result) const {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+void SurfaceTracing::writeProtoFileLocked() {
+ ATRACE_CALL();
- result.appendFormat("Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
- result.appendFormat(" number of entries: %d\n", mTrace ? mTrace->entry_size() : 0);
+ LayersTraceFileProto fileProto;
+ std::string output;
+
+ fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+ LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+ mBuffer.flush(&fileProto);
+ mBuffer.reset(mBufferSize);
+
+ if (!fileProto.SerializeToString(&output)) {
+ ALOGE("Could not save the proto file! Permission denied");
+ mLastErr = PERMISSION_DENIED;
+ }
+ if (!android::base::WriteStringToFile(output, kDefaultFileName, S_IRWXU | S_IRGRP, getuid(),
+ getgid(), true)) {
+ ALOGE("Could not save the proto file! There are missing fields");
+ mLastErr = PERMISSION_DENIED;
+ }
+
+ mLastErr = NO_ERROR;
+}
+
+void SurfaceTracing::dump(std::string& result) const {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
+
+ base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+ base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n",
+ mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
+ float(mBuffer.size()) / float(1_MB));
}
} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index fd8cb82..9484480 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -18,36 +18,78 @@
#include <layerproto/LayerProtoHeader.h>
#include <utils/Errors.h>
-#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
#include <memory>
#include <mutex>
+#include <queue>
+#include <thread>
using namespace android::surfaceflinger;
namespace android {
+class SurfaceFlinger;
+
+constexpr auto operator""_MB(unsigned long long const num) {
+ return num * 1024 * 1024;
+}
/*
* SurfaceTracing records layer states during surface flinging.
*/
class SurfaceTracing {
public:
+ SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
void enable();
- status_t disable();
+ bool disable();
+ status_t writeToFile();
bool isEnabled() const;
+ void notify(const char* where);
- void traceLayers(const char* where, LayersProto);
- void dump(String8& result) const;
+ void setBufferSize(size_t bufferSizeInByte);
+ void writeToFileAsync();
+ void dump(std::string& result) const;
private:
- static constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/layers_trace.pb";
+ static constexpr auto kDefaultBufferCapInByte = 100_MB;
+ static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
- status_t writeProtoFileLocked();
+ class LayersTraceBuffer { // ring buffer
+ public:
+ size_t size() const { return mSizeInBytes; }
+ size_t used() const { return mUsedInBytes; }
+ size_t frameCount() const { return mStorage.size(); }
- bool mEnabled = false;
- std::string mOutputFileName = DEFAULT_FILENAME;
- mutable std::mutex mTraceMutex;
- std::unique_ptr<LayersTraceFileProto> mTrace;
+ void setSize(size_t newSize) { mSizeInBytes = newSize; }
+ void reset(size_t newSize);
+ void emplace(LayersTraceProto&& proto);
+ void flush(LayersTraceFileProto* fileProto);
+
+ private:
+ size_t mUsedInBytes = 0U;
+ size_t mSizeInBytes = 0U;
+ std::queue<LayersTraceProto> mStorage;
+ };
+
+ void mainLoop();
+ void traceLayers(const char* where);
+ LayersTraceProto traceLayersLocked(const char* where);
+ void writeProtoFileLocked() REQUIRES(mTraceLock);
+
+ const SurfaceFlinger& mFlinger;
+
+ const char* mWhere = "";
+ status_t mLastErr = NO_ERROR;
+ std::thread mThread;
+ std::condition_variable mConditionalVariable;
+ mutable std::mutex mTraceLock;
+
+ LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock);
+ size_t mBufferSize GUARDED_BY(mTraceLock) = kDefaultBufferCapInByte;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ bool mWriteToFile GUARDED_BY(mTraceLock) = false;
};
} // namespace android
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
new file mode 100644
index 0000000..cab33ae
--- /dev/null
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "libsurfaceflinger_unittest"
+ },
+ {
+ "name": "libcompositionengine_test"
+ }
+ ]
+}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index d4f1e29..78c6e74 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -24,6 +24,7 @@
#include <log/log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -31,27 +32,14 @@
namespace android {
-TimeStats& TimeStats::getInstance() {
- static std::unique_ptr<TimeStats> sInstance;
- static std::once_flag sOnceFlag;
+namespace impl {
- std::call_once(sOnceFlag, [] { sInstance.reset(new TimeStats); });
- return *sInstance.get();
-}
-
-void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
- String8& result) {
+void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
ATRACE_CALL();
- if (args.size() > index + 10) {
- ALOGD("Invalid args count");
- return;
- }
-
std::unordered_map<std::string, int32_t> argsMap;
- while (index < args.size()) {
+ for (size_t index = 0; index < args.size(); ++index) {
argsMap[std::string(String8(args[index]).c_str())] = index;
- ++index;
}
if (argsMap.count("-disable")) {
@@ -85,7 +73,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.totalFrames++;
+ mTimeStats.totalFrames++;
}
void TimeStats::incrementMissedFrames() {
@@ -94,7 +82,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.missedFrames++;
+ mTimeStats.missedFrames++;
}
void TimeStats::incrementClientCompositionFrames() {
@@ -103,13 +91,13 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.clientCompositionFrames++;
+ mTimeStats.clientCompositionFrames++;
}
-bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) {
+bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
if (!timeRecord->ready) {
- ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(),
- timeRecord->frameNumber);
+ ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
+ timeRecord->frameTime.frameNumber);
return false;
}
@@ -118,11 +106,11 @@
return false;
}
if (timeRecord->acquireFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
- timeRecord->acquireTime = timeRecord->acquireFence->getSignalTime();
+ timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
timeRecord->acquireFence = nullptr;
} else {
- ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(),
- timeRecord->frameNumber);
+ ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
+ timeRecord->frameTime.frameNumber);
}
}
@@ -131,11 +119,11 @@
return false;
}
if (timeRecord->presentFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
- timeRecord->presentTime = timeRecord->presentFence->getSignalTime();
+ timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
timeRecord->presentFence = nullptr;
} else {
- ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(),
- timeRecord->frameNumber);
+ ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
+ timeRecord->frameTime.frameNumber);
}
}
@@ -148,14 +136,15 @@
return static_cast<int32_t>(delta);
}
+// This regular expression captures the following for instance:
+// StatusBar in StatusBar#0
+// com.appname in com.appname/com.appname.activity#0
+// com.appname in SurfaceView - com.appname/com.appname.activity#0
+static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
+
static std::string getPackageName(const std::string& layerName) {
- // This regular expression captures the following for instance:
- // StatusBar in StatusBar#0
- // com.appname in com.appname/com.appname.activity#0
- // com.appname in SurfaceView - com.appname/com.appname.activity#0
- const std::regex re("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
std::smatch match;
- if (std::regex_match(layerName.begin(), layerName.end(), match, re)) {
+ if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) {
// There must be a match for group 1 otherwise the whole string is not
// matched and the above will return false
return match[1];
@@ -163,103 +152,129 @@
return "";
}
-void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
ATRACE_CALL();
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
while (!timeRecords.empty()) {
- if (!recordReadyLocked(layerName, &timeRecords[0])) break;
- ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(),
- timeRecords[0].frameNumber, timeRecords[0].presentTime);
+ if (!recordReadyLocked(layerID, &timeRecords[0])) break;
+ ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
+ timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
+ const std::string& layerName = layerRecord.layerName;
if (prevTimeRecord.ready) {
- if (!timeStats.stats.count(layerName)) {
- timeStats.stats[layerName].layerName = layerName;
- timeStats.stats[layerName].packageName = getPackageName(layerName);
- timeStats.stats[layerName].statsStart = static_cast<int64_t>(std::time(0));
+ if (!mTimeStats.stats.count(layerName)) {
+ mTimeStats.stats[layerName].layerName = layerName;
+ mTimeStats.stats[layerName].packageName = getPackageName(layerName);
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName];
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
timeStatsLayer.totalFrames++;
+ timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
+ layerRecord.droppedFrames = 0;
- const int32_t postToPresentMs =
- msBetween(timeRecords[0].postTime, timeRecords[0].presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(),
- timeRecords[0].frameNumber, postToPresentMs);
+ const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
+ timeRecords[0].frameTime.acquireTime);
+ ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
+ timeRecords[0].frameTime.frameNumber, postToAcquireMs);
+ timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
+
+ const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
+ timeRecords[0].frameTime.presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
+ timeRecords[0].frameTime.frameNumber, postToPresentMs);
timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
- const int32_t acquireToPresentMs =
- msBetween(timeRecords[0].acquireTime, timeRecords[0].presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(),
- timeRecords[0].frameNumber, acquireToPresentMs);
+ const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
+ timeRecords[0].frameTime.presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
+ timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
- const int32_t latchToPresentMs =
- msBetween(timeRecords[0].latchTime, timeRecords[0].presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(),
- timeRecords[0].frameNumber, latchToPresentMs);
+ const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
+ timeRecords[0].frameTime.presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
+ timeRecords[0].frameTime.frameNumber, latchToPresentMs);
timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
- const int32_t desiredToPresentMs =
- msBetween(timeRecords[0].desiredTime, timeRecords[0].presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(),
- timeRecords[0].frameNumber, desiredToPresentMs);
+ const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
+ timeRecords[0].frameTime.presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
+ timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
- const int32_t presentToPresentMs =
- msBetween(prevTimeRecord.presentTime, timeRecords[0].presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(),
- timeRecords[0].frameNumber, presentToPresentMs);
+ const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
+ timeRecords[0].frameTime.presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
+ timeRecords[0].frameTime.frameNumber, presentToPresentMs);
timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
-
- timeStats.stats[layerName].statsEnd = static_cast<int64_t>(std::time(0));
}
+
+ // Output additional trace points to track frame time.
+ ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime);
+ ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(),
+ timeRecords[0].frameTime.acquireTime);
+ ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(),
+ timeRecords[0].frameTime.latchTime);
+ ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(),
+ timeRecords[0].frameTime.desiredTime);
+ ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(),
+ timeRecords[0].frameTime.presentTime);
+
prevTimeRecord = timeRecords[0];
timeRecords.pop_front();
layerRecord.waitData--;
}
}
+// This regular expression captures the following layer names for instance:
+// 1) StatusBat#0
+// 2) NavigationBar#1
+// 3) co(m).*#0
+// 4) SurfaceView - co(m).*#0
+// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
+// is a bit more robust in case there's a slight change.
+// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
+static const std::regex layerNameRegex(
+ "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
+
static bool layerNameIsValid(const std::string& layerName) {
- // This regular expression captures the following layer names for instance:
- // 1) StatusBat#0
- // 2) NavigationBar#1
- // 3) com.*#0
- // 4) SurfaceView - com.*#0
- // Using [-\\s\t]+ for the conjunction part between SurfaceView and com.* is
- // a bit more robust in case there's a slight change.
- // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
- std::regex re("(((SurfaceView[-\\s\\t]+)?com\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
- return std::regex_match(layerName.begin(), layerName.end(), re);
+ return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex);
}
-void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) {
+void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+ nsecs_t postTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime);
+ ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(),
+ postTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) {
- return;
+ if (!mTimeStatsTracker.count(layerID) && layerNameIsValid(layerName)) {
+ mTimeStatsTracker[layerID].layerName = layerName;
}
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
- ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(),
- MAX_NUM_TIME_RECORDS);
- // TODO(zzyiwei): if this happens, there must be a present fence missing
- // or waitData is not in the correct position. Need to think out a
- // reasonable way to recover from this state.
+ ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
+ layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+ mTimeStatsTracker.erase(layerID);
return;
}
// For most media content, the acquireFence is invalid because the buffer is
// ready at the queueBuffer stage. In this case, acquireTime should be given
// a default value as postTime.
TimeRecord timeRecord = {
- .frameNumber = frameNumber,
- .postTime = postTime,
- .acquireTime = postTime,
+ .frameTime =
+ {
+ .frameNumber = frameNumber,
+ .postTime = postTime,
+ .latchTime = postTime,
+ .acquireTime = postTime,
+ .desiredTime = postTime,
+ },
};
layerRecord.timeRecords.push_back(timeRecord);
if (layerRecord.waitData < 0 ||
@@ -267,160 +282,243 @@
layerRecord.waitData = layerRecord.timeRecords.size() - 1;
}
-void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime);
+ ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
- if (timeRecord.frameNumber == frameNumber) {
- timeRecord.latchTime = latchTime;
+ if (timeRecord.frameTime.frameNumber == frameNumber) {
+ timeRecord.frameTime.latchTime = latchTime;
}
}
-void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t desiredTime) {
+void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber,
- desiredTime);
+ ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
- if (timeRecord.frameNumber == frameNumber) {
- timeRecord.desiredTime = desiredTime;
+ if (timeRecord.frameTime.frameNumber == frameNumber) {
+ timeRecord.frameTime.desiredTime = desiredTime;
}
}
-void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber,
- acquireTime);
+ ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
- if (timeRecord.frameNumber == frameNumber) {
- timeRecord.acquireTime = acquireTime;
+ if (timeRecord.frameTime.frameNumber == frameNumber) {
+ timeRecord.frameTime.acquireTime = acquireTime;
}
}
-void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
acquireFence->getSignalTime());
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
- if (timeRecord.frameNumber == frameNumber) {
+ if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.acquireFence = acquireFence;
}
}
-void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber,
- presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
- if (timeRecord.frameNumber == frameNumber) {
- timeRecord.presentTime = presentTime;
+ if (timeRecord.frameTime.frameNumber == frameNumber) {
+ timeRecord.frameTime.presentTime = presentTime;
timeRecord.ready = true;
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerName);
+ flushAvailableRecordsToStatsLocked(layerID);
}
-void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
presentFence->getSignalTime());
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
- if (timeRecord.frameNumber == frameNumber) {
+ if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.presentFence = presentFence;
timeRecord.ready = true;
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerName);
+ flushAvailableRecordsToStatsLocked(layerID);
}
-void TimeStats::onDisconnect(const std::string& layerName) {
+void TimeStats::onDestroy(int32_t layerID) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-onDisconnect", layerName.c_str());
+ ALOGV("[%d]-onDestroy", layerID);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- flushAvailableRecordsToStatsLocked(layerName);
- timeStatsTracker.erase(layerName);
+ if (!mTimeStatsTracker.count(layerID)) return;
+ mTimeStatsTracker.erase(layerID);
}
-void TimeStats::clearLayerRecord(const std::string& layerName) {
+void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-clearLayerRecord", layerName.c_str());
+ ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
- layerRecord.timeRecords.clear();
- layerRecord.prevTimeRecord.ready = false;
- layerRecord.waitData = -1;
-}
-
-void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber);
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
size_t removeAt = 0;
for (const TimeRecord& record : layerRecord.timeRecords) {
- if (record.frameNumber == frameNumber) break;
+ if (record.frameTime.frameNumber == frameNumber) break;
removeAt++;
}
if (removeAt == layerRecord.timeRecords.size()) return;
layerRecord.timeRecords.erase(layerRecord.timeRecords.begin() + removeAt);
if (layerRecord.waitData > static_cast<int32_t>(removeAt)) {
- --layerRecord.waitData;
+ layerRecord.waitData--;
}
+ layerRecord.droppedFrames++;
+}
+
+void TimeStats::flushPowerTimeLocked() {
+ if (!mEnabled.load()) return;
+
+ nsecs_t curTime = systemTime();
+ // elapsedTime is in milliseconds.
+ int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000;
+
+ switch (mPowerTime.powerMode) {
+ case HWC_POWER_MODE_NORMAL:
+ mTimeStats.displayOnTime += elapsedTime;
+ break;
+ case HWC_POWER_MODE_OFF:
+ case HWC_POWER_MODE_DOZE:
+ case HWC_POWER_MODE_DOZE_SUSPEND:
+ default:
+ break;
+ }
+
+ mPowerTime.prevTime = curTime;
+}
+
+void TimeStats::setPowerMode(int32_t powerMode) {
+ if (!mEnabled.load()) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPowerTime.powerMode = powerMode;
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (powerMode == mPowerTime.powerMode) return;
+
+ flushPowerTimeLocked();
+ mPowerTime.powerMode = powerMode;
+}
+
+void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mTimeStats.refreshRateStats.count(fps)) {
+ mTimeStats.refreshRateStats[fps] += duration;
+ } else {
+ mTimeStats.refreshRateStats.insert({fps, duration});
+ }
+}
+
+void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
+ ATRACE_CALL();
+
+ while (!mGlobalRecord.presentFences.empty()) {
+ const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime();
+ if (curPresentTime == Fence::SIGNAL_TIME_PENDING) break;
+
+ if (curPresentTime == Fence::SIGNAL_TIME_INVALID) {
+ ALOGE("GlobalPresentFence is invalid!");
+ mGlobalRecord.prevPresentTime = 0;
+ mGlobalRecord.presentFences.pop_front();
+ continue;
+ }
+
+ ALOGV("GlobalPresentFenceTime[%" PRId64 "]",
+ mGlobalRecord.presentFences.front()->getSignalTime());
+
+ if (mGlobalRecord.prevPresentTime != 0) {
+ const int32_t presentToPresentMs =
+ msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
+ ALOGV("Global present2present[%d] prev[%" PRId64 "] curr[%" PRId64 "]",
+ presentToPresentMs, mGlobalRecord.prevPresentTime, curPresentTime);
+ mTimeStats.presentToPresent.insert(presentToPresentMs);
+ }
+
+ mGlobalRecord.prevPresentTime = curPresentTime;
+ mGlobalRecord.presentFences.pop_front();
+ }
+}
+
+void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (presentFence == nullptr || !presentFence->isValid()) {
+ mGlobalRecord.prevPresentTime = 0;
+ return;
+ }
+
+ if (mPowerTime.powerMode != HWC_POWER_MODE_NORMAL) {
+ // Try flushing the last present fence on HWC_POWER_MODE_NORMAL.
+ flushAvailableGlobalRecordsToStatsLocked();
+ mGlobalRecord.presentFences.clear();
+ mGlobalRecord.prevPresentTime = 0;
+ return;
+ }
+
+ if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) {
+ // The front presentFence must be trapped in pending status in this
+ // case. Try dequeuing the front one to recover.
+ ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+ mGlobalRecord.prevPresentTime = 0;
+ mGlobalRecord.presentFences.pop_front();
+ }
+
+ mGlobalRecord.presentFences.emplace_back(presentFence);
+ flushAvailableGlobalRecordsToStatsLocked();
}
void TimeStats::enable() {
@@ -429,9 +527,10 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- ALOGD("Enabled");
mEnabled.store(true);
- timeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mPowerTime.prevTime = systemTime();
+ ALOGD("Enabled");
}
void TimeStats::disable() {
@@ -440,47 +539,59 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- ALOGD("Disabled");
+ flushPowerTimeLocked();
mEnabled.store(false);
- timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ ALOGD("Disabled");
}
void TimeStats::clear() {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
+ mTimeStatsTracker.clear();
+ mTimeStats.stats.clear();
+ mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+ mTimeStats.statsEnd = 0;
+ mTimeStats.totalFrames = 0;
+ mTimeStats.missedFrames = 0;
+ mTimeStats.clientCompositionFrames = 0;
+ mTimeStats.displayOnTime = 0;
+ mTimeStats.presentToPresent.hist.clear();
+ mTimeStats.refreshRateStats.clear();
+ mPowerTime.prevTime = systemTime();
+ mGlobalRecord.prevPresentTime = 0;
+ mGlobalRecord.presentFences.clear();
ALOGD("Cleared");
- timeStats.stats.clear();
- timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
- timeStats.statsEnd = 0;
- timeStats.totalFrames = 0;
- timeStats.missedFrames = 0;
- timeStats.clientCompositionFrames = 0;
}
bool TimeStats::isEnabled() {
return mEnabled.load();
}
-void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result) {
+void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- if (timeStats.statsStart == 0) {
+ if (mTimeStats.statsStart == 0) {
return;
}
- timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+
+ flushPowerTimeLocked();
if (asProto) {
ALOGD("Dumping TimeStats as proto");
- SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto(maxLayers);
+ SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
} else {
ALOGD("Dumping TimeStats as text");
- result.append(timeStats.toString(maxLayers).c_str());
+ result.append(mTimeStats.toString(maxLayers));
result.append("\n");
}
}
+} // namespace impl
+
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8318210..d8c0786 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -19,10 +19,11 @@
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
+#include <hardware/hwcomposer_defs.h>
+
#include <ui/FenceTime.h>
#include <utils/String16.h>
-#include <utils/String8.h>
#include <utils/Vector.h>
#include <deque>
@@ -33,70 +34,133 @@
using namespace android::surfaceflinger;
namespace android {
-class String8;
class TimeStats {
- // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
- // static const size_t MAX_NUM_LAYER_RECORDS = 200;
- static const size_t MAX_NUM_TIME_RECORDS = 64;
+public:
+ virtual ~TimeStats() = default;
- struct TimeRecord {
- bool ready = false;
+ virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
+ virtual bool isEnabled() = 0;
+
+ virtual void incrementTotalFrames() = 0;
+ virtual void incrementMissedFrames() = 0;
+ virtual void incrementClientCompositionFrames() = 0;
+
+ virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+ nsecs_t postTime) = 0;
+ virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
+ virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+ virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+ virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& acquireFence) = 0;
+ virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
+ virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& presentFence) = 0;
+ // Clean up the layer record
+ virtual void onDestroy(int32_t layerID) = 0;
+ // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
+ virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+
+ virtual void setPowerMode(int32_t powerMode) = 0;
+ // Source of truth is RefrehRateStats.
+ virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0;
+ virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0;
+};
+
+namespace impl {
+
+class TimeStats : public android::TimeStats {
+ struct FrameTime {
uint64_t frameNumber = 0;
nsecs_t postTime = 0;
nsecs_t latchTime = 0;
nsecs_t acquireTime = 0;
nsecs_t desiredTime = 0;
nsecs_t presentTime = 0;
+ };
+
+ struct TimeRecord {
+ bool ready = false;
+ FrameTime frameTime;
std::shared_ptr<FenceTime> acquireFence;
std::shared_ptr<FenceTime> presentFence;
};
struct LayerRecord {
+ std::string layerName;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
int32_t waitData = -1;
+ uint32_t droppedFrames = 0;
TimeRecord prevTimeRecord;
std::deque<TimeRecord> timeRecords;
};
+ struct PowerTime {
+ int32_t powerMode = HWC_POWER_MODE_OFF;
+ nsecs_t prevTime = 0;
+ };
+
+ struct GlobalRecord {
+ nsecs_t prevPresentTime = 0;
+ std::deque<std::shared_ptr<FenceTime>> presentFences;
+ };
+
public:
- static TimeStats& getInstance();
- void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
- void incrementTotalFrames();
- void incrementMissedFrames();
- void incrementClientCompositionFrames();
-
- void setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime);
- void setLatchTime(const std::string& layerName, uint64_t frameNumber, nsecs_t latchTime);
- void setDesiredTime(const std::string& layerName, uint64_t frameNumber, nsecs_t desiredTime);
- void setAcquireTime(const std::string& layerName, uint64_t frameNumber, nsecs_t acquireTime);
- void setAcquireFence(const std::string& layerName, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& acquireFence);
- void setPresentTime(const std::string& layerName, uint64_t frameNumber, nsecs_t presentTime);
- void setPresentFence(const std::string& layerName, uint64_t frameNumber,
- const std::shared_ptr<FenceTime>& presentFence);
- void onDisconnect(const std::string& layerName);
- void clearLayerRecord(const std::string& layerName);
- void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
-
-private:
TimeStats() = default;
- bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
- void flushAvailableRecordsToStatsLocked(const std::string& layerName);
+ void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
+ bool isEnabled() override;
+
+ void incrementTotalFrames() override;
+ void incrementMissedFrames() override;
+ void incrementClientCompositionFrames() override;
+
+ void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+ nsecs_t postTime) override;
+ void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
+ void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
+ void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
+ void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& acquireFence) override;
+ void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
+ void setPresentFence(int32_t layerID, uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& presentFence) override;
+ // Clean up the layer record
+ void onDestroy(int32_t layerID) override;
+ // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
+ void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
+
+ void setPowerMode(int32_t powerMode) override;
+ // Source of truth is RefrehRateStats.
+ void recordRefreshRate(uint32_t fps, nsecs_t duration) override;
+ void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override;
+
+ // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
+ // static const size_t MAX_NUM_LAYER_RECORDS = 200;
+ static const size_t MAX_NUM_TIME_RECORDS = 64;
+
+private:
+ bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
+ void flushAvailableRecordsToStatsLocked(int32_t layerID);
+ void flushPowerTimeLocked();
+ void flushAvailableGlobalRecordsToStatsLocked();
void enable();
void disable();
void clear();
- bool isEnabled();
- void dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result);
+ void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
std::atomic<bool> mEnabled = false;
std::mutex mMutex;
- TimeStatsHelper::TimeStatsGlobal timeStats;
- std::unordered_map<std::string, LayerRecord> timeStatsTracker;
+ TimeStatsHelper::TimeStatsGlobal mTimeStats;
+ // Hashmap for LayerRecord with layerID as the hash key
+ std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
+ PowerTime mPowerTime;
+ GlobalRecord mGlobalRecord;
};
+} // namespace impl
+
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 21f3ef3..16d2da0 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "timestatsproto/TimeStatsHelper.h"
+
#include <android-base/stringprintf.h>
-#include <timestatsproto/TimeStatsHelper.h>
+#include <inttypes.h>
#include <array>
@@ -39,17 +41,25 @@
if (delta < 0) return;
// std::lower_bound won't work on out of range values
if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) {
- hist[histogramConfig[HISTOGRAM_SIZE - 1]]++;
+ hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1];
return;
}
auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta);
hist[*iter]++;
}
+int64_t TimeStatsHelper::Histogram::totalTime() const {
+ int64_t ret = 0;
+ for (const auto& ele : hist) {
+ ret += ele.first * ele.second;
+ }
+ return ret;
+}
+
float TimeStatsHelper::Histogram::averageTime() const {
int64_t ret = 0;
int64_t count = 0;
- for (auto& ele : hist) {
+ for (const auto& ele : hist) {
count += ele.second;
ret += ele.first * ele.second;
}
@@ -68,19 +78,18 @@
}
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
- std::string result = "";
+ std::string result = "\n";
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
- StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
- StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
- StringAppendF(&result, "totalFrames= %d\n", totalFrames);
- auto iter = deltas.find("present2present");
+ StringAppendF(&result, "totalFrames = %d\n", totalFrames);
+ StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
+ const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
}
- for (auto& ele : deltas) {
+ for (const auto& ele : deltas) {
StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
- StringAppendF(&result, "%s", ele.second.toString().c_str());
+ result.append(ele.second.toString());
}
return result;
@@ -88,15 +97,23 @@
std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const {
std::string result = "SurfaceFlinger TimeStats:\n";
- StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
- StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
- StringAppendF(&result, "totalFrames= %d\n", totalFrames);
- StringAppendF(&result, "missedFrames= %d\n", missedFrames);
- StringAppendF(&result, "clientCompositionFrames= %d\n", clientCompositionFrames);
- StringAppendF(&result, "TimeStats for each layer is as below:\n");
+ StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart);
+ StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd);
+ StringAppendF(&result, "totalFrames = %d\n", totalFrames);
+ StringAppendF(&result, "missedFrames = %d\n", missedFrames);
+ StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+ StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+ StringAppendF(&result, "displayConfigStats is as below:\n");
+ for (const auto& [fps, duration] : refreshRateStats) {
+ StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+ }
+ result.back() = '\n';
+ StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
+ StringAppendF(&result, "presentToPresent histogram is as below:\n");
+ result.append(presentToPresent.toString());
const auto dumpStats = generateDumpStats(maxLayers);
- for (auto& ele : dumpStats) {
- StringAppendF(&result, "%s", ele->toString().c_str());
+ for (const auto& ele : dumpStats) {
+ result.append(ele->toString());
}
return result;
@@ -106,15 +123,14 @@
SFTimeStatsLayerProto layerProto;
layerProto.set_layer_name(layerName);
layerProto.set_package_name(packageName);
- layerProto.set_stats_start(statsStart);
- layerProto.set_stats_end(statsEnd);
layerProto.set_total_frames(totalFrames);
- for (auto& ele : deltas) {
+ layerProto.set_dropped_frames(droppedFrames);
+ for (const auto& ele : deltas) {
SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas();
deltaProto->set_delta_name(ele.first);
- for (auto& histEle : ele.second.hist) {
+ for (const auto& histEle : ele.second.hist) {
SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms();
- histProto->set_render_millis(histEle.first);
+ histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
}
@@ -129,8 +145,21 @@
globalProto.set_total_frames(totalFrames);
globalProto.set_missed_frames(missedFrames);
globalProto.set_client_composition_frames(clientCompositionFrames);
+ globalProto.set_display_on_time(displayOnTime);
+ for (const auto& ele : refreshRateStats) {
+ SFTimeStatsDisplayConfigBucketProto* configBucketProto =
+ globalProto.add_display_config_stats();
+ SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config();
+ configProto->set_fps(ele.first);
+ configBucketProto->set_duration_millis(ns2ms(ele.second));
+ }
+ for (const auto& histEle : presentToPresent.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
+ histProto->set_time_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
const auto dumpStats = generateDumpStats(maxLayers);
- for (auto& ele : dumpStats) {
+ for (const auto& ele : dumpStats) {
SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
layerProto->CopyFrom(ele->toProto());
}
@@ -140,7 +169,7 @@
std::vector<TimeStatsHelper::TimeStatsLayer const*>
TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
std::vector<TimeStatsLayer const*> dumpStats;
- for (auto& ele : stats) {
+ for (const auto& ele : stats) {
dumpStats.push_back(&ele.second);
}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 1798555..f2ac7ff 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -16,6 +16,7 @@
#pragma once
#include <timestatsproto/TimeStatsProtoHeader.h>
+#include <utils/Timers.h>
#include <optional>
#include <string>
@@ -34,6 +35,7 @@
std::unordered_map<int32_t, int32_t> hist;
void insert(int32_t delta);
+ int64_t totalTime() const;
float averageTime() const;
std::string toString() const;
};
@@ -42,9 +44,8 @@
public:
std::string layerName;
std::string packageName;
- int64_t statsStart = 0;
- int64_t statsEnd = 0;
int32_t totalFrames = 0;
+ int32_t droppedFrames = 0;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
@@ -58,7 +59,10 @@
int32_t totalFrames = 0;
int32_t missedFrames = 0;
int32_t clientCompositionFrames = 0;
+ int64_t displayOnTime = 0;
+ Histogram presentToPresent;
std::unordered_map<std::string, TimeStatsLayer> stats;
+ std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index f29fbd1..0dacbeb 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -20,46 +20,80 @@
option optimize_for = LITE_RUNTIME;
+// //depot/google3/wireless/android/graphics/surfaceflingerstats/proto/
+// timestats.proto is based on this proto. Please only make valid protobuf
+// changes to these messages, and keep google3 side proto messages in sync if
+// the end to end pipeline needs to be updated.
+
+// Next tag: 10
message SFTimeStatsGlobalProto {
- // The start & end timestamps in UTC as
- // milliseconds since January 1, 1970
+ // The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
+ // The stats end time in UTC as seconds since January 1, 1970
optional int64 stats_end = 2;
- // Total frames
+ // Total number of frames presented during tracing period.
optional int32 total_frames = 3;
// Total missed frames of SurfaceFlinger.
optional int32 missed_frames = 4;
// Total frames fallback to client composition.
optional int32 client_composition_frames = 5;
-
+ // Primary display on time in milliseconds.
+ optional int64 display_on_time = 7;
+ // Stats per display configuration.
+ repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
+ // Present to present histogram.
+ repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
+ // Stats per layer. Apps could have multiple layers.
repeated SFTimeStatsLayerProto stats = 6;
}
+// Next tag: 8
message SFTimeStatsLayerProto {
- // The layer name
+ // The name of the visible view layer.
optional string layer_name = 1;
- // The package name
+ // The package name of the application owning this layer.
optional string package_name = 2;
- // The start & end timestamps in UTC as
- // milliseconds since January 1, 1970
+ // The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 3;
+ // The stats end time in UTC as seconds since January 1, 1970
optional int64 stats_end = 4;
- // Distinct frame count.
+ // Total number of frames presented during tracing period.
optional int32 total_frames = 5;
-
+ // Total number of frames dropped by SurfaceFlinger.
+ optional int32 dropped_frames = 7;
+ // There are multiple timestamps tracked in SurfaceFlinger, and these are the
+ // histograms of deltas between different combinations of those timestamps.
repeated SFTimeStatsDeltaProto deltas = 6;
}
+// Next tag: 3
message SFTimeStatsDeltaProto {
// Name of the time interval
optional string delta_name = 1;
- // Histogram of the delta time
+ // Histogram of the delta time. There should be at most 85 buckets ranging
+ // from [0ms, 1ms) to [1000ms, infinity)
repeated SFTimeStatsHistogramBucketProto histograms = 2;
}
+// Next tag: 3
message SFTimeStatsHistogramBucketProto {
- // Lower bound of render time in milliseconds.
- optional int32 render_millis = 1;
+ // Lower bound of time interval in milliseconds.
+ optional int32 time_millis = 1;
// Number of frames in the bucket.
optional int32 frame_count = 2;
}
+
+// Next tag: 3
+message SFTimeStatsDisplayConfigBucketProto {
+ // Metadata desribing a display config.
+ optional SFTimeStatsDisplayConfigProto config = 1;
+ // Duration in milliseconds for how long the display was in this
+ // configuration.
+ optional int64 duration_millis = 2;
+}
+
+// Next tag: 2
+message SFTimeStatsDisplayConfigProto {
+ // Frames per second, rounded to the nearest integer.
+ optional int32 fps = 1;
+}
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
new file mode 100644
index 0000000..d2b7fe0
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionCompletedThread"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TransactionCompletedThread.h"
+
+#include <cinttypes>
+
+#include <binder/IInterface.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+TransactionCompletedThread::~TransactionCompletedThread() {
+ std::lock_guard lockThread(mThreadMutex);
+
+ {
+ std::lock_guard lock(mMutex);
+ mKeepRunning = false;
+ mConditionVariable.notify_all();
+ }
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+
+ {
+ std::lock_guard lock(mMutex);
+ for (const auto& [listener, listenerStats] : mListenerStats) {
+ listener->unlinkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+void TransactionCompletedThread::run() {
+ std::lock_guard lock(mMutex);
+ if (mRunning || !mKeepRunning) {
+ return;
+ }
+ mDeathRecipient = new ThreadDeathRecipient();
+ mRunning = true;
+
+ std::lock_guard lockThread(mThreadMutex);
+ mThread = std::thread(&TransactionCompletedThread::threadMain, this);
+}
+
+void TransactionCompletedThread::registerPendingCallbackHandle(const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+
+ sp<IBinder> listener = IInterface::asBinder(handle->listener);
+ const auto& callbackIds = handle->callbackIds;
+
+ mPendingTransactions[listener][callbackIds]++;
+}
+
+void TransactionCompletedThread::addPresentedCallbackHandles(
+ const std::deque<sp<CallbackHandle>>& handles) {
+ std::lock_guard lock(mMutex);
+
+ for (const auto& handle : handles) {
+ auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
+ auto& pendingCallbacks = listener->second;
+ auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+ if (pendingCallback != pendingCallbacks.end()) {
+ auto& pendingCount = pendingCallback->second;
+
+ // Decrease the pending count for this listener
+ if (--pendingCount == 0) {
+ pendingCallbacks.erase(pendingCallback);
+ }
+ } else {
+ ALOGE("there are more latched callbacks than there were registered callbacks");
+ }
+
+ addCallbackHandle(handle);
+ }
+}
+
+void TransactionCompletedThread::addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+ addCallbackHandle(handle);
+}
+
+void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+ const sp<IBinder> listener = IInterface::asBinder(handle->listener);
+
+ // If we don't already have a reference to this listener, linkToDeath so we get a notification
+ // if it dies.
+ if (mListenerStats.count(listener) == 0) {
+ status_t error = listener->linkToDeath(mDeathRecipient);
+ if (error != NO_ERROR) {
+ ALOGE("cannot add callback handle because linkToDeath failed, err: %d", error);
+ return;
+ }
+ }
+
+ auto& listenerStats = mListenerStats[listener];
+ listenerStats.listener = handle->listener;
+
+ auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
+ transactionStats.latchTime = handle->latchTime;
+ transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+ handle->previousReleaseFence);
+}
+
+void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPresentFence = presentFence;
+}
+
+void TransactionCompletedThread::sendCallbacks() {
+ std::lock_guard lock(mMutex);
+ if (mRunning) {
+ mConditionVariable.notify_all();
+ }
+}
+
+void TransactionCompletedThread::threadMain() {
+ std::lock_guard lock(mMutex);
+
+ while (mKeepRunning) {
+ mConditionVariable.wait(mMutex);
+
+ // For each listener
+ auto it = mListenerStats.begin();
+ while (it != mListenerStats.end()) {
+ auto& [listener, listenerStats] = *it;
+
+ // For each transaction
+ bool sendCallback = true;
+ for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+ // If we are still waiting on the callback handles for this transaction, skip it
+ if (mPendingTransactions[listener].count(callbackIds) != 0) {
+ sendCallback = false;
+ break;
+ }
+
+ // If the transaction has been latched
+ if (transactionStats.latchTime >= 0) {
+ if (!mPresentFence) {
+ sendCallback = false;
+ break;
+ }
+ transactionStats.presentFence = mPresentFence;
+ }
+ }
+ // If the listener has no pending transactions and all latched transactions have been
+ // presented
+ if (sendCallback) {
+ // If the listener is still alive
+ if (listener->isBinderAlive()) {
+ // Send callback
+ listenerStats.listener->onTransactionCompleted(listenerStats);
+ listener->unlinkToDeath(mDeathRecipient);
+ }
+ it = mListenerStats.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ if (mPresentFence) {
+ mPresentFence.clear();
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+ const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
+ : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
new file mode 100644
index 0000000..f49306d
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2018 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 <condition_variable>
+#include <deque>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include <binder/IBinder.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+
+namespace android {
+
+class CallbackHandle : public RefBase {
+public:
+ CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+ const std::vector<CallbackId>& ids, const sp<IBinder>& sc);
+
+ sp<ITransactionCompletedListener> listener;
+ std::vector<CallbackId> callbackIds;
+ sp<IBinder> surfaceControl;
+
+ bool releasePreviousBuffer = false;
+ sp<Fence> previousReleaseFence;
+ nsecs_t acquireTime = -1;
+ nsecs_t latchTime = -1;
+};
+
+class TransactionCompletedThread {
+public:
+ ~TransactionCompletedThread();
+
+ void run();
+
+ // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
+ // that needs to be latched and presented this frame. This function should be called once the
+ // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
+ // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
+ // presented.
+ void registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+ // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
+ void addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+
+ // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
+ // presented this frame.
+ void addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+
+ void addPresentFence(const sp<Fence>& presentFence);
+
+ void sendCallbacks();
+
+private:
+ void threadMain();
+
+ void addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
+
+ class ThreadDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
+ // Death recipients needs a binderDied function.
+ //
+ // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary.
+ // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
+ void binderDied(const wp<IBinder>& /*who*/) override {}
+ };
+ sp<ThreadDeathRecipient> mDeathRecipient;
+
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& strongPointer) const {
+ return std::hash<IBinder*>{}(strongPointer.get());
+ }
+ };
+
+ // Protects the creation and destruction of mThread
+ std::mutex mThreadMutex;
+
+ std::thread mThread GUARDED_BY(mThreadMutex);
+
+ std::mutex mMutex;
+ std::condition_variable_any mConditionVariable;
+
+ std::unordered_map<
+ sp<IBinder /*listener*/>,
+ std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
+ IBinderHash>
+ mPendingTransactions GUARDED_BY(mMutex);
+ std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
+ GUARDED_BY(mMutex);
+
+ bool mRunning GUARDED_BY(mMutex) = false;
+ bool mKeepRunning GUARDED_BY(mMutex) = true;
+
+ sp<Fence> mPresentFence GUARDED_BY(mMutex);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
deleted file mode 100644
index b11d057..0000000
--- a/services/surfaceflinger/Transform.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TRANSFORM_H
-#define ANDROID_TRANSFORM_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Point.h>
-#include <ui/Rect.h>
-#include <math/vec2.h>
-#include <math/vec3.h>
-
-#include <gui/ISurfaceComposer.h>
-
-#include <hardware/hardware.h>
-
-namespace android {
-
-class Region;
-
-// ---------------------------------------------------------------------------
-
-class Transform
-{
-public:
- Transform();
- Transform(const Transform& other);
- explicit Transform(uint32_t orientation);
- ~Transform();
-
- enum orientation_flags {
- ROT_0 = 0x00000000,
- FLIP_H = HAL_TRANSFORM_FLIP_H,
- FLIP_V = HAL_TRANSFORM_FLIP_V,
- ROT_90 = HAL_TRANSFORM_ROT_90,
- ROT_180 = FLIP_H|FLIP_V,
- ROT_270 = ROT_180|ROT_90,
- ROT_INVALID = 0x80
- };
-
- static orientation_flags fromRotation(ISurfaceComposer::Rotation rotation);
-
- enum type_mask {
- IDENTITY = 0,
- TRANSLATE = 0x1,
- ROTATE = 0x2,
- SCALE = 0x4,
- UNKNOWN = 0x8
- };
-
- // query the transform
- bool preserveRects() const;
- uint32_t getType() const;
- uint32_t getOrientation() const;
-
- const vec3& operator [] (size_t i) const; // returns column i
- float tx() const;
- float ty() 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);
-
- // transform data
- Rect makeBounds(int w, int h) const;
- vec2 transform(int x, int 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& rhs) const;
- // assumes the last row is < 0 , 0 , 1 >
- vec2 transform(const vec2& v) const;
- vec3 transform(const vec3& v) const;
-
- Transform inverse() const;
-
- // for debugging
- void dump(const char* name) const;
-
-private:
- struct mat33 {
- vec3 v[3];
- inline const vec3& operator [] (int i) const { return v[i]; }
- inline vec3& operator [] (int i) { return v[i]; }
- };
-
- enum { UNKNOWN_TYPE = 0x80000000 };
-
- uint32_t type() const;
- static bool absIsOne(float f);
- static bool isZero(float f);
-
- mat33 mMatrix;
- mutable uint32_t mType;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* ANDROID_TRANSFORM_H */
diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h
deleted file mode 100644
index a4c5262..0000000
--- a/services/surfaceflinger/clz.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACE_FLINGER_CLZ_H
-
-#include <stdint.h>
-
-namespace android {
-
-int inline clz(int32_t x) {
- return __builtin_clz(x);
-}
-
-template <typename T>
-static inline T min(T a, T b) {
- return a<b ? a : b;
-}
-template <typename T>
-static inline T min(T a, T b, T c) {
- return min(a, min(b, c));
-}
-template <typename T>
-static inline T min(T a, T b, T c, T d) {
- return min(a, b, min(c, d));
-}
-
-template <typename T>
-static inline T max(T a, T b) {
- return a>b ? a : b;
-}
-template <typename T>
-static inline T max(T a, T b, T c) {
- return max(a, max(b, c));
-}
-template <typename T>
-static inline T max(T a, T b, T c, T d) {
- return max(a, b, max(c, d));
-}
-
-template <typename T>
-static inline
-void swap(T& a, T& b) {
- T t(a);
- a = b;
- b = t;
-}
-
-
-}; // namespace android
-
-#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index ac147fe..cb368b0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -1,6 +1,5 @@
cc_library_shared {
name: "liblayers_proto",
- vendor_available: true,
export_include_dirs: ["include"],
srcs: [
@@ -11,6 +10,7 @@
shared_libs: [
"android.hardware.graphics.common@1.1",
+ "libgui",
"libui",
"libprotobuf-cpp-lite",
"libbase",
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index fcf42f0..7288232 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -31,17 +31,12 @@
int32_t lz = lhs->z;
int32_t rz = rhs->z;
if (lz != rz) {
- return (lz > rz) ? 1 : -1;
+ return lz < rz;
}
return lhs->id < rhs->id;
}
-bool sortLayerUniquePtrs(const std::unique_ptr<LayerProtoParser::Layer>& lhs,
- const std::unique_ptr<LayerProtoParser::Layer>& rhs) {
- return sortLayers(lhs.get(), rhs.get());
-}
-
const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo(
const LayersProto& layersProto) {
LayerGlobal layerGlobal;
@@ -52,84 +47,91 @@
return layerGlobal;
}
-std::vector<std::unique_ptr<LayerProtoParser::Layer>> LayerProtoParser::generateLayerTree(
- const LayersProto& layersProto) {
- std::unordered_map<int32_t, LayerProtoParser::Layer*> layerMap = generateMap(layersProto);
- std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers;
+LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) {
+ LayerTree layerTree;
+ layerTree.allLayers = generateLayerList(layersProto);
- for (std::pair<int32_t, Layer*> kv : layerMap) {
- if (kv.second->parent == nullptr) {
- // Make unique_ptr for top level layers since they are not children. This ensures there
- // will only be one unique_ptr made for each layer.
- layers.push_back(std::unique_ptr<Layer>(kv.second));
+ // find and sort the top-level layers
+ for (Layer& layer : layerTree.allLayers) {
+ if (layer.parent == nullptr) {
+ layerTree.topLevelLayers.push_back(&layer);
}
}
+ std::sort(layerTree.topLevelLayers.begin(), layerTree.topLevelLayers.end(), sortLayers);
- std::sort(layers.begin(), layers.end(), sortLayerUniquePtrs);
- return layers;
+ return layerTree;
}
-std::unordered_map<int32_t, LayerProtoParser::Layer*> LayerProtoParser::generateMap(
+std::vector<LayerProtoParser::Layer> LayerProtoParser::generateLayerList(
const LayersProto& layersProto) {
+ std::vector<Layer> layerList;
std::unordered_map<int32_t, Layer*> layerMap;
+ // build the layer list and the layer map
+ layerList.reserve(layersProto.layers_size());
+ layerMap.reserve(layersProto.layers_size());
for (int i = 0; i < layersProto.layers_size(); i++) {
- const LayerProto& layerProto = layersProto.layers(i);
- layerMap[layerProto.id()] = generateLayer(layerProto);
+ layerList.emplace_back(generateLayer(layersProto.layers(i)));
+ // this works because layerList never changes capacity
+ layerMap[layerList.back().id] = &layerList.back();
}
+ // fix up children and relatives
for (int i = 0; i < layersProto.layers_size(); i++) {
- const LayerProto& layerProto = layersProto.layers(i);
- updateChildrenAndRelative(layerProto, layerMap);
+ updateChildrenAndRelative(layersProto.layers(i), layerMap);
}
- return layerMap;
+ return layerList;
}
-LayerProtoParser::Layer* LayerProtoParser::generateLayer(const LayerProto& layerProto) {
- Layer* layer = new Layer();
- layer->id = layerProto.id();
- layer->name = layerProto.name();
- layer->type = layerProto.type();
- layer->transparentRegion = generateRegion(layerProto.transparent_region());
- layer->visibleRegion = generateRegion(layerProto.visible_region());
- layer->damageRegion = generateRegion(layerProto.damage_region());
- layer->layerStack = layerProto.layer_stack();
- layer->z = layerProto.z();
- layer->position = {layerProto.position().x(), layerProto.position().y()};
- layer->requestedPosition = {layerProto.requested_position().x(),
+LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerProto) {
+ Layer layer;
+ layer.id = layerProto.id();
+ layer.name = layerProto.name();
+ layer.type = layerProto.type();
+ layer.transparentRegion = generateRegion(layerProto.transparent_region());
+ layer.visibleRegion = generateRegion(layerProto.visible_region());
+ layer.damageRegion = generateRegion(layerProto.damage_region());
+ layer.layerStack = layerProto.layer_stack();
+ layer.z = layerProto.z();
+ layer.position = {layerProto.position().x(), layerProto.position().y()};
+ layer.requestedPosition = {layerProto.requested_position().x(),
layerProto.requested_position().y()};
- layer->size = {layerProto.size().w(), layerProto.size().h()};
- layer->crop = generateRect(layerProto.crop());
- layer->finalCrop = generateRect(layerProto.final_crop());
- layer->isOpaque = layerProto.is_opaque();
- layer->invalidate = layerProto.invalidate();
- layer->dataspace = layerProto.dataspace();
- layer->pixelFormat = layerProto.pixel_format();
- layer->color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
+ layer.size = {layerProto.size().w(), layerProto.size().h()};
+ layer.crop = generateRect(layerProto.crop());
+ layer.isOpaque = layerProto.is_opaque();
+ layer.invalidate = layerProto.invalidate();
+ layer.dataspace = layerProto.dataspace();
+ layer.pixelFormat = layerProto.pixel_format();
+ layer.color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
layerProto.color().a()};
- layer->requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
+ layer.requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
layerProto.requested_color().b(), layerProto.requested_color().a()};
- layer->flags = layerProto.flags();
- layer->transform = generateTransform(layerProto.transform());
- layer->requestedTransform = generateTransform(layerProto.requested_transform());
- layer->activeBuffer = generateActiveBuffer(layerProto.active_buffer());
- layer->queuedFrames = layerProto.queued_frames();
- layer->refreshPending = layerProto.refresh_pending();
- layer->hwcFrame = generateRect(layerProto.hwc_frame());
- layer->hwcCrop = generateFloatRect(layerProto.hwc_crop());
- layer->hwcTransform = layerProto.hwc_transform();
- layer->windowType = layerProto.window_type();
- layer->appId = layerProto.app_id();
- layer->hwcCompositionType = layerProto.hwc_composition_type();
- layer->isProtected = layerProto.is_protected();
+ layer.flags = layerProto.flags();
+ layer.transform = generateTransform(layerProto.transform());
+ layer.requestedTransform = generateTransform(layerProto.requested_transform());
+ layer.activeBuffer = generateActiveBuffer(layerProto.active_buffer());
+ layer.bufferTransform = generateTransform(layerProto.buffer_transform());
+ layer.queuedFrames = layerProto.queued_frames();
+ layer.refreshPending = layerProto.refresh_pending();
+ layer.hwcFrame = generateRect(layerProto.hwc_frame());
+ layer.hwcCrop = generateFloatRect(layerProto.hwc_crop());
+ layer.hwcTransform = layerProto.hwc_transform();
+ layer.hwcCompositionType = layerProto.hwc_composition_type();
+ layer.isProtected = layerProto.is_protected();
+ layer.cornerRadius = layerProto.corner_radius();
+ for (const auto& entry : layerProto.metadata()) {
+ const std::string& dataStr = entry.second;
+ std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
+ outData.resize(dataStr.size());
+ memcpy(outData.data(), dataStr.data(), dataStr.size());
+ }
return layer;
}
LayerProtoParser::Region LayerProtoParser::generateRegion(const RegionProto& regionProto) {
LayerProtoParser::Region region;
- region.id = regionProto.id();
for (int i = 0; i < regionProto.rect_size(); i++) {
const RectProto& rectProto = regionProto.rect(i);
region.rects.push_back(generateRect(rectProto));
@@ -186,9 +188,7 @@
for (int i = 0; i < layerProto.children_size(); i++) {
if (layerMap.count(layerProto.children(i)) > 0) {
- // Only make unique_ptrs for children since they are guaranteed to be unique, only one
- // parent per child. This ensures there will only be one unique_ptr made for each layer.
- currLayer->children.push_back(std::unique_ptr<Layer>(layerMap[layerProto.children(i)]));
+ currLayer->children.push_back(layerMap[layerProto.children(i)]);
}
}
@@ -198,42 +198,41 @@
}
}
- if (layerProto.has_parent()) {
+ if (layerProto.parent() != -1) {
if (layerMap.count(layerProto.parent()) > 0) {
currLayer->parent = layerMap[layerProto.parent()];
}
}
- if (layerProto.has_z_order_relative_of()) {
+ if (layerProto.z_order_relative_of() != -1) {
if (layerMap.count(layerProto.z_order_relative_of()) > 0) {
currLayer->zOrderRelativeOf = layerMap[layerProto.z_order_relative_of()];
}
}
}
-std::string LayerProtoParser::layersToString(
- std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers) {
+std::string LayerProtoParser::layerTreeToString(const LayerTree& layerTree) {
std::string result;
- for (std::unique_ptr<LayerProtoParser::Layer>& layer : layers) {
+ for (const LayerProtoParser::Layer* layer : layerTree.topLevelLayers) {
if (layer->zOrderRelativeOf != nullptr) {
continue;
}
- result.append(layerToString(layer.get()).c_str());
+ result.append(layerToString(layer));
}
return result;
}
-std::string LayerProtoParser::layerToString(LayerProtoParser::Layer* layer) {
+std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
std::string result;
std::vector<Layer*> traverse(layer->relatives);
- for (std::unique_ptr<LayerProtoParser::Layer>& child : layer->children) {
+ for (LayerProtoParser::Layer* child : layer->children) {
if (child->zOrderRelativeOf != nullptr) {
continue;
}
- traverse.push_back(child.get());
+ traverse.push_back(child);
}
std::sort(traverse.begin(), traverse.end(), sortLayers);
@@ -244,13 +243,13 @@
if (relative->z >= 0) {
break;
}
- result.append(layerToString(relative).c_str());
+ result.append(layerToString(relative));
}
- result.append(layer->to_string().c_str());
+ result.append(layer->to_string());
result.append("\n");
for (; i < traverse.size(); i++) {
auto& relative = traverse[i];
- result.append(layerToString(relative).c_str());
+ result.append(layerToString(relative));
}
return result;
@@ -298,8 +297,8 @@
z, static_cast<double>(position.x), static_cast<double>(position.y), size.x,
size.y);
- StringAppendF(&result, "crop=%s, finalCrop=%s, ", crop.to_string().c_str(),
- finalCrop.to_string().c_str());
+ StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
+ StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
@@ -312,8 +311,16 @@
StringAppendF(&result, " zOrderRelativeOf=%s\n",
zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str());
+ StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str());
StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending);
- StringAppendF(&result, " windowType=%d, appId=%d", windowType, appId);
+ StringAppendF(&result, " metadata={");
+ bool first = true;
+ for (const auto& entry : metadata.mMap) {
+ if (!first) result.append(", ");
+ first = false;
+ result.append(metadata.itemToString(entry.first, ":"));
+ }
+ result.append("}");
return result;
}
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 74a6f28..d1b2b1f 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -17,6 +17,7 @@
#include <layerproto/LayerProtoHeader.h>
+#include <gui/LayerMetadata.h>
#include <math/vec4.h>
#include <memory>
@@ -80,7 +81,7 @@
public:
int32_t id;
std::string name;
- std::vector<std::unique_ptr<Layer>> children;
+ std::vector<Layer*> children;
std::vector<Layer*> relatives;
std::string type;
LayerProtoParser::Region transparentRegion;
@@ -92,7 +93,6 @@
float2 requestedPosition;
int2 size;
LayerProtoParser::Rect crop;
- LayerProtoParser::Rect finalCrop;
bool isOpaque;
bool invalidate;
std::string dataspace;
@@ -105,15 +105,16 @@
Layer* parent = 0;
Layer* zOrderRelativeOf = 0;
LayerProtoParser::ActiveBuffer activeBuffer;
+ Transform bufferTransform;
int32_t queuedFrames;
bool refreshPending;
LayerProtoParser::Rect hwcFrame;
LayerProtoParser::FloatRect hwcCrop;
int32_t hwcTransform;
- int32_t windowType;
- int32_t appId;
int32_t hwcCompositionType;
bool isProtected;
+ float cornerRadius;
+ LayerMetadata metadata;
std::string to_string() const;
};
@@ -126,13 +127,22 @@
int32_t globalTransform;
};
+ class LayerTree {
+ public:
+ // all layers in LayersProto and in the original order
+ std::vector<Layer> allLayers;
+
+ // pointers to top-level layers in allLayers
+ std::vector<Layer*> topLevelLayers;
+ };
+
static const LayerGlobal generateLayerGlobalInfo(const LayersProto& layersProto);
- static std::vector<std::unique_ptr<Layer>> generateLayerTree(const LayersProto& layersProto);
- static std::string layersToString(std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers);
+ static LayerTree generateLayerTree(const LayersProto& layersProto);
+ static std::string layerTreeToString(const LayerTree& layerTree);
private:
- static std::unordered_map<int32_t, Layer*> generateMap(const LayersProto& layersProto);
- static LayerProtoParser::Layer* generateLayer(const LayerProto& layerProto);
+ static std::vector<Layer> generateLayerList(const LayersProto& layersProto);
+ static LayerProtoParser::Layer generateLayer(const LayerProto& layerProto);
static LayerProtoParser::Region generateRegion(const RegionProto& regionProto);
static LayerProtoParser::Rect generateRect(const RectProto& rectProto);
static LayerProtoParser::FloatRect generateFloatRect(const FloatRectProto& rectProto);
@@ -142,7 +152,7 @@
static void updateChildrenAndRelative(const LayerProto& layerProto,
std::unordered_map<int32_t, Layer*>& layerMap);
- static std::string layerToString(LayerProtoParser::Layer* layer);
+ static std::string layerToString(const LayerProtoParser::Layer* layer);
};
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index edf56ab..fd4695e 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -1,144 +1,157 @@
// Definitions for SurfaceFlinger layers.
-syntax = "proto2";
+syntax = "proto3";
option optimize_for = LITE_RUNTIME;
package android.surfaceflinger;
// Contains a list of all layers.
message LayersProto {
repeated LayerProto layers = 1;
- optional SizeProto resolution = 2;
- optional string color_mode = 3;
- optional string color_transform = 4;
- optional int32 global_transform = 5;
+ SizeProto resolution = 2;
+ string color_mode = 3;
+ string color_transform = 4;
+ int32 global_transform = 5;
}
// Information about each layer.
message LayerProto {
// unique id per layer.
- optional int32 id = 1;
+ int32 id = 1;
// unique name per layer.
- optional string name = 2;
+ string name = 2;
// list of children this layer may have. May be empty.
repeated int32 children = 3;
// list of layers that are z order relative to this layer.
repeated int32 relatives = 4;
// The type of layer, ex Color, Layer
- optional string type = 5;
- optional RegionProto transparent_region = 6;
- optional RegionProto visible_region = 7;
- optional RegionProto damage_region = 8;
- optional uint32 layer_stack = 9;
+ string type = 5;
+ RegionProto transparent_region = 6;
+ RegionProto visible_region = 7;
+ RegionProto damage_region = 8;
+ uint32 layer_stack = 9;
// The layer's z order. Can be z order in layer stack, relative to parent,
// or relative to another layer specified in zOrderRelative.
- optional int32 z = 10;
+ int32 z = 10;
// The layer's position on the display.
- optional PositionProto position = 11;
+ PositionProto position = 11;
// The layer's requested position.
- optional PositionProto requested_position = 12;
+ PositionProto requested_position = 12;
// The layer's size.
- optional SizeProto size = 13;
+ SizeProto size = 13;
// The layer's crop in it's own bounds.
- optional RectProto crop = 14;
+ RectProto crop = 14;
// The layer's crop in it's parent's bounds.
- optional RectProto final_crop = 15;
- optional bool is_opaque = 16;
- optional bool invalidate = 17;
- optional string dataspace = 18;
- optional string pixel_format = 19;
+ RectProto final_crop = 15 [deprecated=true];
+ bool is_opaque = 16;
+ bool invalidate = 17;
+ string dataspace = 18;
+ string pixel_format = 19;
// The layer's actual color.
- optional ColorProto color = 20;
+ ColorProto color = 20;
// The layer's requested color.
- optional ColorProto requested_color = 21;
+ ColorProto requested_color = 21;
// Can be any combination of
// hidden = 0x01
// opaque = 0x02,
// secure = 0x80,
- optional uint32 flags = 22;
+ uint32 flags = 22;
// The layer's actual transform
- optional TransformProto transform = 23;
+ TransformProto transform = 23;
// The layer's requested transform.
- optional TransformProto requested_transform = 24;
+ TransformProto requested_transform = 24;
// The parent layer. This value can be null if there is no parent.
- optional int32 parent = 25 [default = -1];
+ int32 parent = 25;
// The layer that this layer has a z order relative to. This value can be null.
- optional int32 z_order_relative_of = 26 [default = -1];
+ int32 z_order_relative_of = 26;
// This value can be null if there's nothing to draw.
- optional ActiveBufferProto active_buffer = 27;
+ ActiveBufferProto active_buffer = 27;
// The number of frames available.
- optional int32 queued_frames = 28;
- optional bool refresh_pending = 29;
+ int32 queued_frames = 28;
+ bool refresh_pending = 29;
// The layer's composer backend destination frame
- optional RectProto hwc_frame = 30;
+ RectProto hwc_frame = 30;
// The layer's composer backend source crop
- optional FloatRectProto hwc_crop = 31;
+ FloatRectProto hwc_crop = 31;
// The layer's composer backend transform
- optional int32 hwc_transform = 32;
- optional int32 window_type = 33;
- optional int32 app_id = 34;
+ int32 hwc_transform = 32;
+ int32 window_type = 33 [deprecated=true];
+ int32 app_id = 34 [deprecated=true];
// The layer's composition type
- optional int32 hwc_composition_type = 35;
+ int32 hwc_composition_type = 35;
// If it's a buffer layer, indicate if the content is protected
- optional bool is_protected = 36;
+ bool is_protected = 36;
// Current frame number being rendered.
- optional uint64 curr_frame = 37;
+ uint64 curr_frame = 37;
// A list of barriers that the layer is waiting to update state.
repeated BarrierLayerProto barrier_layer = 38;
+ // If active_buffer is not null, record its transform.
+ TransformProto buffer_transform = 39;
+ int32 effective_scaling_mode = 40;
+ // Layer's corner radius.
+ float corner_radius = 41;
+ // Metadata map. May be empty.
+ map<int32, bytes> metadata = 42;
+
+ TransformProto effective_transform = 43;
+ FloatRectProto source_bounds = 44;
+ FloatRectProto bounds = 45;
+ FloatRectProto screen_bounds = 46;
}
message PositionProto {
- optional float x = 1;
- optional float y = 2;
+ float x = 1;
+ float y = 2;
}
message SizeProto {
- optional int32 w = 1;
- optional int32 h = 2;
+ int32 w = 1;
+ int32 h = 2;
}
message TransformProto {
- optional float dsdx = 1;
- optional float dtdx = 2;
- optional float dsdy = 3;
- optional float dtdy = 4;
+ float dsdx = 1;
+ float dtdx = 2;
+ float dsdy = 3;
+ float dtdy = 4;
+ int32 type = 5;
}
message RegionProto {
- optional uint64 id = 1;
+ reserved 1; // Previously: uint64 id
repeated RectProto rect = 2;
}
message RectProto {
- optional int32 left = 1;
- optional int32 top = 2;
- optional int32 right = 3;
- optional int32 bottom = 4;
+ int32 left = 1;
+ int32 top = 2;
+ int32 right = 3;
+ int32 bottom = 4;
}
message FloatRectProto {
- optional float left = 1;
- optional float top = 2;
- optional float right = 3;
- optional float bottom = 4;
+ float left = 1;
+ float top = 2;
+ float right = 3;
+ float bottom = 4;
}
message ActiveBufferProto {
- optional uint32 width = 1;
- optional uint32 height = 2;
- optional uint32 stride = 3;
- optional int32 format = 4;
+ uint32 width = 1;
+ uint32 height = 2;
+ uint32 stride = 3;
+ int32 format = 4;
}
message ColorProto {
- optional float r = 1;
- optional float g = 2;
- optional float b = 3;
- optional float a = 4;
+ float r = 1;
+ float g = 2;
+ float b = 3;
+ float a = 4;
}
message BarrierLayerProto {
// layer id the barrier is waiting on.
- optional int32 id = 1;
+ int32 id = 1;
// frame number the barrier is waiting on.
- optional uint64 frame_number = 2;
+ uint64 frame_number = 2;
}
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index aefe704..e7986d3 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -21,30 +21,35 @@
#include <android/frameworks/displayservice/1.0/IDisplayService.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
-#include <binder/IServiceManager.h>
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
#include <displayservice/DisplayService.h>
#include <hidl/LegacySupport.h>
#include <processgroup/sched_policy.h>
-#include <configstore/Utils.h>
#include "SurfaceFlinger.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceFlingerProperties.h"
using namespace android;
static status_t startGraphicsAllocatorService() {
using android::hardware::configstore::getBool;
using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
- if (!getBool<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+ if (!android::sysprop::start_graphics_allocator_service(false)) {
return OK;
}
- using android::hardware::graphics::allocator::V2_0::IAllocator;
+ status_t result = hardware::registerPassthroughServiceImplementation<
+ android::hardware::graphics::allocator::V3_0::IAllocator>();
+ if (result == OK) {
+ return OK;
+ }
- status_t result =
- hardware::registerPassthroughServiceImplementation<IAllocator>();
+ result = hardware::registerPassthroughServiceImplementation<
+ android::hardware::graphics::allocator::V2_0::IAllocator>();
if (result != OK) {
ALOGE("could not start graphics allocator service");
return result;
@@ -84,7 +89,7 @@
ps->startThreadPool();
// instantiate surfaceflinger
- sp<SurfaceFlinger> flinger = new SurfaceFlinger();
+ sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
new file mode 100644
index 0000000..fe6dc93
--- /dev/null
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -0,0 +1,309 @@
+# 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.
+
+module: "android.sysprop.SurfaceFlingerProperties"
+owner: Platform
+
+# The following two propertiess define (respectively):
+#
+# - The phase offset between hardware vsync and when apps are woken up by the
+# Choreographer callback
+# - The phase offset between hardware vsync and when SurfaceFlinger wakes up
+# to consume input
+# Their values may be tuned to trade off between display pipeline latency (both
+# overall latency and the lengths of the app --> SF and SF --> display phases)
+# and frame delivery jitter (which typically manifests as "jank" or "jerkiness"
+# while interacting with the device). The default values must produce a
+# relatively low amount of jitter at the expense of roughly two frames of
+# app --> display latency, and unless significant testing is performed to avoid
+# increased display jitter (both manual investigation using systrace [1] and
+# automated testing using dumpsys gfxinfo [2] are recommended), they should not
+# be modified.
+#
+# [1] https://developer.android.com/studio/profile/systrace.html
+# [2] https://developer.android.com/training/testing/performance.html
+prop {
+ api_name: "vsync_event_phase_offset_ns"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns"
+}
+
+prop {
+ api_name: "vsync_sf_event_phase_offset_ns"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns"
+}
+
+# Instruct the Render Engine to use EGL_IMG_context_priority hint if available.
+prop {
+ api_name: "use_context_priority"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_context_priority"
+}
+
+# Controls the number of buffers SurfaceFlinger will allocate for use in FramebufferSurface.
+prop {
+ api_name: "max_frame_buffer_acquired_buffers"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
+}
+
+# hasWideColorDisplay indicates that the device has
+# or can support a wide-color display, e.g. color space
+# greater than sRGB. Typical display may have same
+# color primaries as DCI-P3.
+# Indicate support for this feature by setting
+# TARGET_HAS_WIDE_COLOR_DISPLAY to true in BoardConfig.mk
+# This also means that the device is color managed.
+# A color managed device will use the appropriate
+# display mode depending on the content on the screen.
+# Default is sRGB.
+prop {
+ api_name: "has_wide_color_display"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.has_wide_color_display"
+}
+
+# Indicates if Sync framework is available. Sync framework provides fence
+# mechanism which significantly reduces buffer processing latency.
+prop {
+ api_name: "running_without_sync_framework"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.running_without_sync_framework"
+}
+
+# hwHDRDisplay indicates that the device has an High Dynamic Range display.
+# A display is considered High Dynamic Range if it
+#
+# 1. is a wide color gamut display, typically DCI-P3 or lager
+# 2. has high luminance capability, typically 540 nits or higher at 10% OPR
+#
+# Indicate support for this feature by setting
+# ro.surface_flinger.has_HDR_display to true in device.mk
+# ro.surface_flinger.has_wide_color_display must be set to true when
+# ro.surface_flinger.has_HDR_display is true.
+prop {
+ api_name: "has_HDR_display"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.has_HDR_display"
+}
+
+# Specify the offset in nanoseconds to add to vsync time when timestamping present fences.
+prop {
+ api_name: "present_time_offset_from_vsync_ns"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
+}
+
+# Some hardware can do RGB->YUV conversion more efficiently in hardware
+# controlled by HWC than in hardware controlled by the video encoder.
+# This instruct VirtualDisplaySurface to use HWC for such conversion on
+# GL composition.
+prop {
+ api_name: "force_hwc_copy_for_virtual_displays"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
+}
+
+# Maximum dimension supported by HWC for virtual display.
+# Must be equals to min(max_width, max_height).
+prop {
+ api_name: "max_virtual_display_dimension"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_virtual_display_dimension"
+}
+
+# Return true if surface flinger should use vr flinger for compatible vr
+# apps, false otherwise. Devices that will never be running vr apps should
+# return false to avoid extra resource usage. Daydream ready devices must
+# return true for full vr support.
+prop {
+ api_name: "use_vr_flinger"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_vr_flinger"
+}
+
+# Returns true if surface flinger should start
+# hardware.graphics.allocator@2.0::IAllocator service.
+prop {
+ api_name: "start_graphics_allocator_service"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.start_graphics_allocator_service"
+}
+
+# Returns the orientation of the primary display device.
+prop {
+ api_name: "primary_display_orientation"
+ type: Enum
+ enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.primary_display_orientation"
+}
+
+# useColorManagement indicates whether SurfaceFlinger should manage color
+# by switching to appropriate color mode automatically depending on the
+# Dataspace of the surfaces on screen.
+prop {
+ api_name: "use_color_management"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_color_management"
+}
+
+# The following four propertiess define:
+# Returns the default data space and pixel format that SurfaceFlinger
+# expects to receive and output as well as the wide color gamut data space
+# and pixel format for wide color gamut surfaces.
+# To determine the data space and pixel format, there are a few things
+# we recommend to consider:
+#
+# 1. Hardware composer's capability to composite contents with the chosen
+# data space and pixel format efficiently;
+# 2. Hardware composer's ability to composite contents when sRGB contents
+# and the chosen wide color gamut data space contents coexist;
+# 3. For better blending, consider using pixel format where the alpha
+# channel has as many bits as the RGB color channel.
+# 4. Memory consumption and efficient buffer compression when considering
+# more bits in pixel format.
+
+# dataspace is the default data space that SurfaceFlinger expects.
+# The data space must not be Dataspace::UNKNOWN, if unspecified,
+# the default data space is Dataspace::V0_SRGB;
+prop {
+ api_name: "default_composition_dataspace"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.default_composition_dataspace"
+}
+
+# pixelFormat is the default pixel format that SurfaceFlinger
+# expects. If unspecified, the default pixel format is
+# PixelFormat::RGBA_8888.
+prop {
+ api_name: "default_composition_pixel_format"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.default_composition_pixel_format"
+}
+
+# wcgDataspace is the data space that SurfaceFlinger expects for
+# wide color gamut surfaces.
+# When hasWideColorDisplay returns true, this API must return a
+# valid wide color gamut data space.
+# The data space must not be UNKNOWN, if unspecified, the data space
+# is V0_SRGB by default, which essentially indicates there's no wide
+# color gamut, meaning hasWideColorDisplay returns false.
+prop {
+ api_name: "wcg_composition_dataspace"
+ type: Long
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.wcg_composition_dataspace"
+}
+
+# wcgPixelFormat is the pixel format that SurfaceFlinger expects for
+# wide color gamut surfaces. If unspecified, the pixel format is
+# PixelFormat::RGBA_8888 by default.
+prop {
+ api_name: "wcg_composition_pixel_format"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.wcg_composition_pixel_format"
+}
+
+# Return the native panel primary data. The data includes red, green,
+# blue and white. The primary format is CIE 1931 XYZ color space.
+# If unspecified, the primaries is sRGB gamut by default.
+
+prop {
+ api_name: "display_primary_red"
+ type: DoubleList
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_red"
+}
+
+prop {
+ api_name: "display_primary_green"
+ type: DoubleList
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_green"
+}
+
+prop {
+ api_name: "display_primary_blue"
+ type: DoubleList
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_blue"
+}
+
+prop {
+ api_name: "display_primary_white"
+ type: DoubleList
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_white"
+}
+
+# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
+# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
+# refresh rate. Setting this property to 0 means there is no timer.
+prop {
+ api_name: "set_idle_timer_ms"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_idle_timer_ms"
+}
+
+# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
+# screen refresh rate based on that.
+prop {
+ api_name: "use_smart_90_for_video"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_smart_90_for_video"
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index c511c5e..f121a95 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -17,6 +17,8 @@
defaults: ["surfaceflinger_defaults"],
test_suites: ["device-tests"],
srcs: [
+ "BufferGenerator.cpp",
+ "Credentials_test.cpp",
"Stress_test.cpp",
"SurfaceInterceptor_test.cpp",
"Transaction_test.cpp",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
new file mode 100644
index 0000000..8ddda60
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+
+#include <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include "BufferGenerator.h"
+#include "BufferGeneratorShader.h"
+
+namespace android {
+
+/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
+ * away. The fences are sent to the requester via a callback */
+class SurfaceManager {
+public:
+ /* Returns a fence from egl */
+ using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>;
+
+ /* Listens for a new frame, detaches the buffer and returns the fence
+ * through saved callback. */
+ class BufferListener : public ConsumerBase::FrameAvailableListener {
+ public:
+ BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
+ : mConsumer(consumer), mCallback(callback) {}
+
+ void onFrameAvailable(const BufferItem& /*item*/) {
+ BufferItem item;
+
+ if (mConsumer->acquireBuffer(&item, 0)) return;
+ if (mConsumer->detachBuffer(item.mSlot)) return;
+
+ mCallback(item.mGraphicBuffer, item.mFence->dup());
+ }
+
+ private:
+ sp<IGraphicBufferConsumer> mConsumer;
+ BufferCallback mCallback;
+ };
+
+ /* Creates a buffer listener that waits on a new frame from the buffer
+ * queue. */
+ void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
+ BufferCallback callback) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ consumer->setDefaultBufferSize(width, height);
+ consumer->setDefaultBufferFormat(format);
+
+ mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+
+ mListener = new BufferListener(consumer, callback);
+ mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+ mSurface = new Surface(producer, true);
+ }
+
+ /* Used by Egl manager. The surface is never displayed. */
+ sp<Surface> getSurface() const { return mSurface; }
+
+private:
+ sp<BufferItemConsumer> mBufferItemConsumer;
+ sp<BufferListener> mListener;
+ /* Used by Egl manager. The surface is never displayed */
+ sp<Surface> mSurface;
+};
+
+/* Used to generate valid fences. It is not possible to create a dummy sync
+ * fence for testing. Egl can generate buffers along with a valid fence.
+ * The buffer cannot be guaranteed to be the same format across all devices so
+ * a CPU filled buffer is used instead. The Egl fence is used along with the
+ * CPU filled buffer. */
+class EglManager {
+public:
+ EglManager()
+ : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
+
+ ~EglManager() { cleanup(); }
+
+ int initialize(sp<Surface> surface) {
+ mSurface = surface;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL_NO_DISPLAY) return false;
+
+ EGLint major;
+ EGLint minor;
+ if (!eglInitialize(mEglDisplay, &major, &minor)) {
+ ALOGW("Could not initialize EGL");
+ return false;
+ }
+
+ /* We're going to use a 1x1 pbuffer surface later on
+ * The configuration distance doesn't really matter for what we're
+ * trying to do */
+ EGLint configAttrs[] = {EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 8,
+ EGL_BLUE_SIZE,
+ 8,
+ EGL_ALPHA_SIZE,
+ 0,
+ EGL_DEPTH_SIZE,
+ 24,
+ EGL_STENCIL_SIZE,
+ 0,
+ EGL_NONE};
+
+ EGLConfig configs[1];
+ EGLint configCnt;
+ if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1, &configCnt)) {
+ ALOGW("Could not select EGL configuration");
+ eglReleaseThread();
+ eglTerminate(mEglDisplay);
+ return false;
+ }
+
+ if (configCnt <= 0) {
+ ALOGW("Could not find EGL configuration");
+ eglReleaseThread();
+ eglTerminate(mEglDisplay);
+ return false;
+ }
+
+ /* These objects are initialized below but the default "null" values are
+ * used to cleanup properly at any point in the initialization sequence */
+ EGLint attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+ mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT, attrs);
+ if (mEglContext == EGL_NO_CONTEXT) {
+ ALOGW("Could not create EGL context");
+ cleanup();
+ return false;
+ }
+
+ EGLint majorVersion;
+ if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) {
+ ALOGW("Could not query EGL version");
+ cleanup();
+ return false;
+ }
+
+ if (majorVersion != 3) {
+ ALOGW("Unsupported EGL version");
+ cleanup();
+ return false;
+ }
+
+ EGLint surfaceAttrs[] = {EGL_NONE};
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0], mSurface.get(), surfaceAttrs);
+ if (mEglSurface == EGL_NO_SURFACE) {
+ ALOGW("Could not create EGL surface");
+ cleanup();
+ return false;
+ }
+
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ ALOGW("Could not change current EGL context");
+ cleanup();
+ return false;
+ }
+
+ return true;
+ }
+
+ void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); }
+
+ void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); }
+
+private:
+ void cleanup() {
+ if (mEglDisplay == EGL_NO_DISPLAY) return;
+ if (mEglSurface != EGL_NO_SURFACE) eglDestroySurface(mEglDisplay, mEglSurface);
+ if (mEglContext != EGL_NO_CONTEXT) eglDestroyContext(mEglDisplay, mEglContext);
+
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglReleaseThread();
+ eglTerminate(mEglDisplay);
+ }
+
+ sp<Surface> mSurface;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
+};
+
+class Program {
+public:
+ ~Program() {
+ if (mInitialized) {
+ glDetachShader(mProgram, mVertexShader);
+ glDetachShader(mProgram, mFragmentShader);
+
+ glDeleteShader(mVertexShader);
+ glDeleteShader(mFragmentShader);
+
+ glDeleteProgram(mProgram);
+ }
+ }
+
+ bool initialize(const char* vertex, const char* fragment) {
+ mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+ if (!mVertexShader) {
+ return false;
+ }
+
+ mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+ if (!mFragmentShader) {
+ return false;
+ }
+
+ mProgram = glCreateProgram();
+ glAttachShader(mProgram, mVertexShader);
+ glAttachShader(mProgram, mFragmentShader);
+
+ glLinkProgram(mProgram);
+
+ GLint status;
+ glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
+ if (status != GL_TRUE) {
+ GLint length = 0;
+ glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);
+ if (length > 1) {
+ GLchar log[length];
+ glGetProgramInfoLog(mProgram, length, nullptr, &log[0]);
+ ALOGE("%s", log);
+ }
+ ALOGE("Error while linking shaders");
+ return false;
+ }
+ mInitialized = true;
+ return true;
+ }
+
+ void use() const { glUseProgram(mProgram); }
+
+ void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
+
+ void bindVec3(GLint location, const vec3* v, uint32_t count) const {
+ glUniform3fv(location, count, &(v->x));
+ }
+
+ void bindFloat(GLint location, float v) { glUniform1f(location, v); }
+
+private:
+ GLuint buildShader(const char* source, GLenum type) const {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &source, nullptr);
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source);
+ // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+ // use a fixed size instead
+ GLchar log[512];
+ glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
+ ALOGE("Shader info log: %s", log);
+ return 0;
+ }
+
+ return shader;
+ }
+
+ GLuint mProgram = 0;
+ GLuint mVertexShader = 0;
+ GLuint mFragmentShader = 0;
+ bool mInitialized = false;
+};
+
+BufferGenerator::BufferGenerator()
+ : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
+ const float width = 1000.0;
+ const float height = 1000.0;
+
+ auto setBufferWithContext =
+ std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
+ mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+
+ if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
+
+ mEglManager->makeCurrent();
+
+ if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
+ mProgram->use();
+ mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+ mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]);
+
+ mInitialized = true;
+}
+
+BufferGenerator::~BufferGenerator() {
+ mEglManager->makeCurrent();
+}
+
+status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ // mMutex is used to protect get() from getting called by multiple threads at the same time
+ static std::mutex mMutex;
+ std::lock_guard lock(mMutex);
+
+ if (!mInitialized) {
+ if (outBuffer) {
+ *outBuffer = nullptr;
+ }
+ if (*outFence) {
+ *outFence = nullptr;
+ }
+ return -EINVAL;
+ }
+
+ // Generate a buffer and fence. They will be returned through the setBuffer callback
+ mEglManager->makeCurrent();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch;
+ mProgram->bindFloat(1, time.count());
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ mPending = true;
+ mEglManager->present();
+
+ // Wait for the setBuffer callback
+ if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2),
+ [this] { return !mPending; })) {
+ ALOGE("failed to set buffer and fence");
+ return -ETIME;
+ }
+
+ // Return buffer and fence
+ if (outBuffer) {
+ *outBuffer = mGraphicBuffer;
+ }
+ if (outFence) {
+ *outFence = new Fence(mFence);
+ } else {
+ close(mFence);
+ }
+ mGraphicBuffer = nullptr;
+ mFence = -1;
+
+ return NO_ERROR;
+}
+
+// static
+void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
+ void* bufferGenerator) {
+ BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator);
+ generator->mGraphicBuffer = buffer;
+ generator->mFence = fence;
+ generator->mPending = false;
+ generator->mConditionVariable.notify_all();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
new file mode 100644
index 0000000..a3ffe86
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 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 <condition_variable>
+#include <mutex>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+class SurfaceManager;
+class EglManager;
+class Program;
+
+class BufferGenerator {
+public:
+ BufferGenerator();
+ ~BufferGenerator();
+
+ /* Get a new fence */
+ status_t get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+
+ /* Static callback that sets the fence on a particular instance */
+ static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+
+private:
+ bool mInitialized = false;
+
+ std::unique_ptr<SurfaceManager> mSurfaceManager;
+ std::unique_ptr<EglManager> mEglManager;
+ std::unique_ptr<Program> mProgram;
+
+ std::condition_variable_any mConditionVariable;
+
+ sp<GraphicBuffer> mGraphicBuffer;
+ int32_t mFence = -1;
+ bool mPending = false;
+
+ using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
+ Epoch mEpoch = std::chrono::steady_clock::now();
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGeneratorShader.h b/services/surfaceflinger/tests/BufferGeneratorShader.h
new file mode 100644
index 0000000..564cda3
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGeneratorShader.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2018 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 <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+static const char* VERTEX_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) in vec4 mesh_position;
+
+void main() {
+ gl_Position = mesh_position;
+}
+)SHADER__";
+
+static const char* FRAGMENT_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) uniform vec4 resolution;
+layout(location = 1) uniform float time;
+layout(location = 2) uniform vec3[4] SPHERICAL_HARMONICS;
+
+layout(location = 0) out vec4 fragColor;
+
+#define saturate(x) clamp(x, 0.0, 1.0)
+#define PI 3.14159265359
+
+//------------------------------------------------------------------------------
+// Distance field functions
+//------------------------------------------------------------------------------
+
+float sdPlane(in vec3 p) {
+ return p.y;
+}
+
+float sdSphere(in vec3 p, float s) {
+ return length(p) - s;
+}
+
+float sdTorus(in vec3 p, in vec2 t) {
+ return length(vec2(length(p.xz) - t.x, p.y)) - t.y;
+}
+
+vec2 opUnion(vec2 d1, vec2 d2) {
+ return d1.x < d2.x ? d1 : d2;
+}
+
+vec2 scene(in vec3 position) {
+ vec2 scene = opUnion(
+ vec2(sdPlane(position), 1.0),
+ vec2(sdSphere(position - vec3(0.0, 0.4, 0.0), 0.4), 12.0)
+ );
+ return scene;
+}
+
+//------------------------------------------------------------------------------
+// Ray casting
+//------------------------------------------------------------------------------
+
+float shadow(in vec3 origin, in vec3 direction, in float tmin, in float tmax) {
+ float hit = 1.0;
+
+ for (float t = tmin; t < tmax; ) {
+ float h = scene(origin + direction * t).x;
+ if (h < 0.001) return 0.0;
+ t += h;
+ hit = min(hit, 10.0 * h / t);
+ }
+
+ return clamp(hit, 0.0, 1.0);
+}
+
+vec2 traceRay(in vec3 origin, in vec3 direction) {
+ float tmin = 0.02;
+ float tmax = 20.0;
+
+ float material = -1.0;
+ float t = tmin;
+
+ for ( ; t < tmax; ) {
+ vec2 hit = scene(origin + direction * t);
+ if (hit.x < 0.002 || t > tmax) break;
+ t += hit.x;
+ material = hit.y;
+ }
+
+ if (t > tmax) {
+ material = -1.0;
+ }
+
+ return vec2(t, material);
+}
+
+vec3 normal(in vec3 position) {
+ vec3 epsilon = vec3(0.001, 0.0, 0.0);
+ vec3 n = vec3(
+ scene(position + epsilon.xyy).x - scene(position - epsilon.xyy).x,
+ scene(position + epsilon.yxy).x - scene(position - epsilon.yxy).x,
+ scene(position + epsilon.yyx).x - scene(position - epsilon.yyx).x);
+ return normalize(n);
+}
+
+//------------------------------------------------------------------------------
+// BRDF
+//------------------------------------------------------------------------------
+
+float pow5(float x) {
+ float x2 = x * x;
+ return x2 * x2 * x;
+}
+
+float D_GGX(float linearRoughness, float NoH, const vec3 h) {
+ // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
+ float oneMinusNoHSquared = 1.0 - NoH * NoH;
+ float a = NoH * linearRoughness;
+ float k = linearRoughness / (oneMinusNoHSquared + a * a);
+ float d = k * k * (1.0 / PI);
+ return d;
+}
+
+float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) {
+ // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
+ float a2 = linearRoughness * linearRoughness;
+ float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
+ float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
+ return 0.5 / (GGXV + GGXL);
+}
+
+vec3 F_Schlick(const vec3 f0, float VoH) {
+ // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"
+ return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH);
+}
+
+float F_Schlick(float f0, float f90, float VoH) {
+ return f0 + (f90 - f0) * pow5(1.0 - VoH);
+}
+
+float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) {
+ // Burley 2012, "Physically-Based Shading at Disney"
+ float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH;
+ float lightScatter = F_Schlick(1.0, f90, NoL);
+ float viewScatter = F_Schlick(1.0, f90, NoV);
+ return lightScatter * viewScatter * (1.0 / PI);
+}
+
+float Fd_Lambert() {
+ return 1.0 / PI;
+}
+
+//------------------------------------------------------------------------------
+// Indirect lighting
+//------------------------------------------------------------------------------
+
+vec3 Irradiance_SphericalHarmonics(const vec3 n) {
+ return max(
+ SPHERICAL_HARMONICS[0]
+ + SPHERICAL_HARMONICS[1] * (n.y)
+ + SPHERICAL_HARMONICS[2] * (n.z)
+ + SPHERICAL_HARMONICS[3] * (n.x)
+ , 0.0);
+}
+
+vec2 PrefilteredDFG_Karis(float roughness, float NoV) {
+ // Karis 2014, "Physically Based Material on Mobile"
+ const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
+ const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040);
+
+ vec4 r = roughness * c0 + c1;
+ float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
+
+ return vec2(-1.04, 1.04) * a004 + r.zw;
+}
+
+//------------------------------------------------------------------------------
+// Tone mapping and transfer functions
+//------------------------------------------------------------------------------
+
+vec3 Tonemap_ACES(const vec3 x) {
+ // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
+ const float a = 2.51;
+ const float b = 0.03;
+ const float c = 2.43;
+ const float d = 0.59;
+ const float e = 0.14;
+ return (x * (a * x + b)) / (x * (c * x + d) + e);
+}
+
+vec3 OECF_sRGBFast(const vec3 linear) {
+ return pow(linear, vec3(1.0 / 2.2));
+}
+
+//------------------------------------------------------------------------------
+// Rendering
+//------------------------------------------------------------------------------
+
+vec3 render(in vec3 origin, in vec3 direction, out float distance) {
+ // Sky gradient
+ vec3 color = vec3(0.65, 0.85, 1.0) + direction.y * 0.72;
+
+ // (distance, material)
+ vec2 hit = traceRay(origin, direction);
+ distance = hit.x;
+ float material = hit.y;
+
+ // We've hit something in the scene
+ if (material > 0.0) {
+ vec3 position = origin + distance * direction;
+
+ vec3 v = normalize(-direction);
+ vec3 n = normal(position);
+ vec3 l = normalize(vec3(0.6, 0.7, -0.7));
+ vec3 h = normalize(v + l);
+ vec3 r = normalize(reflect(direction, n));
+
+ float NoV = abs(dot(n, v)) + 1e-5;
+ float NoL = saturate(dot(n, l));
+ float NoH = saturate(dot(n, h));
+ float LoH = saturate(dot(l, h));
+
+ vec3 baseColor = vec3(0.0);
+ float roughness = 0.0;
+ float metallic = 0.0;
+
+ float intensity = 2.0;
+ float indirectIntensity = 0.64;
+
+ if (material < 4.0) {
+ // Checkerboard floor
+ float f = mod(floor(6.0 * position.z) + floor(6.0 * position.x), 2.0);
+ baseColor = 0.4 + f * vec3(0.6);
+ roughness = 0.1;
+ } else if (material < 16.0) {
+ // Metallic objects
+ baseColor = vec3(0.3, 0.0, 0.0);
+ roughness = 0.2;
+ }
+
+ float linearRoughness = roughness * roughness;
+ vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
+ vec3 f0 = 0.04 * (1.0 - metallic) + baseColor.rgb * metallic;
+
+ float attenuation = shadow(position, l, 0.02, 2.5);
+
+ // specular BRDF
+ float D = D_GGX(linearRoughness, NoH, h);
+ float V = V_SmithGGXCorrelated(linearRoughness, NoV, NoL);
+ vec3 F = F_Schlick(f0, LoH);
+ vec3 Fr = (D * V) * F;
+
+ // diffuse BRDF
+ vec3 Fd = diffuseColor * Fd_Burley(linearRoughness, NoV, NoL, LoH);
+
+ color = Fd + Fr;
+ color *= (intensity * attenuation * NoL) * vec3(0.98, 0.92, 0.89);
+
+ // diffuse indirect
+ vec3 indirectDiffuse = Irradiance_SphericalHarmonics(n) * Fd_Lambert();
+
+ vec2 indirectHit = traceRay(position, r);
+ vec3 indirectSpecular = vec3(0.65, 0.85, 1.0) + r.y * 0.72;
+ if (indirectHit.y > 0.0) {
+ if (indirectHit.y < 4.0) {
+ vec3 indirectPosition = position + indirectHit.x * r;
+ // Checkerboard floor
+ float f = mod(floor(6.0 * indirectPosition.z) + floor(6.0 * indirectPosition.x), 2.0);
+ indirectSpecular = 0.4 + f * vec3(0.6);
+ } else if (indirectHit.y < 16.0) {
+ // Metallic objects
+ indirectSpecular = vec3(0.3, 0.0, 0.0);
+ }
+ }
+
+ // indirect contribution
+ vec2 dfg = PrefilteredDFG_Karis(roughness, NoV);
+ vec3 specularColor = f0 * dfg.x + dfg.y;
+ vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;
+
+ color += ibl * indirectIntensity;
+ }
+
+ return color;
+}
+
+//------------------------------------------------------------------------------
+// Setup and execution
+//------------------------------------------------------------------------------
+
+mat3 setCamera(in vec3 origin, in vec3 target, float rotation) {
+ vec3 forward = normalize(target - origin);
+ vec3 orientation = vec3(sin(rotation), cos(rotation), 0.0);
+ vec3 left = normalize(cross(forward, orientation));
+ vec3 up = normalize(cross(left, forward));
+ return mat3(left, up, forward);
+}
+
+void main() {
+ // Normalized coordinates
+ vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
+ // Aspect ratio
+ p.x *= resolution.x / resolution.y;
+
+ // Camera position and "look at"
+ vec3 origin = vec3(0.0, 1.0, 0.0);
+ vec3 target = vec3(0.0);
+
+ origin.x += 2.0 * cos(time * 0.2);
+ origin.z += 2.0 * sin(time * 0.2);
+
+ mat3 toWorld = setCamera(origin, target, 0.0);
+ vec3 direction = toWorld * normalize(vec3(p.xy, 2.0));
+
+ // Render scene
+ float distance;
+ vec3 color = render(origin, direction, distance);
+
+ // Tone mapping
+ color = Tonemap_ACES(color);
+
+ // Exponential distance fog
+ color = mix(color, 0.8 * vec3(0.7, 0.8, 1.0), 1.0 - exp2(-0.011 * distance * distance));
+
+ // Gamma compression
+ color = OECF_sRGBFast(color);
+
+ fragColor = vec4(color, 1.0);
+}
+)SHADER__";
+
+static const android::vec3 SPHERICAL_HARMONICS[4] =
+ {{0.754554516862612, 0.748542953903366, 0.790921515418539},
+ {-0.083856548007422, 0.092533500963210, 0.322764661032516},
+ {0.308152705331738, 0.366796330467391, 0.466698181299906},
+ {-0.188884931542396, -0.277402551592231, -0.377844212327557}};
+
+static const android::vec4 TRIANGLE[3] = {{-1.0f, -1.0f, 1.0f, 1.0f},
+ {3.0f, -1.0f, 1.0f, 1.0f},
+ {-1.0f, 3.0f, 1.0f, 1.0f}};
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
new file mode 100644
index 0000000..b667a74
--- /dev/null
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -0,0 +1,365 @@
+#include <algorithm>
+#include <functional>
+#include <limits>
+#include <ostream>
+
+#include <gtest/gtest.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayInfo.h>
+#include <utils/String8.h>
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using ui::ColorMode;
+
+namespace {
+const String8 DISPLAY_NAME("Credentials Display Test");
+const String8 SURFACE_NAME("Test Surface Name");
+const uint32_t ROTATION = 0;
+const float FRAME_SCALE = 1.0f;
+} // namespace
+
+/**
+ * This class tests the CheckCredentials method in SurfaceFlinger.
+ * Methods like EnableVsyncInjections and InjectVsync are not tested since they do not
+ * return anything meaningful.
+ */
+class CredentialsTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ // Start the tests as root.
+ seteuid(AID_ROOT);
+
+ ASSERT_NO_FATAL_FAILURE(initClient());
+ }
+
+ void TearDown() override {
+ mComposerClient->dispose();
+ mBGSurfaceControl.clear();
+ mComposerClient.clear();
+ // Finish the tests as root.
+ seteuid(AID_ROOT);
+ }
+
+ sp<IBinder> mDisplay;
+ sp<IBinder> mVirtualDisplay;
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mBGSurfaceControl;
+ sp<SurfaceControl> mVirtualSurfaceControl;
+
+ void initClient() {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+ }
+
+ void setupBackgroundSurface() {
+ mDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(mDisplay == nullptr);
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
+ const ssize_t displayWidth = info.w;
+ const ssize_t displayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl =
+ mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+
+ Transaction t;
+ t.setDisplayLayerStack(mDisplay, 0);
+ ASSERT_EQ(NO_ERROR,
+ t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
+ }
+
+ void setupVirtualDisplay() {
+ mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ const ssize_t displayWidth = 100;
+ const ssize_t displayHeight = 100;
+
+ // Background surface
+ mVirtualSurfaceControl =
+ mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mVirtualSurfaceControl != nullptr);
+ ASSERT_TRUE(mVirtualSurfaceControl->isValid());
+
+ Transaction t;
+ t.setDisplayLayerStack(mVirtualDisplay, 0);
+ ASSERT_EQ(NO_ERROR,
+ t.setLayer(mVirtualSurfaceControl, INT_MAX - 3)
+ .show(mVirtualSurfaceControl)
+ .apply());
+ }
+
+ /**
+ * Sets UID to imitate Graphic's process.
+ */
+ void setGraphicsUID() {
+ seteuid(AID_ROOT);
+ seteuid(AID_GRAPHICS);
+ }
+
+ /**
+ * Sets UID to imitate System's process.
+ */
+ void setSystemUID() {
+ seteuid(AID_ROOT);
+ seteuid(AID_SYSTEM);
+ }
+
+ /**
+ * Sets UID to imitate a process that doesn't have any special privileges in
+ * our code.
+ */
+ void setBinUID() {
+ seteuid(AID_ROOT);
+ seteuid(AID_BIN);
+ }
+
+ /**
+ * Template function the check a condition for different types of users: root
+ * graphics, system, and non-supported user. Root, graphics, and system should
+ * always equal privilegedValue, and non-supported user should equal unprivilegedValue.
+ */
+ template <typename T>
+ void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) {
+ // Check with root.
+ seteuid(AID_ROOT);
+ ASSERT_EQ(privilegedValue, condition());
+
+ // Check as a Graphics user.
+ setGraphicsUID();
+ ASSERT_EQ(privilegedValue, condition());
+
+ // Check as a system user.
+ setSystemUID();
+ ASSERT_EQ(privilegedValue, condition());
+
+ // Check as a non-supported user.
+ setBinUID();
+ ASSERT_EQ(unprivilegedValue, condition());
+ }
+};
+
+TEST_F(CredentialsTest, ClientInitTest) {
+ // Root can init can init the client.
+ ASSERT_NO_FATAL_FAILURE(initClient());
+
+ // Graphics can init the client.
+ setGraphicsUID();
+ ASSERT_NO_FATAL_FAILURE(initClient());
+
+ // System can init the client.
+ setSystemUID();
+ ASSERT_NO_FATAL_FAILURE(initClient());
+
+ // Anyone else can init the client.
+ setBinUID();
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_NO_FATAL_FAILURE(initClient());
+}
+
+TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
+ std::function<bool()> condition = [] {
+ return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
+ };
+ // Anyone can access display information.
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+}
+
+TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
+ // The following methods are tested with a UID that is not root, graphics,
+ // or system, to show that anyone can access them.
+ setBinUID();
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_TRUE(display != nullptr);
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+
+ Vector<DisplayInfo> configs;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
+
+ ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
+ SurfaceComposerClient::getActiveColorMode(display));
+}
+
+TEST_F(CredentialsTest, GetDisplayColorModesTest) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ std::function<status_t()> condition = [=]() {
+ Vector<ui::ColorMode> outColorModes;
+ return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
+TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ std::function<status_t()> condition = [=]() {
+ ui::DisplayPrimaries primaries;
+ return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
+TEST_F(CredentialsTest, SetActiveConfigTest) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ std::function<status_t()> condition = [=]() {
+ return SurfaceComposerClient::setActiveConfig(display, 0);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+TEST_F(CredentialsTest, SetActiveColorModeTest) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ std::function<status_t()> condition = [=]() {
+ return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+TEST_F(CredentialsTest, CreateDisplayTest) {
+ std::function<bool()> condition = [=]() {
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ return testDisplay.get() != nullptr;
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+
+ condition = [=]() {
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
+ return testDisplay.get() != nullptr;
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+}
+
+TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) {
+ setupVirtualDisplay();
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ // This test currently fails. TODO(b/112002626): Find a way to properly create
+ // a display in the test environment, so that destroy display can remove it.
+ ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
+}
+
+TEST_F(CredentialsTest, CaptureTest) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ std::function<status_t()> condition = [=]() {
+ sp<GraphicBuffer> outBuffer;
+ return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
+ 0 /*reqHeight*/, false, ROTATION, &outBuffer);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+TEST_F(CredentialsTest, CaptureLayersTest) {
+ setupBackgroundSurface();
+ sp<GraphicBuffer> outBuffer;
+ std::function<status_t()> condition = [=]() {
+ sp<GraphicBuffer> outBuffer;
+ return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+ Rect(), FRAME_SCALE, &outBuffer);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
+}
+
+/**
+ * The following tests are for methods accessible directly through SurfaceFlinger.
+ */
+
+/**
+ * An app can pass a buffer queue to the media server and ask the media server to decode a DRM video
+ * to that buffer queue. The media server is the buffer producer in this case. Because the app may create
+ * its own buffer queue and act as the buffer consumer, the media server wants to be careful to avoid
+ * sending decoded video frames to the app. This is where authenticateSurfaceTexture call comes in, to check
+ * the consumer of a buffer queue is SurfaceFlinger.
+ */
+TEST_F(CredentialsTest, AuthenticateSurfaceTextureTest) {
+ setupBackgroundSurface();
+ sp<IGraphicBufferProducer> producer =
+ mBGSurfaceControl->getSurface()->getIGraphicBufferProducer();
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+ std::function<bool()> condition = [=]() { return sf->authenticateSurfaceTexture(producer); };
+ // Anyone should be able to check if the consumer of the buffer queue is SF.
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+}
+
+TEST_F(CredentialsTest, GetLayerDebugInfo) {
+ setupBackgroundSurface();
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+ // Historically, only root and shell can access the getLayerDebugInfo which
+ // is called when we call dumpsys. I don't see a reason why we should change this.
+ std::vector<LayerDebugInfo> outLayers;
+ // Check with root.
+ seteuid(AID_ROOT);
+ ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
+
+ // Check as a shell.
+ seteuid(AID_SHELL);
+ ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
+
+ // Check as anyone else.
+ seteuid(AID_ROOT);
+ seteuid(AID_BIN);
+ ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers));
+}
+
+TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+ bool result = false;
+ status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
+ ASSERT_EQ(NO_ERROR, error);
+ bool hasWideColorMode = false;
+ Vector<ColorMode> colorModes;
+ SurfaceComposerClient::getDisplayColorModes(display, &colorModes);
+ for (ColorMode colorMode : colorModes) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::ADOBE_RGB:
+ case ColorMode::DCI_P3:
+ hasWideColorMode = true;
+ break;
+ default:
+ break;
+ }
+ }
+ ASSERT_EQ(hasWideColorMode, result);
+}
+
+TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+ std::function<status_t()> condition = [=]() {
+ bool result = false;
+ return SurfaceComposerClient::isWideColorDisplay(display, &result);
+ };
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
+}
+
+TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+ ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(display);
+ ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index 4577153..ee857b0 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -34,7 +34,7 @@
auto surf = client->createSurface(String8("t"), 100, 100,
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(surf != nullptr);
- client->destroySurface(surf->getHandle());
+ surf.clear();
}
};
@@ -101,10 +101,7 @@
for (int i = 0; i < 100000; i++) {
surfaceflinger::LayersProto layersProto = generateLayerProto();
auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto);
- // Allow some layerTrees to just fall out of scope (instead of std::move)
- if (i % 2) {
- surfaceflinger::LayerProtoParser::layersToString(std::move(layerTree));
- }
+ surfaceflinger::LayerProtoParser::layerTreeToString(layerTree);
}
system(cmd.c_str());
}
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index cca84e5..be862c9 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "LayerTransactionTest.*:LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*:CropLatchingTest.*:GeometryLatchingTest.*:ScreenCaptureTest.*:DereferenceSurfaceControlTest.*:-CropLatchingTest.FinalCropLatchingBufferOldSize"
+ "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*"
}
}
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index de78c3f..5cc946a 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -42,13 +42,17 @@
constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
constexpr uint32_t SIZE_UPDATE = 134;
constexpr uint32_t STACK_UPDATE = 1;
-constexpr uint64_t DEFERRED_UPDATE = 13;
+constexpr uint64_t DEFERRED_UPDATE = 0;
constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float CORNER_RADIUS_UPDATE = 0.2f;
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);
const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
+constexpr auto TEST_SURFACE_NAME = "BG Interceptor Test Surface";
+constexpr auto UNIQUE_TEST_SURFACE_NAME = "BG Interceptor Test Surface#0";
constexpr auto LAYER_NAME = "Layer Create and Delete Test";
+constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
@@ -94,30 +98,21 @@
system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
}
-int32_t getSurfaceId(const std::string& surfaceName) {
- enableInterceptor();
- disableInterceptor();
- Trace capturedTrace;
- readProtoFile(&capturedTrace);
+int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
int32_t layerId = 0;
- for (const auto& increment : *capturedTrace.mutable_increment()) {
+ for (const auto& increment : capturedTrace.increment()) {
if (increment.increment_case() == increment.kSurfaceCreation) {
if (increment.surface_creation().name() == surfaceName) {
layerId = increment.surface_creation().id();
- break;
}
}
}
return layerId;
}
-int32_t getDisplayId(const std::string& displayName) {
- enableInterceptor();
- disableInterceptor();
- Trace capturedTrace;
- readProtoFile(&capturedTrace);
+int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
int32_t displayId = 0;
- for (const auto& increment : *capturedTrace.mutable_increment()) {
+ for (const auto& increment : capturedTrace.increment()) {
if (increment.increment_case() == increment.kDisplayCreation) {
if (increment.display_creation().name() == displayName) {
displayId = increment.display_creation().id();
@@ -130,36 +125,15 @@
class SurfaceInterceptorTest : public ::testing::Test {
protected:
- virtual void SetUp() {
+ void SetUp() override {
// Allow SurfaceInterceptor write to /data
system("setenforce 0");
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
- ISurfaceComposer::eDisplayIdMain));
- DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
- ssize_t displayWidth = info.w;
- ssize_t displayHeight = info.h;
-
- // Background surface
- mBGSurfaceControl = mComposerClient->createSurface(
- String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mBGSurfaceControl != nullptr);
- ASSERT_TRUE(mBGSurfaceControl->isValid());
- mBGLayerId = getSurfaceId("BG Interceptor Test Surface");
-
- Transaction t;
- t.setDisplayLayerStack(display, 0);
- ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
- .show(mBGSurfaceControl)
- .apply());
}
- virtual void TearDown() {
+ void TearDown() override {
mComposerClient->dispose();
mBGSurfaceControl.clear();
mComposerClient.clear();
@@ -168,18 +142,25 @@
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mBGSurfaceControl;
int32_t mBGLayerId;
- // Used to verify creation and destruction of surfaces and displays
- int32_t mTargetId;
public:
- void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
- bool (SurfaceInterceptorTest::* verification)(Trace *));
- void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
- SurfaceChange::SurfaceChangeCase changeCase);
- void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
- Increment::IncrementCase incrementCase);
- void runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&),
- bool intercepted = false);
+ using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
+ using TestAction = void (SurfaceInterceptorTest::*)();
+ using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
+ using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);
+
+ void setupBackgroundSurface();
+ void preProcessTrace(const Trace& trace);
+
+ // captureTest will enable SurfaceInterceptor, setup background surface,
+ // disable SurfaceInterceptor, collect the trace and process the trace for
+ // id of background surface before further verification.
+ void captureTest(TestTransactionAction action, TestBooleanVerification verification);
+ void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
+ void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
+ void captureTest(TestAction action, TestBooleanVerification verification);
+ void captureTest(TestAction action, TestVerification verification);
+ void runInTransaction(TestTransactionAction action);
// Verification of changes to a surface
bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
@@ -187,7 +168,7 @@
bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
- bool finalCropUpdateFound(const SurfaceChange& change, bool foundFinalCrop);
+ bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -196,18 +177,22 @@
bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
- bool surfaceUpdateFound(Trace* trace, SurfaceChange::SurfaceChangeCase changeCase);
- void assertAllUpdatesFound(Trace* trace);
+ bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
+
+ // Find all of the updates in the single trace
+ void assertAllUpdatesFound(const Trace& trace);
// Verification of creation and deletion of a surface
bool surfaceCreationFound(const Increment& increment, bool foundSurface);
- bool surfaceDeletionFound(const Increment& increment, bool foundSurface);
+ bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
+ bool foundSurface);
bool displayCreationFound(const Increment& increment, bool foundDisplay);
- bool displayDeletionFound(const Increment& increment, bool foundDisplay);
- bool singleIncrementFound(Trace* trace, Increment::IncrementCase incrementCase);
+ bool displayDeletionFound(const Increment& increment, const int32_t targetId,
+ bool foundDisplay);
+ bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);
// Verification of buffer updates
- bool bufferUpdatesFound(Trace* trace);
+ bool bufferUpdatesFound(const Trace& trace);
// Perform each of the possible changes to a surface
void positionUpdate(Transaction&);
@@ -215,7 +200,7 @@
void alphaUpdate(Transaction&);
void layerUpdate(Transaction&);
void cropUpdate(Transaction&);
- void finalCropUpdate(Transaction&);
+ void cornerRadiusUpdate(Transaction&);
void matrixUpdate(Transaction&);
void overrideScalingModeUpdate(Transaction&);
void transparentRegionHintUpdate(Transaction&);
@@ -230,48 +215,95 @@
void nBufferUpdates();
void runAllUpdates();
+
+private:
+ void captureInTransaction(TestTransactionAction action, Trace*);
+ void capture(TestAction action, Trace*);
};
-void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
- bool (SurfaceInterceptorTest::* verification)(Trace *))
-{
- runInTransaction(action, true);
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- ASSERT_TRUE((this->*verification)(&capturedTrace));
+void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
+ enableInterceptor();
+ setupBackgroundSurface();
+ runInTransaction(action);
+ disableInterceptor();
+ ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
+ preProcessTrace(*outTrace);
}
-void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
- Increment::IncrementCase incrementCase)
-{
- runInTransaction(action, true);
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase));
+void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
+ enableInterceptor();
+ setupBackgroundSurface();
+ (this->*action)();
+ disableInterceptor();
+ ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
+ preProcessTrace(*outTrace);
}
-void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&),
- SurfaceChange::SurfaceChangeCase changeCase)
-{
- runInTransaction(action, true);
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase));
+void SurfaceInterceptorTest::setupBackgroundSurface() {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = mComposerClient->createSurface(
+ String8(TEST_SURFACE_NAME), displayWidth, displayHeight,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+
+ Transaction t;
+ t.setDisplayLayerStack(display, 0);
+ ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
+ .show(mBGSurfaceControl)
+ .apply());
}
-void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&),
- bool intercepted)
-{
- if (intercepted) {
- enableInterceptor();
- }
+void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
+ mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME);
+}
+
+void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
+ TestBooleanVerification verification) {
+ Trace capturedTrace;
+ captureInTransaction(action, &capturedTrace);
+ ASSERT_TRUE((this->*verification)(capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
+ Increment::IncrementCase incrementCase) {
+ Trace capturedTrace;
+ captureInTransaction(action, &capturedTrace);
+ ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
+}
+
+void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
+ SurfaceChange::SurfaceChangeCase changeCase) {
+ Trace capturedTrace;
+ captureInTransaction(action, &capturedTrace);
+ ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
+}
+
+void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
+ Trace capturedTrace;
+ capture(action, &capturedTrace);
+ ASSERT_TRUE((this->*verification)(capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
+ Trace capturedTrace;
+ capture(action, &capturedTrace);
+ (this->*verification)(capturedTrace);
+}
+
+void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
Transaction t;
(this->*action)(t);
t.apply(true);
-
- if (intercepted) {
- disableInterceptor();
- }
}
void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
@@ -286,16 +318,16 @@
t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
}
+void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
+ t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
+}
+
void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
}
void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
- t.setCrop(mBGSurfaceControl, CROP_UPDATE);
-}
-
-void SurfaceInterceptorTest::finalCropUpdate(Transaction& t) {
- t.setFinalCrop(mBGSurfaceControl, CROP_UPDATE);
+ t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
}
void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
@@ -328,7 +360,8 @@
}
void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
- t.deferTransactionUntil(mBGSurfaceControl, mBGSurfaceControl->getHandle(), DEFERRED_UPDATE);
+ t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(),
+ DEFERRED_UPDATE);
}
void SurfaceInterceptorTest::displayCreation(Transaction&) {
@@ -338,7 +371,6 @@
void SurfaceInterceptorTest::displayDeletion(Transaction&) {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
- mTargetId = getDisplayId(DISPLAY_NAME.string());
SurfaceComposerClient::destroyDisplay(testDisplay);
}
@@ -346,9 +378,9 @@
runInTransaction(&SurfaceInterceptorTest::positionUpdate);
runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+ runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
runInTransaction(&SurfaceInterceptorTest::layerUpdate);
runInTransaction(&SurfaceInterceptorTest::cropUpdate);
- runInTransaction(&SurfaceInterceptorTest::finalCropUpdate);
runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
@@ -380,9 +412,8 @@
bool hasY(change.position().y() == POSITION_UPDATE);
if (hasX && hasY && !foundPosition) {
foundPosition = true;
- }
- // Failed because the position update was found a second time
- else if (hasX && hasY && foundPosition) {
+ } else if (hasX && hasY && foundPosition) {
+ // Failed because the position update was found a second time
[] () { FAIL(); }();
}
return foundPosition;
@@ -393,8 +424,7 @@
bool hasHeight(change.size().w() == SIZE_UPDATE);
if (hasWidth && hasHeight && !foundSize) {
foundSize = true;
- }
- else if (hasWidth && hasHeight && foundSize) {
+ } else if (hasWidth && hasHeight && foundSize) {
[] () { FAIL(); }();
}
return foundSize;
@@ -404,19 +434,28 @@
bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
if (hasAlpha && !foundAlpha) {
foundAlpha = true;
- }
- else if (hasAlpha && foundAlpha) {
+ } else if (hasAlpha && foundAlpha) {
[] () { FAIL(); }();
}
return foundAlpha;
}
+bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
+ bool foundCornerRadius) {
+ bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
+ if (hasCornerRadius && !foundCornerRadius) {
+ foundCornerRadius = true;
+ } else if (hasCornerRadius && foundCornerRadius) {
+ [] () { FAIL(); }();
+ }
+ return foundCornerRadius;
+}
+
bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
bool hasLayer(change.layer().layer() == LAYER_UPDATE);
if (hasLayer && !foundLayer) {
foundLayer = true;
- }
- else if (hasLayer && foundLayer) {
+ } else if (hasLayer && foundLayer) {
[] () { FAIL(); }();
}
return foundLayer;
@@ -429,59 +468,38 @@
bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
foundCrop = true;
- }
- else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
+ } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
[] () { FAIL(); }();
}
return foundCrop;
}
-bool SurfaceInterceptorTest::finalCropUpdateFound(const SurfaceChange& change,
- bool foundFinalCrop)
-{
- bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left);
- bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top);
- bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right);
- bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom);
- if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) {
- foundFinalCrop = true;
- }
- else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) {
- [] () { FAIL(); }();
- }
- return foundFinalCrop;
-}
-
bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
- bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2);
- bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2);
+ bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
+ bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
foundMatrix = true;
- }
- else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
+ } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
[] () { FAIL(); }();
}
return foundMatrix;
}
bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
- bool foundScalingMode)
-{
+ bool foundScalingMode) {
bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
if (hasScalingUpdate && !foundScalingMode) {
foundScalingMode = true;
- }
- else if (hasScalingUpdate && foundScalingMode) {
+ } else if (hasScalingUpdate && foundScalingMode) {
[] () { FAIL(); }();
}
return foundScalingMode;
}
bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
- bool foundTransparentRegion)
-{
+ bool foundTransparentRegion) {
auto traceRegion = change.transparent_region_hint().region(0);
bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
bool hasTop(traceRegion.top() == CROP_UPDATE.top);
@@ -489,84 +507,72 @@
bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
foundTransparentRegion = true;
- }
- else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
+ } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
[] () { FAIL(); }();
}
return foundTransparentRegion;
}
bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
- bool foundLayerStack)
-{
+ bool foundLayerStack) {
bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
if (hasLayerStackUpdate && !foundLayerStack) {
foundLayerStack = true;
- }
- else if (hasLayerStackUpdate && foundLayerStack) {
+ } else if (hasLayerStackUpdate && foundLayerStack) {
[] () { FAIL(); }();
}
return foundLayerStack;
}
bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
- bool foundHiddenFlag)
-{
+ bool foundHiddenFlag) {
bool hasHiddenFlag(change.hidden_flag().hidden_flag());
if (hasHiddenFlag && !foundHiddenFlag) {
foundHiddenFlag = true;
- }
- else if (hasHiddenFlag && foundHiddenFlag) {
+ } else if (hasHiddenFlag && foundHiddenFlag) {
[] () { FAIL(); }();
}
return foundHiddenFlag;
}
bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
- bool foundOpaqueFlag)
-{
+ bool foundOpaqueFlag) {
bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
if (hasOpaqueFlag && !foundOpaqueFlag) {
foundOpaqueFlag = true;
- }
- else if (hasOpaqueFlag && foundOpaqueFlag) {
+ } else if (hasOpaqueFlag && foundOpaqueFlag) {
[] () { FAIL(); }();
}
return foundOpaqueFlag;
}
bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
- bool foundSecureFlag)
-{
+ bool foundSecureFlag) {
bool hasSecureFlag(change.secure_flag().secure_flag());
if (hasSecureFlag && !foundSecureFlag) {
foundSecureFlag = true;
- }
- else if (hasSecureFlag && foundSecureFlag) {
+ } else if (hasSecureFlag && foundSecureFlag) {
[] () { FAIL(); }();
}
return foundSecureFlag;
}
bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
- bool foundDeferred)
-{
+ bool foundDeferred) {
bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
if (hasId && hasFrameNumber && !foundDeferred) {
foundDeferred = true;
- }
- else if (hasId && hasFrameNumber && foundDeferred) {
+ } else if (hasId && hasFrameNumber && foundDeferred) {
[] () { FAIL(); }();
}
return foundDeferred;
}
-bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace,
- SurfaceChange::SurfaceChangeCase changeCase)
-{
+bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
+ SurfaceChange::SurfaceChangeCase changeCase) {
bool foundUpdate = false;
- for (const auto& increment : *trace->mutable_increment()) {
+ for (const auto& increment : trace.increment()) {
if (increment.increment_case() == increment.kTransaction) {
for (const auto& change : increment.transaction().surface_change()) {
if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
@@ -587,8 +593,8 @@
case SurfaceChange::SurfaceChangeCase::kCrop:
foundUpdate = cropUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kFinalCrop:
- foundUpdate = finalCropUpdateFound(change, foundUpdate);
+ case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+ foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
foundUpdate = matrixUpdateFound(change, foundUpdate);
@@ -624,13 +630,12 @@
return foundUpdate;
}
-void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) {
+void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kFinalCrop));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
@@ -642,24 +647,23 @@
}
bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
- bool isMatch(increment.surface_creation().name() == LAYER_NAME &&
+ bool isMatch(increment.surface_creation().name() == UNIQUE_LAYER_NAME &&
increment.surface_creation().w() == SIZE_UPDATE &&
increment.surface_creation().h() == SIZE_UPDATE);
if (isMatch && !foundSurface) {
foundSurface = true;
- }
- else if (isMatch && foundSurface) {
+ } else if (isMatch && foundSurface) {
[] () { FAIL(); }();
}
return foundSurface;
}
-bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool foundSurface) {
- bool isMatch(increment.surface_deletion().id() == mTargetId);
+bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
+ const int32_t targetId, bool foundSurface) {
+ bool isMatch(increment.surface_deletion().id() == targetId);
if (isMatch && !foundSurface) {
foundSurface = true;
- }
- else if (isMatch && foundSurface) {
+ } else if (isMatch && foundSurface) {
[] () { FAIL(); }();
}
return foundSurface;
@@ -670,42 +674,45 @@
increment.display_creation().is_secure());
if (isMatch && !foundDisplay) {
foundDisplay = true;
- }
- else if (isMatch && foundDisplay) {
+ } else if (isMatch && foundDisplay) {
[] () { FAIL(); }();
}
return foundDisplay;
}
-bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, bool foundDisplay) {
- bool isMatch(increment.display_deletion().id() == mTargetId);
+bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
+ const int32_t targetId, bool foundDisplay) {
+ bool isMatch(increment.display_deletion().id() == targetId);
if (isMatch && !foundDisplay) {
foundDisplay = true;
- }
- else if (isMatch && foundDisplay) {
+ } else if (isMatch && foundDisplay) {
[] () { FAIL(); }();
}
return foundDisplay;
}
-bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace,
- Increment::IncrementCase incrementCase)
-{
+bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
+ Increment::IncrementCase incrementCase) {
bool foundIncrement = false;
- for (const auto& increment : *trace->mutable_increment()) {
+ for (const auto& increment : trace.increment()) {
if (increment.increment_case() == incrementCase) {
+ int32_t targetId = 0;
switch (incrementCase) {
case Increment::IncrementCase::kSurfaceCreation:
foundIncrement = surfaceCreationFound(increment, foundIncrement);
break;
case Increment::IncrementCase::kSurfaceDeletion:
- foundIncrement = surfaceDeletionFound(increment, foundIncrement);
+ // Find the id of created surface.
+ targetId = getSurfaceId(trace, UNIQUE_LAYER_NAME);
+ foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
break;
case Increment::IncrementCase::kDisplayCreation:
foundIncrement = displayCreationFound(increment, foundIncrement);
break;
case Increment::IncrementCase::kDisplayDeletion:
- foundIncrement = displayDeletionFound(increment, foundIncrement);
+ // Find the id of created display.
+ targetId = getDisplayId(trace, DISPLAY_NAME.string());
+ foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
break;
default:
/* code */
@@ -716,9 +723,9 @@
return foundIncrement;
}
-bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) {
+bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
uint32_t updates = 0;
- for (const auto& inc : *trace->mutable_increment()) {
+ for (const auto& inc : trace.increment()) {
if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
updates++;
}
@@ -747,9 +754,9 @@
captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
}
-TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::finalCropUpdate,
- SurfaceChange::SurfaceChangeCase::kFinalCrop);
+TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
+ SurfaceChange::SurfaceChangeCase::kCornerRadius);
}
TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
@@ -792,14 +799,8 @@
}
TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
- enableInterceptor();
- runAllUpdates();
- disableInterceptor();
-
- // Find all of the updates in the single trace
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- assertAllUpdatesFound(&capturedTrace);
+ captureTest(&SurfaceInterceptorTest::runAllUpdates,
+ &SurfaceInterceptorTest::assertAllUpdatesFound);
}
TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
@@ -807,40 +808,30 @@
Increment::IncrementCase::kSurfaceCreation);
}
-TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) {
- sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME),
- SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0);
- this->mTargetId = getSurfaceId(LAYER_NAME);
- enableInterceptor();
- mComposerClient->destroySurface(layerToDelete->getHandle());
- disableInterceptor();
-
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceDeletion));
-}
-
TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
captureTest(&SurfaceInterceptorTest::displayCreation,
Increment::IncrementCase::kDisplayCreation);
}
TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
- captureTest(&SurfaceInterceptorTest::displayDeletion,
- Increment::IncrementCase::kDisplayDeletion);
+ enableInterceptor();
+ runInTransaction(&SurfaceInterceptorTest::displayDeletion);
+ disableInterceptor();
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
}
TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
- nBufferUpdates();
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
+ captureTest(&SurfaceInterceptorTest::nBufferUpdates,
+ &SurfaceInterceptorTest::bufferUpdatesFound);
}
// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
// first create a snapshot of the existing displays and surfaces and then start capturing
// the buffer updates
TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
+ setupBackgroundSurface();
std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
enableInterceptor();
disableInterceptor();
@@ -854,6 +845,7 @@
TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
enableInterceptor();
+ setupBackgroundSurface();
std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
@@ -863,10 +855,11 @@
Trace capturedTrace;
ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ preProcessTrace(capturedTrace);
- assertAllUpdatesFound(&capturedTrace);
- ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
- ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceCreation));
+ assertAllUpdatesFound(capturedTrace);
+ ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
+ ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
}
}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 79f27df..d62afa5 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -15,27 +15,38 @@
*/
#include <algorithm>
+#include <chrono>
+#include <cinttypes>
#include <functional>
#include <limits>
#include <ostream>
+#include <thread>
#include <gtest/gtest.h>
#include <android/native_window.h>
+#include <binder/ProcessState.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
-
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <hardware/hwcomposer_defs.h>
+#include <private/android_filesystem_config.h>
#include <private/gui/ComposerService.h>
+#include <ui/ColorSpace.h>
#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
#include <utils/String8.h>
#include <math.h>
#include <math/vec3.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "BufferGenerator.h"
namespace android {
@@ -62,38 +73,29 @@
const Color Color::BLACK{0, 0, 0, 255};
const Color Color::TRANSPARENT{0, 0, 0, 0};
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using namespace std::chrono_literals;
+
std::ostream& operator<<(std::ostream& os, const Color& color) {
os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
return os;
}
// Fill a region with the specified color.
-void fillBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const Color& color) {
- int32_t x = rect.left;
- int32_t y = rect.top;
- int32_t width = rect.right - rect.left;
- int32_t height = rect.bottom - rect.top;
-
- if (x < 0) {
- width += x;
- x = 0;
- }
- if (y < 0) {
- height += y;
- y = 0;
- }
- if (x + width > buffer.width) {
- x = std::min(x, buffer.width);
- width = buffer.width - x;
- }
- if (y + height > buffer.height) {
- y = std::min(y, buffer.height);
- height = buffer.height - y;
+void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect,
+ const Color& color) {
+ Rect r(0, 0, buffer.width, buffer.height);
+ if (!r.intersect(rect, &r)) {
+ return;
}
- for (int32_t j = 0; j < height; j++) {
- uint8_t* dst = static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (y + j) + x) * 4;
- for (int32_t i = 0; i < width; i++) {
+ int32_t width = r.right - r.left;
+ int32_t height = r.bottom - r.top;
+
+ for (int32_t row = 0; row < height; row++) {
+ uint8_t* dst =
+ static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4;
+ for (int32_t column = 0; column < width; column++) {
dst[0] = color.r;
dst[1] = color.g;
dst[2] = color.b;
@@ -103,6 +105,33 @@
}
}
+// Fill a region with the specified color.
+void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) {
+ Rect r(0, 0, buffer->width, buffer->height);
+ if (!r.intersect(rect, &r)) {
+ return;
+ }
+
+ int32_t width = r.right - r.left;
+ int32_t height = r.bottom - r.top;
+
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ for (int32_t row = 0; row < height; row++) {
+ uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4;
+ for (int32_t column = 0; column < width; column++) {
+ dst[0] = color.r;
+ dst[1] = color.g;
+ dst[2] = color.b;
+ dst[3] = color.a;
+ dst += 4;
+ }
+ }
+ buffer->unlock();
+}
+
// Check if a region has the specified color.
void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, const Rect& rect,
const Color& color, uint8_t tolerance) {
@@ -169,17 +198,17 @@
// individual pixel values for testing purposes.
class ScreenCapture : public RefBase {
public:
- static void captureScreen(sp<ScreenCapture>* sc, int32_t minLayerZ = 0,
- int32_t maxLayerZ = std::numeric_limits<int32_t>::max()) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+ captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+ }
+
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+ const auto sf = ComposerService::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR,
- sf->captureScreen(display, &outBuffer, Rect(), 0, 0, minLayerZ, maxLayerZ,
- false));
- *sc = new ScreenCapture(outBuffer);
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
+ *sc = std::make_unique<ScreenCapture>(outBuffer);
}
static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
@@ -299,18 +328,27 @@
ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
}
- sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
- uint32_t flags = 0) {
- auto layer =
- mClient->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
- EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+ virtual void TearDown() {
+ mBlackBgSurface = 0;
+ mClient->dispose();
+ mClient = 0;
+ }
- status_t error = Transaction()
- .setLayerStack(layer, mDisplayLayerStack)
- .setLayer(layer, mLayerZBase)
- .apply();
+ virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+ const char* name, uint32_t width, uint32_t height,
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ auto layer =
+ createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent);
+
+ Transaction t;
+ t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
+
+ status_t error = t.apply();
if (error != NO_ERROR) {
ADD_FAILURE() << "failed to initialize SurfaceControl";
layer.clear();
@@ -319,7 +357,21 @@
return layer;
}
- ANativeWindow_Buffer getLayerBuffer(const sp<SurfaceControl>& layer) {
+ virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
+ const char* name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t flags,
+ SurfaceControl* parent = nullptr) {
+ auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+ EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+ return layer;
+ }
+
+ virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ return createLayer(mClient, name, width, height, flags, parent);
+ }
+
+ ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
// wait for previous transactions (such as setSize) to complete
Transaction().apply(true);
@@ -329,63 +381,148 @@
return buffer;
}
- void postLayerBuffer(const sp<SurfaceControl>& layer) {
+ void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
// wait for the newly posted buffer to be latched
waitForLayerBuffers();
}
- void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color) {
+ virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+ int32_t bufferWidth, int32_t bufferHeight) {
ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
- fillBufferColor(buffer, Rect(0, 0, buffer.width, buffer.height), color);
- postLayerBuffer(layer);
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
+ postBufferQueueLayerBuffer(layer);
}
- void fillLayerQuadrant(const sp<SurfaceControl>& layer, const Color& topLeft,
+ virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+ int32_t bufferWidth, int32_t bufferHeight) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
+ Transaction().setBuffer(layer, buffer).apply();
+ }
+
+ void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
+ int32_t bufferWidth, int32_t bufferHeight) {
+ switch (mLayerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+ break;
+ default:
+ ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+ }
+ }
+
+ void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
+ int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
const Color& topRight, const Color& bottomLeft,
const Color& bottomRight) {
- ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
- ASSERT_TRUE(buffer.width % 2 == 0 && buffer.height % 2 == 0);
-
- const int32_t halfW = buffer.width / 2;
- const int32_t halfH = buffer.height / 2;
- fillBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
- fillBufferColor(buffer, Rect(halfW, 0, buffer.width, halfH), topRight);
- fillBufferColor(buffer, Rect(0, halfH, halfW, buffer.height), bottomLeft);
- fillBufferColor(buffer, Rect(halfW, halfH, buffer.width, buffer.height), bottomRight);
-
- postLayerBuffer(layer);
+ switch (mLayerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+ bottomLeft, bottomRight);
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+ bottomLeft, bottomRight);
+ break;
+ default:
+ ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+ }
}
- sp<ScreenCapture> screenshot() {
- sp<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot, mLayerZBase);
+ virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+ int32_t bufferHeight, const Color& topLeft,
+ const Color& topRight, const Color& bottomLeft,
+ const Color& bottomRight) {
+ ANativeWindow_Buffer buffer;
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+ const int32_t halfW = bufferWidth / 2;
+ const int32_t halfH = bufferHeight / 2;
+ fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+ fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight);
+ fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft);
+ fillANativeWindowBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight),
+ bottomRight);
+
+ postBufferQueueLayerBuffer(layer);
+ }
+
+ virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+ int32_t bufferHeight, const Color& topLeft,
+ const Color& topRight, const Color& bottomLeft,
+ const Color& bottomRight) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+ const int32_t halfW = bufferWidth / 2;
+ const int32_t halfH = bufferHeight / 2;
+ fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+ fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight);
+ fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft);
+ fillGraphicBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), bottomRight);
+
+ Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
+ }
+
+ std::unique_ptr<ScreenCapture> screenshot() {
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
return screenshot;
}
+ void asTransaction(const std::function<void(Transaction&)>& exec) {
+ Transaction t;
+ exec(t);
+ t.apply(true);
+ }
+
+ static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ static BufferGenerator bufferGenerator;
+ return bufferGenerator.get(outBuffer, outFence);
+ }
+
sp<SurfaceComposerClient> mClient;
sp<IBinder> mDisplay;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
uint32_t mDisplayLayerStack;
+ Rect mDisplayRect = Rect::INVALID_RECT;
// leave room for ~256 layers
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
+ sp<SurfaceControl> mBlackBgSurface;
+ bool mColorManagementUsed;
+
private:
void SetUpDisplay() {
- mDisplay = mClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
- ASSERT_NE(nullptr, mDisplay.get()) << "failed to get built-in display";
+ mDisplay = mClient->getInternalDisplayToken();
+ ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
// get display width/height
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(mDisplay, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
mDisplayWidth = info.w;
mDisplayHeight = info.h;
+ mDisplayRect =
+ Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
@@ -393,42 +530,211 @@
mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
mDisplayLayerStack = 0;
+
+ mBlackBgSurface =
+ createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+
// set layer stack (b/68888219)
Transaction t;
t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
+ t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+ t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
+ t.setColor(mBlackBgSurface, half3{0, 0, 0});
+ t.setLayer(mBlackBgSurface, mLayerZBase);
t.apply();
}
- void waitForLayerBuffers() { usleep(mBufferPostDelay); }
+ void waitForLayerBuffers() {
+ // Request an empty transaction to get applied synchronously to ensure the buffer is
+ // latched.
+ Transaction().apply(true);
+ usleep(mBufferPostDelay);
+ }
int32_t mBufferPostDelay;
+
+ friend class LayerRenderPathTestHarness;
+};
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+class LayerRenderPathTestHarness {
+public:
+ LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
+ : mDelegate(delegate), mRenderPath(renderPath) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() {
+ switch (mRenderPath) {
+ case RenderPath::SCREENSHOT:
+ return mDelegate->screenshot();
+ case RenderPath::VIRTUAL_DISPLAY:
+
+ const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ DisplayInfo mainDisplayInfo;
+ SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+
+ sp<IBinder> vDisplay;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ sp<BufferItemConsumer> itemConsumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+
+ itemConsumer = new BufferItemConsumer(consumer,
+ // Sample usage bits from screenrecord
+ GRALLOC_USAGE_HW_VIDEO_ENCODER |
+ GRALLOC_USAGE_SW_READ_OFTEN);
+
+ vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
+ false /*secure*/);
+
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(vDisplay, producer);
+ t.setDisplayLayerStack(vDisplay, 0);
+ t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
+ Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
+ Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+ t.apply();
+ SurfaceComposerClient::Transaction().apply(true);
+ BufferItem item;
+ itemConsumer->acquireBuffer(&item, 0, true);
+ auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
+ itemConsumer->releaseBuffer(item);
+ SurfaceComposerClient::destroyDisplay(vDisplay);
+ return sc;
+ }
+ }
+
+protected:
+ LayerTransactionTest* mDelegate;
+ RenderPath mRenderPath;
};
-TEST_F(LayerTransactionTest, SetPositionBasic) {
+class LayerTypeTransactionHarness : public LayerTransactionTest {
+public:
+ LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
+
+ sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+ uint32_t flags = 0, SurfaceControl* parent = nullptr) {
+ // if the flags already have a layer type specified, return an error
+ if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+ return nullptr;
+ }
+ return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent);
+ }
+
+ void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
+ int32_t bufferHeight) {
+ ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
+ bufferWidth, bufferHeight));
+ }
+
+ void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+ int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+ const Color& bottomLeft, const Color& bottomRight) {
+ ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
+ bufferWidth, bufferHeight,
+ topLeft, topRight,
+ bottomLeft, bottomRight));
+ }
+
+protected:
+ uint32_t mLayerType;
+};
+
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+ public ::testing::WithParamInterface<uint32_t> {
+public:
+ LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+class LayerTypeAndRenderTypeTransactionTest
+ : public LayerTypeTransactionHarness,
+ public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
+public:
+ LayerTypeAndRenderTypeTransactionTest()
+ : LayerTypeTransactionHarness(std::get<0>(GetParam())),
+ mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() {
+ return mRenderPathHarness.getScreenCapture();
+ }
+
+protected:
+ LayerRenderPathTestHarness mRenderPathHarness;
+};
+
+// Environment for starting up binder threads. This is required for testing
+// virtual displays, as BufferQueue parameters may be queried over binder.
+class BinderEnvironment : public ::testing::Environment {
+public:
+ void SetUp() override { ProcessState::self()->startThreadPool(); }
+};
+
+::testing::Environment* const binderEnv =
+ ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerRenderTypeTransactionTest : public LayerTransactionTest,
+ public ::testing::WithParamInterface<RenderPath> {
+public:
+ LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
+
+ std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
+ void setRelativeZBasicHelper(uint32_t layerType);
+ void setRelativeZGroupHelper(uint32_t layerType);
+ void setAlphaBasicHelper(uint32_t layerType);
+ void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
+ Color finalColor);
+
+protected:
+ LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(
+ LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
+ ::testing::Combine(
+ ::testing::Values(
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
+ ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+ ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
+INSTANTIATE_TEST_CASE_P(
+ LayerTypeTransactionTests, LayerTypeTransactionTest,
+ ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+ static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
{
SCOPED_TRACE("default position");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ const Rect rect(0, 0, 32, 32);
+ auto shot = getScreenCapture();
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
Transaction().setPosition(layer, 5, 10).apply();
{
SCOPED_TRACE("new position");
- auto shot = screenshot();
- shot->expectColor(Rect(5, 10, 37, 42), Color::RED);
- shot->expectBorder(Rect(5, 10, 37, 42), Color::BLACK);
+ const Rect rect(5, 10, 37, 42);
+ auto shot = getScreenCapture();
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetPositionRounding) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// GLES requires only 4 bits of subpixel precision during rasterization
// XXX GLES composition does not match HWC composition due to precision
@@ -437,113 +743,114 @@
Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
{
SCOPED_TRACE("rounding down");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
{
SCOPED_TRACE("rounding up");
- screenshot()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+ getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
Transaction().setPosition(layer, -32, -32).apply();
{
SCOPED_TRACE("negative coordinates");
- screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
{
SCOPED_TRACE("positive coordinates");
- screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// partially out of bounds
Transaction().setPosition(layer, -30, -30).apply();
{
SCOPED_TRACE("negative coordinates");
- screenshot()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
}
Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
{
SCOPED_TRACE("positive coordinates");
- screenshot()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
- mDisplayHeight),
- Color::RED);
+ getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+ mDisplayHeight),
+ Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// setPosition is applied immediately by default, with or without resize
// pending
Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
- shot->expectColor(Rect(5, 10, 37, 42), Color::RED);
- shot->expectBorder(Rect(5, 10, 37, 42), Color::BLACK);
+ auto shot = getScreenCapture();
+ const Rect rect(5, 10, 37, 42);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("resize applied");
- screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+ getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionWithNextResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// request setPosition to be applied with the next resize
Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply();
{
SCOPED_TRACE("new position pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setPosition(layer, 15, 20).apply();
{
SCOPED_TRACE("pending new position modified");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
// finally resize and latch the buffer
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("new position applied");
- screenshot()->expectColor(Rect(15, 20, 79, 84), Color::RED);
+ getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// setPosition is not immediate even with SCALE_TO_WINDOW override
Transaction()
@@ -554,116 +861,137 @@
.apply();
{
SCOPED_TRACE("new position pending");
- screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
}
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("new position applied");
- screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+ getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetSizeBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
Transaction().setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ auto shot = getScreenCapture();
+ const Rect rect(0, 0, 32, 32);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("resize applied");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
- shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ auto shot = getScreenCapture();
+ const Rect rect(0, 0, 64, 64);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetSizeInvalid) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
// cannot test robustness against invalid sizes (zero or really huge)
}
-TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
Transaction()
.setSize(layer, 64, 64)
.setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
.apply();
- screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
}
-TEST_F(LayerTransactionTest, SetZBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
Transaction().setLayer(layerR, mLayerZBase + 1).apply();
{
SCOPED_TRACE("layerR");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setLayer(layerG, mLayerZBase + 2).apply();
{
SCOPED_TRACE("layerG");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
}
}
-TEST_F(LayerTransactionTest, SetZNegative) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+ Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+ Transaction()
+ .reparent(layerR, parent->getHandle())
+ .reparent(layerG, parent->getHandle())
+ .apply();
Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
{
SCOPED_TRACE("layerR");
- sp<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot, -2, -1);
- screenshot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setLayer(layerR, -3).apply();
{
SCOPED_TRACE("layerG");
- sp<ScreenCapture> screenshot;
- ScreenCapture::captureScreen(&screenshot, -3, -1);
- screenshot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
}
}
-TEST_F(LayerTransactionTest, SetRelativeZBasic) {
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ Transaction()
+ .setPosition(layerG, 16, 16)
+ .setRelativeLayer(layerG, layerR->getHandle(), 1)
+ .apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ Transaction()
+ .setFrame(layerR, Rect(0, 0, 32, 32))
+ .setFrame(layerG, Rect(16, 16, 48, 48))
+ .setRelativeLayer(layerG, layerR->getHandle(), 1)
+ .apply();
+ break;
+ default:
+ ASSERT_FALSE(true) << "Unsupported layer type";
+ }
{
SCOPED_TRACE("layerG above");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
}
@@ -671,53 +999,86 @@
Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
{
SCOPED_TRACE("layerG below");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
}
}
-TEST_F(LayerTransactionTest, SetRelativeZNegative) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer);
+ Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
sp<SurfaceControl> layerB;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
+
+ Transaction()
+ .reparent(layerB, parent->getHandle())
+ .apply();
// layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
- sp<ScreenCapture> screenshot;
+ std::unique_ptr<ScreenCapture> screenshot;
// only layerB is in this range
- ScreenCapture::captureScreen(&screenshot, -2, -1);
+ sp<IBinder> parentHandle = parent->getHandle();
+ ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
-TEST_F(LayerTransactionTest, SetRelativeZGroup) {
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
sp<SurfaceControl> layerB;
- ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
- ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
- ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE));
+ ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32));
// layerR = 0, layerG = layerR + 3, layerB = 2
- Transaction()
- .setPosition(layerG, 8, 8)
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
- .setPosition(layerB, 16, 16)
- .setLayer(layerB, mLayerZBase + 2)
- .apply();
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ Transaction()
+ .setPosition(layerG, 8, 8)
+ .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setPosition(layerB, 16, 16)
+ .setLayer(layerB, mLayerZBase + 2)
+ .apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ Transaction()
+ .setFrame(layerR, Rect(0, 0, 32, 32))
+ .setFrame(layerG, Rect(8, 8, 40, 40))
+ .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setFrame(layerB, Rect(16, 16, 48, 48))
+ .setLayer(layerB, mLayerZBase + 2)
+ .apply();
+ break;
+ default:
+ ASSERT_FALSE(true) << "Unsupported layer type";
+ }
+
{
SCOPED_TRACE("(layerR < layerG) < layerB");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
@@ -727,7 +1088,7 @@
Transaction().setLayer(layerR, mLayerZBase + 4).apply();
{
SCOPED_TRACE("layerB < (layerR < layerG)");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -737,7 +1098,7 @@
Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
{
SCOPED_TRACE("layerB < (layerG < layerR)");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
@@ -748,7 +1109,7 @@
Transaction().setLayer(layerG, mLayerZBase).apply();
{
SCOPED_TRACE("layerG < layerB < layerR");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
}
@@ -758,57 +1119,65 @@
Transaction().setLayer(layerR, mLayerZBase + 1).apply();
{
SCOPED_TRACE("layerG < layerR < layerB");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
}
}
-TEST_F(LayerTransactionTest, SetRelativeZBug64572777) {
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
+ ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
Transaction()
.setPosition(layerG, 16, 16)
.setRelativeLayer(layerG, layerR->getHandle(), 1)
.apply();
- mClient->destroySurface(layerG->getHandle());
+ layerG.clear();
// layerG should have been removed
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(LayerTransactionTest, SetFlagsHidden) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
{
SCOPED_TRACE("layer hidden");
- screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
{
SCOPED_TRACE("layer shown");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetFlagsOpaque) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
const Color translucentRed = {100, 0, 0, 100};
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32));
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
Transaction()
.setLayer(layerR, mLayerZBase + 1)
@@ -816,21 +1185,21 @@
.apply();
{
SCOPED_TRACE("layerR opaque");
- screenshot()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
}
Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
{
SCOPED_TRACE("layerR translucent");
const uint8_t g = uint8_t(255 - translucentRed.a);
- screenshot()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
}
}
-TEST_F(LayerTransactionTest, SetFlagsSecure) {
+TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
sp<ISurfaceComposer> composer = ComposerService::getComposerService();
sp<GraphicBuffer> outBuffer;
@@ -838,31 +1207,79 @@
.setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
.apply(true);
ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase,
- false));
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
ASSERT_EQ(NO_ERROR,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase,
- false));
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) {
+/** RAII Wrapper around get/seteuid */
+class UIDFaker {
+ uid_t oldId;
+public:
+ UIDFaker(uid_t uid) {
+ oldId = geteuid();
+ seteuid(uid);
+ }
+ ~UIDFaker() {
+ seteuid(oldId);
+ }
+};
+
+TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<GraphicBuffer> outBuffer;
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+ .apply(true);
+ ASSERT_EQ(PERMISSION_DENIED,
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ UIDFaker f(AID_SYSTEM);
+
+ // By default the system can capture screenshots with secure layers but they
+ // will be blacked out
+ ASSERT_EQ(NO_ERROR,
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+ // to receive them...we are expected to take care with the results.
+ ASSERT_EQ(NO_ERROR,
+ composer->captureScreen(mDisplay, &outBuffer,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+ Rect(), 0, 0, false,
+ ISurfaceComposer::eRotateNone, true));
+ ScreenCapture sc(outBuffer);
+ sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
const Rect top(0, 0, 32, 16);
const Rect bottom(0, 16, 32, 32);
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
- ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, top, Color::TRANSPARENT));
- ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, bottom, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
+ ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::RED));
// setTransparentRegionHint always applies to the following buffer
Transaction().setTransparentRegionHint(layer, Region(top)).apply();
- ASSERT_NO_FATAL_FAILURE(postLayerBuffer(layer));
+ ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
{
SCOPED_TRACE("top transparent");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::BLACK);
shot->expectColor(bottom, Color::RED);
}
@@ -870,24 +1287,75 @@
Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
{
SCOPED_TRACE("transparent region hint pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::BLACK);
shot->expectColor(bottom, Color::RED);
}
- ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
- ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, top, Color::RED));
- ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, bottom, Color::TRANSPARENT));
- ASSERT_NO_FATAL_FAILURE(postLayerBuffer(layer));
+ ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+ ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
+ ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
{
SCOPED_TRACE("bottom transparent");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(top, Color::RED);
shot->expectColor(bottom, Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
+ const Rect top(0, 0, 32, 16);
+ const Rect bottom(0, 16, 32, 32);
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
+ ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::RED));
+ Transaction()
+ .setTransparentRegionHint(layer, Region(top))
+ .setBuffer(layer, buffer)
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .apply();
+ {
+ SCOPED_TRACE("top transparent");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::BLACK);
+ shot->expectColor(bottom, Color::RED);
+ }
+
+ Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
+ {
+ SCOPED_TRACE("transparent region hint intermediate");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::BLACK);
+ shot->expectColor(bottom, Color::BLACK);
+ }
+
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT));
+ Transaction().setBuffer(layer, buffer).apply();
+ {
+ SCOPED_TRACE("bottom transparent");
+ auto shot = getScreenCapture();
+ shot->expectColor(top, Color::RED);
+ shot->expectColor(bottom, Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
sp<SurfaceControl> layerTransparent;
sp<SurfaceControl> layerR;
ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
@@ -895,32 +1363,66 @@
// check that transparent region hint is bound by the layer size
Transaction()
- .setTransparentRegionHint(layerTransparent,
- Region(Rect(0, 0, mDisplayWidth, mDisplayHeight)))
+ .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
.setPosition(layerR, 16, 16)
.setLayer(layerR, mLayerZBase + 1)
.apply();
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerTransparent, Color::TRANSPARENT));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED));
- screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+ ASSERT_NO_FATAL_FAILURE(
+ fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
+ getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
}
-TEST_F(LayerTransactionTest, SetAlphaBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
+ sp<SurfaceControl> layerTransparent;
+ sp<SurfaceControl> layerR;
+ ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(
+ layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ // check that transparent region hint is bound by the layer size
+ Transaction()
+ .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+ .setFrame(layerR, Rect(16, 16, 48, 48))
+ .setLayer(layerR, mLayerZBase + 1)
+ .apply();
+ ASSERT_NO_FATAL_FAILURE(
+ fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+ getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+}
+
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
sp<SurfaceControl> layer1;
sp<SurfaceControl> layer2;
- ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32));
- ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer1, {64, 0, 0, 255}));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer2, {0, 64, 0, 255}));
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32));
- Transaction()
- .setAlpha(layer1, 0.25f)
- .setAlpha(layer2, 0.75f)
- .setPosition(layer2, 16, 0)
- .setLayer(layer2, mLayerZBase + 1)
- .apply();
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ Transaction()
+ .setAlpha(layer1, 0.25f)
+ .setAlpha(layer2, 0.75f)
+ .setPosition(layer2, 16, 0)
+ .setLayer(layer2, mLayerZBase + 1)
+ .apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ Transaction()
+ .setAlpha(layer1, 0.25f)
+ .setAlpha(layer2, 0.75f)
+ .setFrame(layer1, Rect(0, 0, 32, 32))
+ .setFrame(layer2, Rect(16, 0, 48, 32))
+ .setLayer(layer2, mLayerZBase + 1)
+ .apply();
+ break;
+ default:
+ ASSERT_FALSE(true) << "Unsupported layer type";
+ }
{
- auto shot = screenshot();
+ auto shot = getScreenCapture();
uint8_t r = 16; // 64 * 0.25f
uint8_t g = 48; // 64 * 0.75f
shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
@@ -931,37 +1433,102 @@
}
}
-TEST_F(LayerTransactionTest, SetAlphaClamped) {
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
+ ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
+ ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
const Color color = {64, 0, 0, 255};
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32));
Transaction().setAlpha(layer, 2.0f).apply();
{
SCOPED_TRACE("clamped to 1.0f");
- screenshot()->expectColor(Rect(0, 0, 32, 32), color);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
}
Transaction().setAlpha(layer, -1.0f).apply();
{
SCOPED_TRACE("clamped to 0.0f");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetColorBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
+ sp<SurfaceControl> layer;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+ Transaction()
+ .setCornerRadius(layer, cornerRadius)
+ .apply();
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Transparent corners
+ 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), Color::BLACK);
+ shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
+ 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 / 2));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .reparent(child, parent->getHandle())
+ .setPosition(child, 0, size / 2)
+ .apply();
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Top edge of child should not have rounded corners because it's translated in the parent
+ shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
+ Color::GREEN);
+ // But bottom edges should have been clipped according to parent bounds
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+ shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
- Transaction().setLayer(colorLayer, mLayerZBase + 1).apply();
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(colorLayer, mLayerZBase + 1)
+ .apply();
+
{
SCOPED_TRACE("default color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
@@ -972,26 +1539,217 @@
Transaction().setColor(colorLayer, color).apply();
{
SCOPED_TRACE("new color");
- screenshot()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
}
}
-TEST_F(LayerTransactionTest, SetColorClamped) {
- sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// BLUE: prior background color
+// GREEN: final background color
+// BLACK: no color or fill
+void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
+ bool bufferFill, float alpha,
+ Color finalColor) {
+ sp<SurfaceControl> layer;
+ int32_t width = 500;
+ int32_t height = 500;
- Transaction().setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ Color fillColor = Color::RED;
+ Color priorBgColor = Color::BLUE;
+ Color expectedColor = Color::BLACK;
+ switch (layerType) {
+ case ISurfaceComposerClient::eFXSurfaceColor:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
+ Transaction()
+ .setCrop_legacy(layer, Rect(0, 0, width, height))
+ .setColor(layer, half3(1.0f, 0, 0))
+ .apply();
+ expectedColor = fillColor;
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+ if (bufferFill) {
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
+ expectedColor = fillColor;
+ }
+ Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+ break;
+ case ISurfaceComposerClient::eFXSurfaceBufferState:
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
+ if (bufferFill) {
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+ expectedColor = fillColor;
+ }
+ Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+ break;
+ default:
+ GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
+ return;
+ }
+
+ if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) {
+ Transaction()
+ .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
+ .apply();
+ if (!bufferFill) {
+ expectedColor = priorBgColor;
+ }
+ }
+
+ {
+ SCOPED_TRACE("default before setting background color layer");
+ screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
+ }
+ Transaction()
+ .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
+ .apply();
+
+ {
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, width, height), finalColor);
+ shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
+ }
}
-TEST_F(LayerTransactionTest, SetColorWithAlpha) {
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = true;
+ float alpha = 1.0f;
+ Color finalColor = Color::RED;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 1.0f;
+ Color finalColor = Color::GREEN;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
+ bool priorColor = false;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+ SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
+ bool priorColor = true;
+ bool bufferFill = false;
+ float alpha = 0;
+ Color finalColor = Color::BLACK;
+ ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+ priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
+ .apply();
+
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
@@ -1004,20 +1762,21 @@
.setAlpha(colorLayer, alpha)
.setLayer(colorLayer, mLayerZBase + 1)
.apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
- tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
}
-TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> parentLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
- ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer(
- "childWithColor", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
-
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+ ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -1030,91 +1789,137 @@
.setAlpha(parentLayer, alpha)
.setLayer(parentLayer, mLayerZBase + 1)
.apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
- tolerance);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
}
-TEST_F(LayerTransactionTest, SetColorWithBuffer) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
sp<SurfaceControl> bufferLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
// color is ignored
Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(LayerTransactionTest, SetLayerStackBasic) {
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
{
SCOPED_TRACE("non-existing layer stack");
- screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
}
Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
{
SCOPED_TRACE("original layer stack");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetMatrixBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(
- fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
{
SCOPED_TRACE("IDENTITY");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
}
Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
{
SCOPED_TRACE("FLIP_H");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
- Color::BLUE);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
+ Color::WHITE, Color::BLUE);
}
Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
{
SCOPED_TRACE("FLIP_V");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
- Color::GREEN);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
+ Color::RED, Color::GREEN);
}
Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
{
SCOPED_TRACE("ROT_90");
- screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
- Color::GREEN);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
+ Color::WHITE, Color::GREEN);
}
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
{
SCOPED_TRACE("SCALE");
- screenshot()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, true /* filtered */);
}
}
-TEST_F(LayerTransactionTest, SetMatrixRot45) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction()
+ .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .apply();
+ {
+ SCOPED_TRACE("IDENTITY");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
+ {
+ SCOPED_TRACE("FLIP_H");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
+ {
+ SCOPED_TRACE("FLIP_V");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
+ {
+ SCOPED_TRACE("ROT_90");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+
+ Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
+ {
+ SCOPED_TRACE("SCALE");
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(
- fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
const float rot = M_SQRT1_2; // 45 degrees
const float trans = M_SQRT2 * 16.0f;
Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
// check a 8x8 region inside each color
auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
const int32_t halfL = 4;
@@ -1127,31 +1932,33 @@
shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
}
-TEST_F(LayerTransactionTest, SetMatrixWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// setMatrix is applied after any pending resize, unlike setPosition
Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
- shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ auto shot = getScreenCapture();
+ const Rect rect(0, 0, 32, 32);
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
}
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
{
SCOPED_TRACE("resize applied");
- screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+ const Rect rect(0, 0, 128, 128);
+ getScreenCapture()->expectColor(rect, Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
// setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
Transaction()
@@ -1159,14 +1966,14 @@
.setSize(layer, 64, 64)
.setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
.apply();
- screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
}
-TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(
- fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
// XXX SCALE_CROP is not respected; calling setSize and
// setOverrideScalingMode in separate transactions does not work
@@ -1177,363 +1984,2080 @@
.apply();
{
SCOPED_TRACE("SCALE_TO_WINDOW");
- screenshot()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, Color::BLUE,
- Color::WHITE, true /* filtered */);
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE, true /* filtered */);
}
}
-TEST_F(LayerTransactionTest, SetCropBasic) {
+TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+
+ sp<IBinder> handle = layer->getHandle();
+ ASSERT_TRUE(handle != nullptr);
+
+ FrameStats frameStats;
+ mClient->getLayerFrameStats(handle, &frameStats);
+
+ ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
const Rect crop(8, 8, 24, 24);
- Transaction().setCrop(layer, crop).apply();
- auto shot = screenshot();
+ Transaction().setCrop_legacy(layer, crop).apply();
+ auto shot = getScreenCapture();
shot->expectColor(crop, Color::RED);
shot->expectBorder(crop, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropEmpty) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ const Rect crop(8, 8, 24, 24);
+
+ Transaction().setCrop(layer, crop).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("empty rect");
+ Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+
+ {
+ SCOPED_TRACE("negative rect");
+ Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
{
SCOPED_TRACE("empty rect");
Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
}
{
SCOPED_TRACE("negative rect");
Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
}
}
-TEST_F(LayerTransactionTest, SetCropOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply();
- auto shot = screenshot();
+ Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropWithTranslation) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
+ fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
+
+ Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
+
+ Transaction().setBuffer(layer, buffer).apply();
+
+ // Partially out of bounds in the negative (upper left) direction
+ Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
+ {
+ SCOPED_TRACE("out of bounds, negative (upper left) direction");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ }
+
+ // Partially out of bounds in the positive (lower right) direction
+ Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
+ {
+ SCOPED_TRACE("out of bounds, positive (lower right) direction");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ }
+
+ // Fully out of buffer space bounds
+ Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
+ {
+ SCOPED_TRACE("Fully out of bounds");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
+ shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
+ shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
const Point position(32, 32);
const Rect crop(8, 8, 24, 24);
- Transaction().setPosition(layer, position.x, position.y).setCrop(layer, crop).apply();
- auto shot = screenshot();
+ Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+ auto shot = getScreenCapture();
shot->expectColor(crop + position, Color::RED);
shot->expectBorder(crop + position, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropWithScale) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ const Rect frame(32, 32, 64, 64);
+ const Rect crop(8, 8, 24, 24);
+ Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(frame, Color::RED);
+ shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // crop is affected by matrix
+ // crop_legacy is affected by matrix
Transaction()
.setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setCrop(layer, Rect(8, 8, 24, 24))
+ .setCrop_legacy(layer, Rect(8, 8, 24, 24))
.apply();
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetCropWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // setCrop is applied immediately by default, with or without resize pending
- Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+ // setCrop_legacy is applied immediately by default, with or without resize pending
+ Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
{
SCOPED_TRACE("resize pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
}
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
{
SCOPED_TRACE("resize applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetCropWithNextResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // request setCrop to be applied with the next resize
- Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setGeometryAppliesWithResize(layer).apply();
+ // request setCrop_legacy to be applied with the next resize
+ Transaction()
+ .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+ .setGeometryAppliesWithResize(layer)
+ .apply();
{
SCOPED_TRACE("waiting for next resize");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
- Transaction().setCrop(layer, Rect(4, 4, 12, 12)).apply();
+ Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply();
{
SCOPED_TRACE("pending crop modified");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
Transaction().setSize(layer, 16, 16).apply();
{
SCOPED_TRACE("resize pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
// finally resize
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
{
SCOPED_TRACE("new crop applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) {
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // setCrop is not immediate even with SCALE_TO_WINDOW override
+ // setCrop_legacy is not immediate even with SCALE_TO_WINDOW override
Transaction()
- .setCrop(layer, Rect(4, 4, 12, 12))
+ .setCrop_legacy(layer, Rect(4, 4, 12, 12))
.setSize(layer, 16, 16)
.setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
.setGeometryAppliesWithResize(layer)
.apply();
{
SCOPED_TRACE("new crop pending");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
}
// XXX crop is never latched without other geometry change (b/69315677)
Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply();
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
Transaction().setPosition(layer, 0, 0).apply();
{
SCOPED_TRACE("new crop applied");
- auto shot = screenshot();
+ auto shot = getScreenCapture();
shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetFinalCropBasic) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
- const Rect crop(8, 8, 24, 24);
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ const Rect frame(8, 8, 24, 24);
- // same as in SetCropBasic
- Transaction().setFinalCrop(layer, crop).apply();
- auto shot = screenshot();
- shot->expectColor(crop, Color::RED);
- shot->expectBorder(crop, Color::BLACK);
+ Transaction().setFrame(layer, frame).apply();
+ auto shot = getScreenCapture();
+ shot->expectColor(frame, Color::RED);
+ shot->expectBorder(frame, Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFinalCropEmpty) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
- // same as in SetCropEmpty
{
SCOPED_TRACE("empty rect");
- Transaction().setFinalCrop(layer, Rect(8, 8, 8, 8)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
{
SCOPED_TRACE("negative rect");
- Transaction().setFinalCrop(layer, Rect(8, 8, 0, 0)).apply();
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
}
-TEST_F(LayerTransactionTest, SetFinalCropOutOfBounds) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
- // same as in SetCropOutOfBounds
- Transaction().setFinalCrop(layer, Rect(-128, -64, 128, 64)).apply();
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ // A parentless layer will default to a frame with the same size as the buffer
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
+ sp<SurfaceControl> parent, child;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+ Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+ ASSERT_NO_FATAL_FAILURE(
+ child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+ Transaction().reparent(child, parent->getHandle()).apply();
+
+ // A layer will default to the frame of its parent
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFinalCropWithTranslation) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
+ sp<SurfaceControl> parent, child;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
- // final crop is applied post-translation
- Transaction().setPosition(layer, 16, 16).setFinalCrop(layer, Rect(8, 8, 24, 24)).apply();
- auto shot = screenshot();
- shot->expectColor(Rect(16, 16, 24, 24), Color::RED);
- shot->expectBorder(Rect(16, 16, 24, 24), Color::BLACK);
+ ASSERT_NO_FATAL_FAILURE(
+ child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+ Transaction().reparent(child, parent->getHandle()).apply();
+
+ // A layer will default to the frame of its parent
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
}
-TEST_F(LayerTransactionTest, SetFinalCropWithScale) {
+TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+ Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
- // final crop is not affected by matrix
+ std::this_thread::sleep_for(500ms);
+
+ Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
+ shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
+ sp<SurfaceControl> parent, child;
+ ASSERT_NO_FATAL_FAILURE(
+ parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(
+ child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ Transaction().reparent(child, parent->getHandle()).apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+ Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+ Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
+ shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("set buffer 1");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+
+ {
+ SCOPED_TRACE("set buffer 2");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ {
+ SCOPED_TRACE("set buffer 3");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
+ sp<SurfaceControl> layer1;
+ ASSERT_NO_FATAL_FAILURE(
+ layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<SurfaceControl> layer2;
+ ASSERT_NO_FATAL_FAILURE(
+ layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+
+ Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
+ {
+ SCOPED_TRACE("set layer 1 buffer red");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+
+ Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
+ {
+ SCOPED_TRACE("set layer 2 buffer blue");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
+ shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+ {
+ SCOPED_TRACE("set layer 1 buffer green");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+ shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+
+ {
+ SCOPED_TRACE("set layer 2 buffer white");
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
+ shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+ shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 10> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 70> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+ std::array<sp<GraphicBuffer>, 65> buffers;
+
+ size_t idx = 0;
+ for (auto& buffer : buffers) {
+ buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ Color color = colors[idx % colors.size()];
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+ idx++;
+ }
+
+ // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+ // cache is working.
+ idx = 0;
+ for (auto& buffer : buffers) {
+ for (int i = 0; i < 2; i++) {
+ Transaction().setBuffer(layer, buffer).apply();
+
+ Color color = colors[idx % colors.size()];
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+ }
+ if (idx == 0) {
+ buffers[0].clear();
+ }
+ idx++;
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
Transaction()
- .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setFinalCrop(layer, Rect(8, 8, 24, 24))
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
.apply();
- auto shot = screenshot();
- shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
- shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
+
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
+ Color::GREEN, true /* filtered */);
}
-TEST_F(LayerTransactionTest, SetFinalCropWithResize) {
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- // same as in SetCropWithResize
- Transaction().setFinalCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
- {
- SCOPED_TRACE("resize pending");
- auto shot = screenshot();
- shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
- shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
- }
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
- {
- SCOPED_TRACE("resize applied");
- auto shot = screenshot();
- shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
- shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
- }
-}
-
-TEST_F(LayerTransactionTest, SetFinalCropWithNextResize) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
-
- // same as in SetCropWithNextResize
Transaction()
- .setFinalCrop(layer, Rect(8, 8, 24, 24))
- .setGeometryAppliesWithResize(layer)
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
+ .apply();
+
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
+ Color::BLUE, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+ Color::BLUE, Color::WHITE));
+
+ Transaction()
+ .setFrame(layer, Rect(0, 0, 32, 32))
+ .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
+ .apply();
+
+ getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
+ Color::GREEN, true /* filtered */);
+}
+
+TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction().setTransformToDisplayInverse(layer, false).apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+
+ Transaction().setTransformToDisplayInverse(layer, true).apply();
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ Transaction transaction;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ sp<Fence> fence;
+ if (getBuffer(nullptr, &fence) != NO_ERROR) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+ status_t status = fence->wait(1000);
+ ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
+ std::this_thread::sleep_for(200ms);
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ sp<Fence> fence = Fence::NO_FENCE;
+
+ Transaction()
+ .setBuffer(layer, buffer)
+ .setAcquireFence(layer, fence)
+ .apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ Transaction()
+ .setBuffer(layer, buffer)
+ .setDataspace(layer, ui::Dataspace::UNKNOWN)
+ .apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ HdrMetadata hdrMetadata;
+ hdrMetadata.validTypes = 0;
+ Transaction()
+ .setBuffer(layer, buffer)
+ .setHdrMetadata(layer, hdrMetadata)
+ .apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ Region region;
+ region.set(32, 32);
+ Transaction()
+ .setBuffer(layer, buffer)
+ .setSurfaceDamageRegion(layer, region)
+ .apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ Transaction()
+ .setBuffer(layer, buffer)
+ .setApi(layer, NATIVE_WINDOW_API_CPU)
+ .apply();
+
+ auto shot = getScreenCapture();
+ shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+ shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ // verify this doesn't cause a crash
+ Transaction().setSidebandStream(layer, nullptr).apply();
+}
+
+TEST_F(LayerTransactionTest, ReparentToSelf) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+ Transaction().reparent(layer, layer->getHandle()).apply();
+
+ {
+ // We expect the transaction to be silently dropped, but for SurfaceFlinger
+ // to still be functioning.
+ SCOPED_TRACE("after reparent to self");
+ const Rect rect(0, 0, 32, 32);
+ auto shot = screenshot();
+ shot->expectColor(rect, Color::RED);
+ shot->expectBorder(rect, Color::BLACK);
+ }
+}
+
+class ColorTransformHelper {
+public:
+ static void DegammaColorSingle(half& s) {
+ if (s <= 0.03928f)
+ s = s / 12.92f;
+ else
+ s = pow((s + 0.055f) / 1.055f, 2.4f);
+ }
+
+ static void DegammaColor(half3& color) {
+ DegammaColorSingle(color.r);
+ DegammaColorSingle(color.g);
+ DegammaColorSingle(color.b);
+ }
+
+ static void GammaColorSingle(half& s) {
+ if (s <= 0.0031308f) {
+ s = s * 12.92f;
+ } else {
+ s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f;
+ }
+ }
+
+ static void GammaColor(half3& color) {
+ GammaColorSingle(color.r);
+ GammaColorSingle(color.g);
+ GammaColorSingle(color.b);
+ }
+
+ static void applyMatrix(half3& color, const mat3& mat) {
+ half3 ret = half3(0);
+
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ ret[i] = ret[i] + color[j] * mat[j][i];
+ }
+ }
+ color = ret;
+ }
+};
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(colorLayer, mLayerZBase + 1)
.apply();
{
- SCOPED_TRACE("waiting for next resize");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
- Transaction().setFinalCrop(layer, Rect(4, 4, 12, 12)).apply();
- {
- SCOPED_TRACE("pending final crop modified");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+ half3 expected = color;
+ mat3 matrix;
+ matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
+ matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
+ matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
+
+ // degamma before applying the matrix
+ if (mColorManagementUsed) {
+ ColorTransformHelper::DegammaColor(expected);
}
- Transaction().setSize(layer, 16, 16).apply();
- {
- SCOPED_TRACE("resize pending");
- screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ ColorTransformHelper::applyMatrix(expected, matrix);
+
+ if (mColorManagementUsed) {
+ ColorTransformHelper::GammaColor(expected);
}
- // finally resize
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+ const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+ uint8_t(expected.b * 255), 255};
+
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+
+ Transaction().setColor(colorLayer, color)
+ .setColorTransform(colorLayer, matrix, vec3()).apply();
{
- SCOPED_TRACE("new final crop applied");
- auto shot = screenshot();
- shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
- shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
}
}
-TEST_F(LayerTransactionTest, SetFinalCropWithNextResizeScaleToWindow) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
+ sp<SurfaceControl> parentLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer));
+ ASSERT_NO_FATAL_FAILURE(
+ colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
- // same as in SetCropWithNextResizeScaleToWindow
Transaction()
- .setFinalCrop(layer, Rect(4, 4, 12, 12))
- .setSize(layer, 16, 16)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .setGeometryAppliesWithResize(layer)
+ .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(parentLayer, mLayerZBase + 1)
.apply();
{
- SCOPED_TRACE("new final crop pending");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
- shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
- // XXX final crop is never latched without other geometry change (b/69315677)
- Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply();
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED));
- Transaction().setPosition(layer, 0, 0).apply();
- {
- SCOPED_TRACE("new final crop applied");
- auto shot = screenshot();
- shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
- shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
+ const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+ half3 expected = color;
+ mat3 matrix;
+ matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
+ matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
+ matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
+
+ // degamma before applying the matrix
+ if (mColorManagementUsed) {
+ ColorTransformHelper::DegammaColor(expected);
}
+
+ ColorTransformHelper::applyMatrix(expected, matrix);
+
+ if (mColorManagementUsed) {
+ ColorTransformHelper::GammaColor(expected);
+ }
+
+ const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+ uint8_t(expected.b * 255), 255};
+
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+
+ Transaction()
+ .setColor(colorLayer, color)
+ .setColorTransform(parentLayer, matrix, vec3())
+ .apply();
+ {
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
+ sp<SurfaceControl> parentLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceContainer));
+ ASSERT_NO_FATAL_FAILURE(
+ colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
+
+ Transaction()
+ .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(parentLayer, mLayerZBase + 1)
+ .apply();
+ {
+ SCOPED_TRACE("default color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+ half3 expected = color;
+ mat3 matrixChild;
+ matrixChild[0][0] = 0.3; matrixChild[1][0] = 0.59; matrixChild[2][0] = 0.11;
+ matrixChild[0][1] = 0.3; matrixChild[1][1] = 0.59; matrixChild[2][1] = 0.11;
+ matrixChild[0][2] = 0.3; matrixChild[1][2] = 0.59; matrixChild[2][2] = 0.11;
+ mat3 matrixParent;
+ matrixParent[0][0] = 0.2; matrixParent[1][0] = 0.4; matrixParent[2][0] = 0.10;
+ matrixParent[0][1] = 0.2; matrixParent[1][1] = 0.4; matrixParent[2][1] = 0.10;
+ matrixParent[0][2] = 0.2; matrixParent[1][2] = 0.4; matrixParent[2][2] = 0.10;
+
+ // degamma before applying the matrix
+ if (mColorManagementUsed) {
+ ColorTransformHelper::DegammaColor(expected);
+ }
+
+ ColorTransformHelper::applyMatrix(expected, matrixChild);
+ ColorTransformHelper::applyMatrix(expected, matrixParent);
+
+ if (mColorManagementUsed) {
+ ColorTransformHelper::GammaColor(expected);
+ }
+
+ const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+ uint8_t(expected.b * 255), 255};
+
+ // this is handwavy, but the precison loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+
+ Transaction()
+ .setColor(colorLayer, color)
+ .setColorTransform(parentLayer, matrixParent, vec3())
+ .setColorTransform(colorLayer, matrixChild, vec3())
+ .apply();
+ {
+ SCOPED_TRACE("new color");
+ getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+ }
+}
+
+struct CallbackData {
+ CallbackData() = default;
+ CallbackData(nsecs_t time, const sp<Fence>& fence,
+ const std::vector<SurfaceControlStats>& stats)
+ : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
+
+ nsecs_t latchTime;
+ sp<Fence> presentFence;
+ std::vector<SurfaceControlStats> surfaceControlStats;
+};
+
+class ExpectedResult {
+public:
+ enum Transaction {
+ NOT_PRESENTED = 0,
+ PRESENTED,
+ };
+
+ enum Buffer {
+ NOT_ACQUIRED = 0,
+ ACQUIRED,
+ };
+
+ enum PreviousBuffer {
+ NOT_RELEASED = 0,
+ RELEASED,
+ UNKNOWN,
+ };
+
+ void reset() {
+ mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+ mExpectedSurfaceResults.clear();
+ }
+
+ void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
+ ExpectedResult::Buffer bufferResult = ACQUIRED,
+ ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+ mTransactionResult = transactionResult;
+ mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
+ std::forward_as_tuple(bufferResult, previousBufferResult));
+ }
+
+ void addSurfaces(ExpectedResult::Transaction transactionResult,
+ const std::vector<sp<SurfaceControl>>& layers,
+ ExpectedResult::Buffer bufferResult = ACQUIRED,
+ ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+ for (const auto& layer : layers) {
+ addSurface(transactionResult, layer, bufferResult, previousBufferResult);
+ }
+ }
+
+ void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+ mExpectedPresentTime = expectedPresentTime;
+ }
+
+ void verifyCallbackData(const CallbackData& callbackData) const {
+ const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
+ if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
+ ASSERT_GE(latchTime, 0) << "bad latch time";
+ ASSERT_NE(presentFence, nullptr);
+ if (mExpectedPresentTime >= 0) {
+ ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+ ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+ // if the panel is running at 30 hz, at the worst case, our expected time just
+ // misses vsync and we have to wait another 33.3ms
+ ASSERT_LE(presentFence->getSignalTime(),
+ mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+ }
+ } else {
+ ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
+ ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
+ }
+
+ ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
+ << "wrong number of surfaces";
+
+ for (const auto& stats : surfaceControlStats) {
+ ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+ const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
+ ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
+ << "unexpected surface control";
+ expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
+ }
+ }
+
+private:
+ class ExpectedSurfaceResult {
+ public:
+ ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
+ ExpectedResult::PreviousBuffer previousBufferResult)
+ : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
+
+ void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
+ nsecs_t latchTime) const {
+ const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
+
+ ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+ << "bad acquire time";
+ ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+
+ if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
+ ASSERT_NE(previousReleaseFence, nullptr)
+ << "failed to set release prev buffer fence";
+ } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
+ ASSERT_EQ(previousReleaseFence, nullptr)
+ << "should not have set released prev buffer fence";
+ }
+ }
+
+ private:
+ ExpectedResult::Buffer mBufferResult;
+ ExpectedResult::PreviousBuffer mPreviousBufferResult;
+ };
+
+ struct SCHash {
+ std::size_t operator()(const sp<SurfaceControl>& sc) const {
+ return std::hash<IBinder*>{}(sc->getHandle().get());
+ }
+ };
+ ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+ nsecs_t mExpectedPresentTime = -1;
+ std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
+};
+
+class CallbackHelper {
+public:
+ static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context");
+ }
+ CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
+ std::lock_guard lock(helper->mMutex);
+ helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
+ helper->mConditionVariable.notify_all();
+ }
+
+ void getCallbackData(CallbackData* outData) {
+ std::unique_lock lock(mMutex);
+
+ if (mCallbackDataQueue.empty()) {
+ ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
+ std::cv_status::timeout)
+ << "did not receive callback";
+ }
+
+ *outData = std::move(mCallbackDataQueue.front());
+ mCallbackDataQueue.pop();
+ }
+
+ void verifyFinalState() {
+ // Wait to see if there are extra callbacks
+ std::this_thread::sleep_for(500ms);
+
+ std::lock_guard lock(mMutex);
+ EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+ mCallbackDataQueue = {};
+ }
+
+ void* getContext() { return static_cast<void*>(this); }
+
+ std::mutex mMutex;
+ std::condition_variable mConditionVariable;
+ std::queue<CallbackData> mCallbackDataQueue;
+};
+
+class LayerCallbackTest : public LayerTransactionTest {
+public:
+ virtual sp<SurfaceControl> createBufferStateLayer() {
+ return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+ }
+
+ static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+ const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true,
+ bool setBackgroundColor = false) {
+ if (layer) {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ if (setBuffer) {
+ int err = getBuffer(&buffer, &fence);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transaction.setBuffer(layer, buffer);
+ transaction.setAcquireFence(layer, fence);
+ }
+
+ if (setBackgroundColor) {
+ transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
+ ui::Dataspace::UNKNOWN);
+ }
+ }
+
+ transaction.addTransactionCompletedCallback(callbackHelper->function,
+ callbackHelper->getContext());
+ return NO_ERROR;
+ }
+
+ static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+ bool finalState = false) {
+ CallbackData callbackData;
+ ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+ EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+ if (finalState) {
+ ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+ }
+ }
+
+ static void waitForCallbacks(CallbackHelper& helper,
+ const std::vector<ExpectedResult>& expectedResults,
+ bool finalState = false) {
+ for (const auto& expectedResult : expectedResults) {
+ waitForCallback(helper, expectedResult);
+ }
+ if (finalState) {
+ ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+ }
+ }
+};
+
+TEST_F(LayerCallbackTest, BufferColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferNoColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, false, false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, BufferNoColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true, false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferColor) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoStateChange) {
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, OffScreen) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeBufferNoColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeNoBufferColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2, false, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+TEST_F(LayerCallbackTest, Merge_SameCallback) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction1, &callback, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameLayer) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_DifferentClients) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ ExpectedResult expected;
+
+ if (i == 0) {
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ } else {
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ }
+
+ transaction.apply();
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ if (i == 0) {
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ } else {
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expected;
+ expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
+ : ExpectedResult::Transaction::NOT_PRESENTED,
+ layer,
+ (i == 0) ? ExpectedResult::Buffer::ACQUIRED
+ : ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+ ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+ ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+
+ // Normal call to set up test
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+ expected.reset();
+
+ // Test
+ err = fillTransaction(transaction1, &callback1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.merge(std::move(transaction1)).apply();
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
+ sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+ client2(new SurfaceComposerClient);
+
+ ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+ ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1, callback2;
+
+ // Normal call to set up test
+ int err = fillTransaction(transaction1, &callback1, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ ExpectedResult expected;
+ expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+ expected.reset();
+
+ // Test
+ err = fillTransaction(transaction1, &callback1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ std::vector<ExpectedResult> expectedResults(50);
+ for (auto& expected : expectedResults) {
+ expected.reset();
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::UNKNOWN);
+
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+ }
+ EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ // Normal call to set up test
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+
+ // Test
+ std::vector<ExpectedResult> expectedResults(50);
+ for (auto& expected : expectedResults) {
+ expected.reset();
+
+ err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+ }
+ EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ // Normal call to set up test
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+ ExpectedResult expectedResult;
+ expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
+
+ // Test
+ std::vector<ExpectedResult> expectedResults(50);
+ for (auto& expected : expectedResults) {
+ expected.reset();
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED);
+
+ err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+ }
+ EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTime(time);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback1;
+ int err = fillTransaction(transaction, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected1;
+ expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected1.addExpectedPresentTime(time);
+
+ CallbackHelper callback2;
+ err = fillTransaction(transaction, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 33ms after the first frame
+ time += (33.3 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected2;
+ expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+ expected2.addExpectedPresentTime(time);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback1;
+ int err = fillTransaction(transaction, &callback1, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the future
+ nsecs_t time = systemTime() + (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected1;
+ expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected1.addExpectedPresentTime(time);
+
+ CallbackHelper callback2;
+ err = fillTransaction(transaction, &callback2, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 33ms before the previous frame
+ time -= (33.3 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected2;
+ expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ // Try to present 100ms in the past
+ nsecs_t time = systemTime() - (100 * 1e6);
+
+ transaction.setDesiredPresentTime(time);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+ expected.addExpectedPresentTime(systemTime());
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
class LayerUpdateTest : public LayerTransactionTest {
protected:
virtual void SetUp() {
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
- sp<IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
// Background surface
- mBGSurfaceControl =
- mComposerClient->createSurface(String8("BG Test Surface"), displayWidth,
- displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+ mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth,
+ displayHeight, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
ASSERT_TRUE(mBGSurfaceControl->isValid());
fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
// Foreground surface
- mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
- PIXEL_FORMAT_RGBA_8888, 0);
+ mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
ASSERT_TRUE(mFGSurfaceControl != nullptr);
ASSERT_TRUE(mFGSurfaceControl->isValid());
fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
// Synchronization surface
- mSyncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
- PIXEL_FORMAT_RGBA_8888, 0);
+ mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
ASSERT_TRUE(mSyncSurfaceControl != nullptr);
ASSERT_TRUE(mSyncSurfaceControl->isValid());
@@ -1555,11 +4079,10 @@
}
virtual void TearDown() {
- mComposerClient->dispose();
+ LayerTransactionTest::TearDown();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
mSyncSurfaceControl = 0;
- mComposerClient = 0;
}
void waitForPostedBuffers() {
@@ -1572,13 +4095,7 @@
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
}
- void asTransaction(const std::function<void(Transaction&)>& exec) {
- Transaction t;
- exec(t);
- t.apply(true);
- }
- sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
@@ -1588,10 +4105,10 @@
};
TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
- sp<ScreenCapture> sc;
- sp<SurfaceControl> relative = mComposerClient->createSurface(String8("relativeTestSurface"), 10,
- 10, PIXEL_FORMAT_RGBA_8888, 0);
+ std::unique_ptr<ScreenCapture> sc;
+
+ sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
fillSurfaceRGBA8(relative, 10, 10, 10);
waitForPostedBuffers();
@@ -1649,13 +4166,12 @@
asTransaction([&](Transaction& t) {
t.setSize(mFGSurfaceControl, 64, 64);
t.setPosition(mFGSurfaceControl, 64, 64);
- t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
- t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
+ t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
});
EXPECT_INITIAL_STATE("After restoring initial state");
}
- sp<ScreenCapture> sc;
+ std::unique_ptr<ScreenCapture> sc;
};
class CropLatchingTest : public GeometryLatchingTest {
@@ -1679,45 +4195,8 @@
}
};
-// In this test we ensure that setGeometryAppliesWithResize actually demands
-// a buffer of the new size, and not just any size.
-TEST_F(CropLatchingTest, FinalCropLatchingBufferOldSize) {
- EXPECT_INITIAL_STATE("before anything");
- // Normally the crop applies immediately even while a resize is pending.
- asTransaction([&](Transaction& t) {
- t.setSize(mFGSurfaceControl, 128, 128);
- t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- });
-
- EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)");
-
- restoreInitialState();
-
- // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
- // initiating the resize.
- lockAndFillFGBuffer();
-
- asTransaction([&](Transaction& t) {
- t.setSize(mFGSurfaceControl, 128, 128);
- t.setGeometryAppliesWithResize(mFGSurfaceControl);
- t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- });
-
- EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)");
-
- // We now submit our old buffer, at the old size, and ensure it doesn't
- // trigger geometry latching.
- unlockFGBuffer();
-
- EXPECT_INITIAL_STATE("after unlocking FG buffer (with geometryAppliesWithResize)");
-
- completeFGResize();
-
- EXPECT_CROPPED_STATE("after the resize finishes");
-}
-
TEST_F(LayerUpdateTest, DeferredTransactionTest) {
- sp<ScreenCapture> sc;
+ std::unique_ptr<ScreenCapture> sc;
{
SCOPED_TRACE("before anything");
ScreenCapture::captureScreen(&sc);
@@ -1729,14 +4208,14 @@
// set up two deferred transactions on different frames
asTransaction([&](Transaction& t) {
t.setAlpha(mFGSurfaceControl, 0.75);
- t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
- mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+ t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+ mSyncSurfaceControl->getSurface()->getNextFrameNumber());
});
asTransaction([&](Transaction& t) {
t.setPosition(mFGSurfaceControl, 128, 128);
- t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
- mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+ t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+ mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
});
{
@@ -1772,26 +4251,27 @@
}
TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
- sp<ScreenCapture> sc;
+ std::unique_ptr<ScreenCapture> sc;
sp<SurfaceControl> childNoBuffer =
- mComposerClient->createSurface(String8("Bufferless child"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- sp<SurfaceControl> childBuffer =
- mComposerClient->createSurface(String8("Buffered child"), 20, 20,
- PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+ createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
+ PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
fillSurfaceRGBA8(childBuffer, 200, 200, 200);
-
- SurfaceComposerClient::Transaction{}.show(childNoBuffer).show(childBuffer).apply(true);
-
+ SurfaceComposerClient::Transaction{}
+ .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+ .show(childNoBuffer)
+ .show(childBuffer)
+ .apply(true);
{
ScreenCapture::captureScreen(&sc);
sc->expectChildColor(73, 73);
sc->expectFGColor(74, 74);
}
-
- SurfaceComposerClient::Transaction{}.setSize(childNoBuffer, 20, 20).apply(true);
-
+ SurfaceComposerClient::Transaction{}
+ .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+ .apply(true);
{
ScreenCapture::captureScreen(&sc);
sc->expectChildColor(73, 73);
@@ -1800,7 +4280,7 @@
}
TEST_F(LayerUpdateTest, MergingTransactions) {
- sp<ScreenCapture> sc;
+ std::unique_ptr<ScreenCapture> sc;
{
SCOPED_TRACE("before move");
ScreenCapture::captureScreen(&sc);
@@ -1827,13 +4307,13 @@
protected:
void SetUp() override {
LayerUpdateTest::SetUp();
- mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
fillSurfaceRGBA8(mChild, 200, 200, 200);
{
SCOPED_TRACE("before anything");
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(64, 64);
}
}
@@ -1843,7 +4323,7 @@
}
sp<SurfaceControl> mChild;
- sp<ScreenCapture> mCapture;
+ std::unique_ptr<ScreenCapture> mCapture;
};
TEST_F(ChildLayerTest, ChildLayerPositioning) {
@@ -1854,7 +4334,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// But 10 pixels in we should see the child surface
@@ -1866,7 +4346,7 @@
asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground should now be at 0, 0
mCapture->expectFGColor(0, 0);
// But 10 pixels in we should see the child surface
@@ -1881,27 +4361,11 @@
t.show(mChild);
t.setPosition(mChild, 0, 0);
t.setPosition(mFGSurfaceControl, 0, 0);
- t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
+ t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
});
{
- ScreenCapture::captureScreen(&mCapture);
- mCapture->expectChildColor(0, 0);
- mCapture->expectChildColor(4, 4);
- mCapture->expectBGColor(5, 5);
- }
-}
-
-TEST_F(ChildLayerTest, ChildLayerFinalCropping) {
- asTransaction([&](Transaction& t) {
- t.show(mChild);
- t.setPosition(mChild, 0, 0);
- t.setPosition(mFGSurfaceControl, 0, 0);
- t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
- });
-
- {
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(0, 0);
mCapture->expectChildColor(4, 4);
mCapture->expectBGColor(5, 5);
@@ -1916,7 +4380,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectFGColor(0, 0);
// Last pixel in foreground should now be the child.
mCapture->expectChildColor(63, 63);
@@ -1931,7 +4395,7 @@
// Find the boundary between the parent and child
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(9, 9);
mCapture->expectFGColor(10, 10);
}
@@ -1941,7 +4405,7 @@
// The boundary should be twice as far from the origin now.
// The pixels from the last test should all be child now
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(9, 9);
mCapture->expectChildColor(10, 10);
mCapture->expectChildColor(19, 19);
@@ -1949,6 +4413,32 @@
}
}
+// A child with a scale transform should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
+ asTransaction([&](Transaction& t) {
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setPosition(mChild, 0, 0);
+ });
+
+ // Find the boundary between the parent and child.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+ mCapture->expectFGColor(10, 10);
+ }
+
+ asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
+
+ // The child should fill its parent bounds and be cropped by it.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(63, 63);
+ mCapture->expectBGColor(64, 64);
+ }
+}
+
TEST_F(ChildLayerTest, ChildLayerAlpha) {
fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
@@ -1962,7 +4452,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Unblended child color
mCapture->checkPixel(0, 0, 0, 254, 0);
}
@@ -1970,7 +4460,7 @@
asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Child and BG blended.
mCapture->checkPixel(0, 0, 127, 127, 0);
}
@@ -1978,7 +4468,7 @@
asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Child and BG blended.
mCapture->checkPixel(0, 0, 95, 64, 95);
}
@@ -1992,7 +4482,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// But 10 pixels in we should see the child surface
@@ -2006,7 +4496,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectFGColor(64, 64);
// In reparenting we should have exposed the entire foreground surface.
mCapture->expectFGColor(74, 74);
@@ -2017,6 +4507,36 @@
}
}
+TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
+ sp<SurfaceControl> mGrandChild =
+ createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+ {
+ SCOPED_TRACE("Grandchild visible");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->checkPixel(64, 64, 111, 111, 111);
+ }
+
+ mChild.clear();
+
+ {
+ SCOPED_TRACE("After destroying child");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->expectFGColor(64, 64);
+ }
+
+ asTransaction([&](Transaction& t) {
+ t.reparent(mGrandChild, mFGSurfaceControl->getHandle());
+ });
+
+ {
+ SCOPED_TRACE("After reparenting grandchild");
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->checkPixel(64, 64, 111, 111, 111);
+ }
+}
+
TEST_F(ChildLayerTest, DetachChildrenSameClient) {
asTransaction([&](Transaction& t) {
t.show(mChild);
@@ -2025,7 +4545,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// But 10 pixels in we should see the child surface
@@ -2034,6 +4554,7 @@
mCapture->expectFGColor(84, 84);
}
+
asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
asTransaction([&](Transaction& t) { t.hide(mChild); });
@@ -2041,7 +4562,7 @@
// Since the child has the same client as the parent, it will not get
// detached and will be hidden.
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectFGColor(64, 64);
mCapture->expectFGColor(74, 74);
mCapture->expectFGColor(84, 84);
@@ -2051,10 +4572,9 @@
TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
sp<SurfaceControl> mChildNewClient =
- mNewComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- ASSERT_TRUE(mChildNewClient != nullptr);
ASSERT_TRUE(mChildNewClient->isValid());
fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
@@ -2067,7 +4587,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// But 10 pixels in we should see the child surface
@@ -2082,13 +4602,68 @@
// Nothing should have changed.
{
- ScreenCapture::captureScreen(&mCapture);
+ 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());
+
+ 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, ChildrenInheritNonTransformScalingFromParent) {
asTransaction([&](Transaction& t) {
t.show(mChild);
@@ -2097,11 +4672,11 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// We've positioned the child in the top left.
mCapture->expectChildColor(0, 0);
- // But it's only 10x10.
- mCapture->expectFGColor(10, 10);
+ // But it's only 10x15.
+ mCapture->expectFGColor(10, 15);
}
asTransaction([&](Transaction& t) {
@@ -2111,13 +4686,13 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// We've positioned the child in the top left.
mCapture->expectChildColor(0, 0);
mCapture->expectChildColor(10, 10);
- mCapture->expectChildColor(19, 19);
- // And now it should be scaled all the way to 20x20
- mCapture->expectFGColor(20, 20);
+ mCapture->expectChildColor(19, 29);
+ // And now it should be scaled all the way to 20x30
+ mCapture->expectFGColor(20, 30);
}
}
@@ -2130,11 +4705,12 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// We've positioned the child in the top left.
mCapture->expectChildColor(0, 0);
- // But it's only 10x10.
- mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(9, 14);
+ // But it's only 10x15.
+ mCapture->expectFGColor(10, 15);
}
// We set things up as in b/37673612 so that there is a mismatch between the buffer size and
// the WM specified state size.
@@ -2149,25 +4725,133 @@
{
// The child should still be in the same place and not have any strange scaling as in
// b/37673612.
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(0, 0);
mCapture->expectFGColor(10, 10);
}
}
+// A child with a buffer transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setSize(mChild, 100, 100);
+ });
+ fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ {
+ mCapture = screenshot();
+
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(63, 63);
+ mCapture->expectBGColor(64, 64);
+ }
+
+ asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ // Apply a 90 transform on the buffer.
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 64, 128);
+ fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+ waitForPostedBuffers();
+
+ // The child should be cropped by the new parent bounds.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(99, 63);
+ mCapture->expectFGColor(100, 63);
+ mCapture->expectBGColor(128, 64);
+ }
+}
+
+// A child with a scale transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ t.setSize(mChild, 200, 200);
+ });
+ fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+ {
+ mCapture = screenshot();
+
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(63, 63);
+ mCapture->expectBGColor(64, 64);
+ }
+
+ asTransaction([&](Transaction& t) {
+ t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // Set a scaling by 2.
+ t.setSize(mFGSurfaceControl, 128, 128);
+ });
+
+ // Child should inherit its parents scale but should be cropped by its parent bounds.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(127, 127);
+ mCapture->expectBGColor(128, 128);
+ }
+}
+
+// Regression test for b/127368943
+// Child should ignore the buffer transform but apply parent scale transform.
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
+ asTransaction([&](Transaction& t) {
+ t.show(mChild);
+ t.setPosition(mChild, 0, 0);
+ t.setPosition(mFGSurfaceControl, 0, 0);
+ });
+
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 14);
+ mCapture->expectFGColor(10, 15);
+ }
+
+ // Change the size of the foreground to 128 * 64 so we can test rotation as well.
+ asTransaction([&](Transaction& t) {
+ t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ t.setSize(mFGSurfaceControl, 128, 64);
+ });
+ sp<Surface> s = mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
+ // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 32, 64);
+ fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+ waitForPostedBuffers();
+
+ // The child should ignore the buffer transform but apply the 2.0 scale from parent.
+ {
+ mCapture = screenshot();
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(19, 29);
+ mCapture->expectFGColor(20, 30);
+ }
+}
+
TEST_F(ChildLayerTest, Bug36858924) {
// Destroy the child layer
mChild.clear();
// Now recreate it as hidden
- mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
- mFGSurfaceControl.get());
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
// Show the child layer in a deferred transaction
asTransaction([&](Transaction& t) {
- t.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber());
+ t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber());
t.show(mChild);
});
@@ -2194,7 +4878,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// But 10 pixels in we should see the child surface
@@ -2206,7 +4890,7 @@
asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectFGColor(64, 64);
// In reparenting we should have exposed the entire foreground surface.
mCapture->expectFGColor(74, 74);
@@ -2225,7 +4909,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// But 10 pixels in we should see the child surface
@@ -2235,17 +4919,16 @@
}
asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
{
- ScreenCapture::captureScreen(&mCapture);
- // Nothing should have changed.
+ mCapture = screenshot();
+ // The surface should now be offscreen.
mCapture->expectFGColor(64, 64);
- mCapture->expectChildColor(74, 74);
+ mCapture->expectFGColor(74, 74);
mCapture->expectFGColor(84, 84);
}
}
TEST_F(ChildLayerTest, ReparentFromNoParent) {
- sp<SurfaceControl> newSurface = mComposerClient->createSurface(String8("New Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0);
+ sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
ASSERT_TRUE(newSurface != nullptr);
ASSERT_TRUE(newSurface->isValid());
@@ -2259,7 +4942,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Top left of foreground must now be visible
mCapture->expectFGColor(64, 64);
// At 10, 10 we should see the new surface
@@ -2269,7 +4952,7 @@
asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
// mFGSurface, putting it at 74, 74.
mCapture->expectFGColor(64, 64);
@@ -2279,13 +4962,12 @@
}
TEST_F(ChildLayerTest, NestedChildren) {
- sp<SurfaceControl> grandchild =
- mComposerClient->createSurface(String8("Grandchild surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
fillSurfaceRGBA8(grandchild, 50, 50, 50);
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
// which begins at 64, 64
mCapture->checkPixel(64, 64, 50, 50, 50);
@@ -2293,8 +4975,7 @@
}
TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
- sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 128,
- 128, PIXEL_FORMAT_RGBA_8888, 0);
+ sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
fillSurfaceRGBA8(relative, 255, 255, 255);
Transaction t;
@@ -2306,13 +4987,214 @@
// We expect that the child should have been elevated above our
// INT_MAX layer even though it's not a child of it.
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(0, 0);
mCapture->expectChildColor(9, 9);
mCapture->checkPixel(10, 10, 255, 255, 255);
}
}
+class BoundlessLayerTest : public LayerUpdateTest {
+protected:
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+// Verify setting a size on a buffer layer has no effect.
+TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
+ sp<SurfaceControl> bufferLayer =
+ createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
+ ASSERT_TRUE(bufferLayer->isValid());
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
+ asTransaction([&](Transaction& t) { t.show(bufferLayer); });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
+ // Buffer layer should not extend past buffer bounds
+ mCapture->expectFGColor(95, 95);
+ }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
+// which will crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
+// a crop which will be used to crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
+ sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
+ ASSERT_TRUE(cropLayer->isValid());
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(cropLayer);
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // 5 pixels from the foreground we should see the child surface
+ mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
+ // 10 pixels from the foreground we should be back to the foreground surface
+ mCapture->expectFGColor(74, 74);
+ }
+}
+
+// Verify for boundless layer with no children, their transforms have no effect.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setPosition(colorLayer, 320, 320);
+ t.setMatrix(colorLayer, 2, 0, 0, 2);
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify for boundless layer with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
+ sp<SurfaceControl> boundlessLayerRightShift =
+ createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
+ ASSERT_TRUE(boundlessLayerRightShift->isValid());
+ sp<SurfaceControl> boundlessLayerDownShift =
+ createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, boundlessLayerRightShift.get());
+ ASSERT_TRUE(boundlessLayerDownShift->isValid());
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setPosition(boundlessLayerRightShift, 32, 0);
+ t.show(boundlessLayerRightShift);
+ t.setPosition(boundlessLayerDownShift, 0, 32);
+ t.show(boundlessLayerDownShift);
+ t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify child layers do not get clipped if they temporarily move into the negative
+// coordinate space as the result of an intermediate transformation.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
+ sp<SurfaceControl> boundlessLayer =
+ mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
+ ASSERT_TRUE(boundlessLayer != nullptr);
+ ASSERT_TRUE(boundlessLayer->isValid());
+ sp<SurfaceControl> colorLayer =
+ mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
+ ASSERT_TRUE(colorLayer != nullptr);
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ // shift child layer off bounds. If this layer was not boundless, we will
+ // expect the child layer to be cropped.
+ t.setPosition(boundlessLayer, 32, 32);
+ t.show(boundlessLayer);
+ t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ // undo shift by parent
+ t.setPosition(colorLayer, -32, -32);
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(129, 129);
+ }
+}
+
+// Verify for boundless root layers with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
+ sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
+ PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+ ASSERT_TRUE(rootBoundlessLayer->isValid());
+ sp<SurfaceControl> colorLayer =
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
+
+ ASSERT_TRUE(colorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
+ t.setPosition(rootBoundlessLayer, 32, 32);
+ t.show(rootBoundlessLayer);
+ t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+ t.setColor(colorLayer, half3{0, 0, 0});
+ t.show(colorLayer);
+ t.hide(mFGSurfaceControl);
+ });
+ {
+ mCapture = screenshot();
+ // Top left of background must now be visible
+ mCapture->expectBGColor(0, 0);
+ // Top left of foreground must now be visible
+ mCapture->expectBGColor(31, 31);
+ // Foreground Surface bounds must be color layer
+ mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
+ // Color layer should not extend past foreground bounds
+ mCapture->expectBGColor(97, 97);
+ }
+}
+
class ScreenCaptureTest : public LayerUpdateTest {
protected:
std::unique_ptr<ScreenCapture> mCapture;
@@ -2329,9 +5211,8 @@
TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -2345,9 +5226,8 @@
TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -2359,9 +5239,8 @@
}
TEST_F(ScreenCaptureTest, CaptureTransparent) {
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
@@ -2379,11 +5258,10 @@
TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
- sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 10,
- 10, PIXEL_FORMAT_RGBA_8888, 0);
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+ sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
fillSurfaceRGBA8(child, 200, 200, 200);
fillSurfaceRGBA8(relative, 100, 100, 100);
@@ -2403,12 +5281,10 @@
TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
- sp<SurfaceControl> relative =
- mComposerClient->createSurface(String8("Relative surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
fillSurfaceRGBA8(relative, 100, 100, 100);
@@ -2437,71 +5313,75 @@
void SetUp() override {
LayerUpdateTest::SetUp();
- mChild =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
fillSurfaceRGBA8(mChild, 200, 200, 200);
SurfaceComposerClient::Transaction().show(mChild).apply(true);
}
- void verify() {
+ void verify(std::function<void()> verifyStartingState) {
+ // Verify starting state before a screenshot is taken.
+ verifyStartingState();
+
+ // Verify child layer does not inherit any of the properties of its
+ // parent when its screenshot is captured.
auto fgHandle = mFGSurfaceControl->getHandle();
ScreenCapture::captureChildLayers(&mCapture, fgHandle);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->expectChildColor(0, 0);
+
+ // Verify all assumptions are still true after the screenshot is taken.
+ verifyStartingState();
}
std::unique_ptr<ScreenCapture> mCapture;
sp<SurfaceControl> mChild;
};
+// Regression test b/76099859
TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
// Even though the parent is hidden we should still capture the child.
- verify();
+
+ // Before and after reparenting, verify child is properly hidden
+ // when rendering full-screen.
+ verify([&] { screenshot()->expectBGColor(64, 64); });
}
TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-
- SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
+ SurfaceComposerClient::Transaction()
+ .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
+ .apply(true);
// Even though the parent is cropped out we should still capture the child.
- verify();
+
+ // Before and after reparenting, verify child is cropped by parent.
+ verify([&] { screenshot()->expectBGColor(65, 65); });
}
+// Regression test b/124372894
TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
-
- SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2);
+ SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
// We should not inherit the parent scaling.
- verify();
-}
-TEST_F(ScreenCaptureChildOnlyTest, RegressionTest76099859) {
- SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
- // Even though the parent is hidden we should still capture the child.
- verify();
-
- // Verify everything was properly hidden when rendering the full-screen.
- screenshot()->expectBGColor(0,0);
+ // Before and after reparenting, verify child is properly scaled.
+ verify([&] { screenshot()->expectChildColor(80, 80); });
}
TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> grandchild =
- mComposerClient->createSurface(String8("Grandchild surface"), 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
fillSurfaceRGBA8(grandchild, 50, 50, 50);
SurfaceComposerClient::Transaction()
@@ -2518,9 +5398,8 @@
}
TEST_F(ScreenCaptureTest, CaptureChildOnly) {
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
auto childHandle = child->getHandle();
@@ -2533,15 +5412,13 @@
}
TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
- sp<SurfaceControl> child =
- mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
auto childHandle = child->getHandle();
- sp<SurfaceControl> grandchild =
- mComposerClient->createSurface(String8("Grandchild surface"), 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
fillSurfaceRGBA8(grandchild, 50, 50, 50);
SurfaceComposerClient::Transaction()
@@ -2559,14 +5436,12 @@
}
TEST_F(ScreenCaptureTest, CaptureCrop) {
- sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60,
- PIXEL_FORMAT_RGBA_8888, 0);
- sp<SurfaceControl> blueLayer =
- mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
- 0, redLayer.get());
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
SurfaceComposerClient::Transaction()
.setLayer(redLayer, INT32_MAX - 1)
@@ -2584,7 +5459,7 @@
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- Rect crop = Rect(0, 0, 30, 30);
+ const Rect crop = Rect(0, 0, 30, 30);
ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
// Capturing the cropped screen, cropping out the shown red area, should leave only the blue
// area visible.
@@ -2593,14 +5468,12 @@
}
TEST_F(ScreenCaptureTest, CaptureSize) {
- sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60,
- PIXEL_FORMAT_RGBA_8888, 0);
- sp<SurfaceControl> blueLayer =
- mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
- 0, redLayer.get());
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED));
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
SurfaceComposerClient::Transaction()
.setLayer(redLayer, INT32_MAX - 1)
@@ -2629,13 +5502,12 @@
}
TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
- sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60,
- PIXEL_FORMAT_RGBA_8888, 0);
+ sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
auto redLayerHandle = redLayer->getHandle();
- mComposerClient->destroySurface(redLayerHandle);
+ redLayer.clear();
SurfaceComposerClient::Transaction().apply(true);
sp<GraphicBuffer> outBuffer;
@@ -2651,9 +5523,9 @@
void SetUp() override {
LayerTransactionTest::SetUp();
bgLayer = createLayer("BG layer", 20, 20);
- fillLayerColor(bgLayer, Color::RED);
+ fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
fgLayer = createLayer("FG layer", 20, 20);
- fillLayerColor(fgLayer, Color::BLUE);
+ fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
{
SCOPED_TRACE("before anything");
@@ -2690,4 +5562,162 @@
}
}
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+ }
+
+ virtual void TearDown() {
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ LayerTransactionTest::TearDown();
+ mColorLayer = 0;
+ }
+
+ void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+ mVirtualDisplay =
+ SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+ asTransaction([&](Transaction& t) {
+ t.setDisplaySurface(mVirtualDisplay, mProducer);
+ t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+ t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
+ Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
+ });
+ }
+
+ void createColorLayer(uint32_t layerStack) {
+ mColorLayer =
+ createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ ASSERT_TRUE(mColorLayer != nullptr);
+ ASSERT_TRUE(mColorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setLayerStack(mColorLayer, layerStack);
+ t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+ t.setLayer(mColorLayer, INT32_MAX - 2);
+ t.setColor(mColorLayer,
+ half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+ mExpectedColor.b / 255.0f});
+ t.show(mColorLayer);
+ });
+ }
+
+ DisplayInfo mMainDisplayInfo;
+ sp<IBinder> mMainDisplay;
+ sp<IBinder> mVirtualDisplay;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<SurfaceControl> mColorLayer;
+ Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+ createColorLayer(1 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer does not render on main display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ // Verify color layer renders correctly on virtual display.
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+ // Create a display and set its layer stack to the main display's layer stack so
+ // the contents of the main display are mirrored on to the virtual display.
+
+ // Assumption here is that the new mirrored display has the same viewport as the
+ // primary display that it is mirroring.
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
+ createColorLayer(0 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer renders correctly on main display and it is mirrored on the
+ // virtual display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
+class DisplayActiveConfigTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+ SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs);
+ EXPECT_GT(mDisplayconfigs.size(), 0);
+
+ // set display power to on to make sure config can be changed
+ SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL);
+ }
+
+ sp<IBinder> mDisplayToken;
+ Vector<DisplayInfo> mDisplayconfigs;
+};
+
+TEST_F(DisplayActiveConfigTest, allConfigsAllowed) {
+ std::vector<int32_t> allowedConfigs;
+
+ // Add all configs to the allowed configs
+ for (int i = 0; i < mDisplayconfigs.size(); i++) {
+ allowedConfigs.push_back(i);
+ }
+
+ status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
+ EXPECT_EQ(res, NO_ERROR);
+
+ std::vector<int32_t> outConfigs;
+ res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(allowedConfigs, outConfigs);
+}
+
+TEST_F(DisplayActiveConfigTest, changeAllowedConfig) {
+ // we need at least 2 configs available for this test
+ if (mDisplayconfigs.size() <= 1) return;
+
+ int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
+
+ // We want to set the allowed config to everything but the active config
+ std::vector<int32_t> allowedConfigs;
+ for (int i = 0; i < mDisplayconfigs.size(); i++) {
+ if (i != activeConfig) {
+ allowedConfigs.push_back(i);
+ }
+ }
+
+ status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
+ EXPECT_EQ(res, NO_ERROR);
+
+ // Allow some time for the config change
+ std::this_thread::sleep_for(200ms);
+
+ int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
+ EXPECT_NE(activeConfig, newActiveConfig);
+
+ // Make sure the new config is part of allowed config
+ EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) !=
+ allowedConfigs.end());
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 6ad1b87..a2c0611 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -11,6 +11,7 @@
shared_libs: [
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
"android.hardware.power@1.3",
"libbase",
"libbinder",
@@ -30,8 +31,9 @@
"libutils",
],
static_libs: [
+ "libgmock",
+ "librenderengine",
"libtrace_proto",
- "libgmock"
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 9b31985..f9e0b64 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -145,7 +145,7 @@
void TearDown() override;
void waitForDisplayTransaction();
- bool waitForHotplugEvent(uint32_t id, bool connected);
+ bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected);
sp<IComposer> mFakeService;
sp<SurfaceComposerClient> mComposerClient;
@@ -242,7 +242,7 @@
mMockComposer->runVSyncAndWait();
}
-bool DisplayTest::waitForHotplugEvent(uint32_t id, bool connected) {
+bool DisplayTest::waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
@@ -250,11 +250,12 @@
mReceivedDisplayEvents.pop_front();
ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
- "event hotplug: id %d, connected %d\t", event.header.id,
- event.hotplug.connected);
+ "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", connected %d\t",
+ event.header.displayId, event.hotplug.connected);
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
- event.header.id == id && event.hotplug.connected == connected) {
+ event.header.displayId == displayId && event.hotplug.connected == connected) {
return true;
}
}
@@ -294,13 +295,14 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
@@ -328,14 +330,15 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, false));
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
ASSERT_EQ(400u, info.w);
ASSERT_EQ(200u, info.h);
@@ -364,11 +367,12 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, false));
+ EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
DisplayInfo info;
auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
EXPECT_NE(NO_ERROR, result);
@@ -402,11 +406,12 @@
waitForDisplayTransaction();
- EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, true));
+ EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
{
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
DisplayInfo info;
auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
EXPECT_EQ(NO_ERROR, result);
@@ -473,10 +478,11 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
ALOGI("TransactionTest::SetUp - display");
- sp<android::IBinder> display(
- SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(display, &info);
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
mDisplayWidth = info.w;
mDisplayHeight = info.h;
@@ -624,7 +630,7 @@
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
- ts.setCrop(mFGSurfaceControl, cropRect);
+ ts.setCrop_legacy(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
@@ -634,40 +640,6 @@
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
-TEST_F(TransactionTest, LayerFinalCrop) {
- // TODO: Add scaling to confirm that crop happens in display space?
- {
- TransactionScope ts(*sFakeComposer);
- Rect cropRect(32, 32, 32 + 64, 32 + 64);
- ts.setFinalCrop(mFGSurfaceControl, cropRect);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- // In display space we are cropping with [32, 32, 96, 96] against display rect
- // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
-
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, LayerFinalCropEmpty) {
- // TODO: Add scaling to confirm that crop happens in display space?
- {
- TransactionScope ts(*sFakeComposer);
- Rect cropRect(16, 16, 32, 32);
- ts.setFinalCrop(mFGSurfaceControl, cropRect);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- // In display space we are cropping with [16, 16, 32, 32] against display rect
- // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
- std::vector<RenderState> referenceFrame(1);
- referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
TEST_F(TransactionTest, LayerSetLayer) {
{
TransactionScope ts(*sFakeComposer);
@@ -847,18 +819,16 @@
{
TransactionScope ts(*sFakeComposer);
ts.setAlpha(mFGSurfaceControl, 0.75);
- ts.deferTransactionUntil(mFGSurfaceControl,
- syncSurfaceControl->getHandle(),
- syncSurfaceControl->getSurface()->getNextFrameNumber());
+ ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber());
}
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(mFGSurfaceControl, 128, 128);
- ts.deferTransactionUntil(mFGSurfaceControl,
- syncSurfaceControl->getHandle(),
- syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+ ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
}
EXPECT_EQ(4, sFakeComposer->getFrameCount());
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
@@ -981,7 +951,7 @@
ts.show(mChild);
ts.setPosition(mChild, 0, 0);
ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
+ ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
}
// NOTE: The foreground surface would be occluded by the child
// now, but is included in the stack because the child is
@@ -994,22 +964,6 @@
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
}
-TEST_F(ChildLayerTest, FinalCropping) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
- }
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
TEST_F(ChildLayerTest, Constraints) {
{
TransactionScope ts(*sFakeComposer);
@@ -1095,7 +1049,7 @@
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
-TEST_F(ChildLayerTest, DetachChildren) {
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
@@ -1111,14 +1065,59 @@
{
TransactionScope ts(*sFakeComposer);
+ ts.setPosition(mFGSurfaceControl, 0, 0);
ts.detachChildren(mFGSurfaceControl);
}
{
TransactionScope ts(*sFakeComposer);
+ ts.setPosition(mFGSurfaceControl, 64, 64);
ts.hide(mChild);
}
+ std::vector<RenderState> refFrame(2);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ refFrame[FG_LAYER] = mBaseFrame[FG_LAYER];
+
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
+ 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());
+ fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.hide(mChild);
+ ts.show(childNewClient);
+ ts.setPosition(childNewClient, 10, 10);
+ ts.setPosition(mFGSurfaceControl, 64, 64);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.detachChildren(mFGSurfaceControl);
+ ts.setPosition(mFGSurfaceControl, 0, 0);
+ }
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setPosition(mFGSurfaceControl, 64, 64);
+ ts.setPosition(childNewClient, 0, 0);
+ ts.hide(childNewClient);
+ }
+
// Nothing should have changed. The child control becomes a no-op
// zombie on detach. See comments for detachChildren in the
// SurfaceControl.h file.
@@ -1193,8 +1192,8 @@
// Show the child layer in a deferred transaction
{
TransactionScope ts(*sFakeComposer);
- ts.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber());
+ ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber());
ts.show(mChild);
}
@@ -1217,6 +1216,82 @@
sFakeComposer->runVSyncAndWait();
}
+class ChildColorLayerTest : public ChildLayerTest {
+protected:
+ void SetUp() override {
+ TransactionTest::SetUp();
+ mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor,
+ mFGSurfaceControl.get());
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setColor(mChild,
+ {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
+ ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10));
+ }
+
+ sFakeComposer->runVSyncAndWait();
+ mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+ mBaseFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
+ mBaseFrame[CHILD_LAYER].mSwapCount = 0;
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+ }
+};
+
+TEST_F(ChildColorLayerTest, LayerAlpha) {
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 0, 0);
+ ts.setPosition(mFGSurfaceControl, 0, 0);
+ ts.setAlpha(mChild, 0.5);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setAlpha(mFGSurfaceControl, 0.5);
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
+ referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildColorLayerTest, LayerZeroAlpha) {
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 0, 0);
+ ts.setPosition(mFGSurfaceControl, 0, 0);
+ ts.setAlpha(mChild, 0.5);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setAlpha(mFGSurfaceControl, 0.0f);
+ }
+
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
class LatchingTest : public TransactionTest {
protected:
void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
@@ -1235,8 +1310,7 @@
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 64, 64);
ts.setPosition(mFGSurfaceControl, 64, 64);
- ts.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
- ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
+ ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
}
};
@@ -1280,7 +1354,7 @@
{
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63));
+ ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
}
auto referenceFrame1 = mBaseFrame;
@@ -1294,7 +1368,7 @@
TransactionScope ts(*sFakeComposer);
ts.setSize(mFGSurfaceControl, 128, 128);
ts.setGeometryAppliesWithResize(mFGSurfaceControl);
- ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63));
+ ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
}
EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
@@ -1307,111 +1381,6 @@
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
-TEST_F(LatchingTest, FinalCropLatching) {
- // Normally the crop applies immediately even while a resize is pending.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- }
-
- auto referenceFrame1 = mBaseFrame;
- referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
- referenceFrame1[FG_LAYER].mSourceCrop =
- hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
- EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
- restoreInitialState();
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setGeometryAppliesWithResize(mFGSurfaceControl);
- ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- }
- EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
- completeFGResize();
-
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
- referenceFrame2[FG_LAYER].mSourceCrop =
- hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
- referenceFrame2[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-}
-
-// In this test we ensure that setGeometryAppliesWithResize actually demands
-// a buffer of the new size, and not just any size.
-TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
- // Normally the crop applies immediately even while a resize is pending.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- }
-
- auto referenceFrame1 = mBaseFrame;
- referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
- referenceFrame1[FG_LAYER].mSourceCrop =
- hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
- EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
- restoreInitialState();
-
- // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
- // initiating the resize.
- lockAndFillFGBuffer();
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setGeometryAppliesWithResize(mFGSurfaceControl);
- ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- }
- EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
- // We now submit our old buffer, at the old size, and ensure it doesn't
- // trigger geometry latching.
- unlockFGBuffer();
-
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-
- completeFGResize();
- auto referenceFrame3 = referenceFrame2;
- referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
- referenceFrame3[FG_LAYER].mSourceCrop =
- hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
- referenceFrame3[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
- // In this scenario, we attempt to set the final crop a second time while the resize
- // is still pending, and ensure we are successful. Success meaning the second crop
- // is the one which eventually latches and not the first.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setGeometryAppliesWithResize(mFGSurfaceControl);
- ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127));
- }
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1));
- }
- EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
- completeFGResize();
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
} // namespace
int main(int argc, char** argv) {
diff --git a/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp b/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp
new file mode 100644
index 0000000..4274254
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include "AllowedDisplayConfigs.h"
+
+namespace android {
+namespace {
+
+class AllowedDisplayConfigsTest : public testing::Test {
+protected:
+ AllowedDisplayConfigsTest();
+ ~AllowedDisplayConfigsTest() override;
+
+ void buildAllowedConfigs();
+
+ const std::vector<int32_t> expectedConfigs = {0, 2, 7, 129};
+ constexpr static int32_t notAllowedConfig = 5;
+ std::unique_ptr<const AllowedDisplayConfigs> mAllowedConfigs;
+};
+
+AllowedDisplayConfigsTest::AllowedDisplayConfigsTest() {
+ 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());
+}
+
+AllowedDisplayConfigsTest::~AllowedDisplayConfigsTest() {
+ 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 AllowedDisplayConfigsTest::buildAllowedConfigs() {
+ AllowedDisplayConfigs::Builder builder;
+ for (int config : expectedConfigs) {
+ builder.addConfig(config);
+ }
+ mAllowedConfigs = builder.build();
+}
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(AllowedDisplayConfigsTest, checkConfigs) {
+ buildAllowedConfigs();
+
+ // Check that all expected configs are allowed
+ for (int config : expectedConfigs) {
+ EXPECT_TRUE(mAllowedConfigs->isConfigAllowed(config));
+ }
+
+ // Check that all the allowed configs are expected
+ std::vector<int32_t> allowedConfigVector;
+ mAllowedConfigs->getAllowedConfigs(&allowedConfigVector);
+ EXPECT_EQ(allowedConfigVector, expectedConfigs);
+
+ // Check that notAllowedConfig is indeed not allowed
+ EXPECT_TRUE(std::find(expectedConfigs.begin(), expectedConfigs.end(), notAllowedConfig) ==
+ expectedConfigs.end());
+ EXPECT_FALSE(mAllowedConfigs->isConfigAllowed(notAllowedConfig));
+}
+
+TEST_F(AllowedDisplayConfigsTest, getAllowedConfigsNullptr) {
+ buildAllowedConfigs();
+
+ // No other expectations rather than no crash
+ mAllowedConfigs->getAllowedConfigs(nullptr);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9949bfa..25ce4ac 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 The Android Open Source Project
+// Copyright 2018 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.
@@ -16,26 +16,58 @@
name: "libsurfaceflinger_unittest",
defaults: ["libsurfaceflinger_defaults"],
test_suites: ["device-tests"],
+ sanitize: {
+ // Using the address sanitizer not only helps uncover issues in the code
+ // covered by the tests, but also covers some of the tricky injection of
+ // fakes the unit tests currently do.
+ //
+ // Note: If you get an runtime link error like:
+ //
+ // CANNOT LINK EXECUTABLE "/data/local/tmp/libsurfaceflinger_unittest": library "libclang_rt.asan-aarch64-android.so" not found
+ //
+ // it is because the address sanitizer shared objects are not installed
+ // by default in the system image.
+ //
+ // You can either "make dist tests" before flashing, or set this
+ // option to false temporarily.
+ address: true,
+ },
srcs: [
":libsurfaceflinger_sources",
+ "libsurfaceflinger_unittest_main.cpp",
+ "AllowedDisplayConfigsTest.cpp",
+ "CompositionTest.cpp",
+ "DispSyncSourceTest.cpp",
+ "DisplayIdentificationTest.cpp",
"DisplayTransactionTest.cpp",
"EventControlThreadTest.cpp",
"EventThreadTest.cpp",
+ "IdleTimerTest.cpp",
+ "LayerHistoryTest.cpp",
+ "LayerMetadataTest.cpp",
+ "SchedulerTest.cpp",
+ "SchedulerUtilsTest.cpp",
+ "RefreshRateStatsTest.cpp",
+ "TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
- "mock/DisplayHardware/MockDisplaySurface.cpp",
+ "mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/gui/MockGraphicBufferConsumer.cpp",
"mock/gui/MockGraphicBufferProducer.cpp",
+ "mock/MockDispSync.cpp",
"mock/MockEventControlThread.cpp",
"mock/MockEventThread.cpp",
"mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockSurfaceInterceptor.cpp",
- "mock/RenderEngine/MockRenderEngine.cpp",
+ "mock/MockTimeStats.cpp",
"mock/system/window/MockNativeWindow.cpp",
],
static_libs: [
"libgmock",
+ "libcompositionengine",
+ "libcompositionengine_mocks",
+ "librenderengine_mocks",
],
header_libs: [
"libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
index 2245ee1..8bed766 100644
--- a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
+++ b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
@@ -75,14 +75,16 @@
template <typename... Args>
class AsyncCallRecorder<void (*)(Args...)> {
public:
- // For the tests, we expect the wait for an expected change to be signaled
- // to be much shorter than this.
- static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};
+ // This wait value needs to be large enough to avoid flakes caused by delays
+ // scheduling threads, but small enough that tests don't take forever if
+ // something really is wrong. Based on some empirical evidence, 100ms should
+ // be enough to avoid the former.
+ static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{100};
- // The wait here is tricky. We don't expect a change, but we don't want to
- // wait forever (or for longer than the typical test function runtime). As
- // even the simplest Google Test can take 1ms (1000us) to run, we wait for
- // half that time.
+ // The wait here is tricky. It's for when We don't expect to record a call,
+ // but we don't want to wait forever (or for longer than the typical test
+ // function runtime). As even the simplest Google Test can take 1ms (1000us)
+ // to run, we wait for half that time.
static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
new file mode 100644
index 0000000..ea2818d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -0,0 +1,1307 @@
+/*
+ * Copyright 2018 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 "CompositionTest"
+
+#include <compositionengine/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/IProducerListener.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+#include <renderengine/mock/Framebuffer.h>
+#include <renderengine/mock/Image.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <system/window.h>
+#include <utils/String8.h>
+
+#include "BufferQueueLayer.h"
+#include "ColorLayer.h"
+#include "Layer.h"
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+namespace {
+
+using testing::_;
+using testing::AtLeast;
+using testing::Between;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Field;
+using testing::Invoke;
+using testing::IsNull;
+using testing::Mock;
+using testing::NotNull;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+
+using android::Hwc2::Error;
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+using android::Hwc2::Transform;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+constexpr hwc2_display_t HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+constexpr hwc2_layer_t HWC_LAYER = 5000;
+constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+
+constexpr int DEFAULT_CONFIG_ID = 0;
+constexpr int DEFAULT_TEXTURE_ID = 6000;
+constexpr int DEFAULT_LAYER_STACK = 7000;
+
+constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500;
+
+constexpr int DEFAULT_SIDEBAND_STREAM = 51;
+
+class CompositionTest : public testing::Test {
+public:
+ CompositionTest() {
+ 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());
+
+ mFlinger.mutableEventQueue().reset(mMessageQueue);
+ setupScheduler();
+
+ EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ setupComposer(0);
+ }
+
+ ~CompositionTest() {
+ 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 setupComposer(int virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getCapabilities())
+ .WillOnce(Return(std::vector<IComposer::Capability>()));
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+ }
+
+ void setupScheduler() {
+ mScheduler = new TestableScheduler();
+ mScheduler->mutableEventControlThread().reset(mEventControlThread);
+ mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
+ EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
+ sp<Scheduler::ConnectionHandle> connectionHandle =
+ mScheduler->addConnection(std::move(mEventThread));
+ mFlinger.mutableSfConnectionHandle() = std::move(connectionHandle);
+
+ mFlinger.mutableScheduler().reset(mScheduler);
+ }
+
+ void setupForceGeometryDirty() {
+ // TODO: This requires the visible region and other related
+ // state to be set, and is problematic for BufferLayers since they are
+ // not visible without a buffer (and setting up a buffer looks like a
+ // pain)
+ // mFlinger.mutableVisibleRegionsDirty() = true;
+
+ mFlinger.mutableGeometryInvalid() = true;
+ }
+
+ template <typename Case>
+ void displayRefreshCompositionDirtyGeometry();
+
+ template <typename Case>
+ void displayRefreshCompositionDirtyFrame();
+
+ template <typename Case>
+ void captureScreenComposition();
+
+ std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream};
+
+ TestableScheduler* mScheduler;
+ TestableSurfaceFlinger mFlinger;
+ sp<DisplayDevice> mDisplay;
+ sp<DisplayDevice> mExternalDisplay;
+ sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+ new compositionengine::mock::DisplaySurface();
+ mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+
+ sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+ ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
+
+ std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
+ mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+
+ Hwc2::mock::Composer* mComposer = nullptr;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+ renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer();
+
+ sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
+
+ sp<GraphicBuffer> mCaptureScreenBuffer;
+};
+
+template <typename LayerCase>
+void CompositionTest::displayRefreshCompositionDirtyGeometry() {
+ setupForceGeometryDirty();
+ LayerCase::setupForDirtyGeometry(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+ mFlinger.onMessageReceived(MessageQueue::REFRESH);
+
+ LayerCase::cleanup(this);
+}
+
+template <typename LayerCase>
+void CompositionTest::displayRefreshCompositionDirtyFrame() {
+ LayerCase::setupForDirtyFrame(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+ mFlinger.onMessageReceived(MessageQueue::REFRESH);
+
+ LayerCase::cleanup(this);
+}
+
+template <typename LayerCase>
+void CompositionTest::captureScreenComposition() {
+ LayerCase::setupForScreenCapture(this);
+
+ const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ constexpr bool useIdentityTransform = true;
+ constexpr bool forSystem = true;
+
+ DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
+ ui::Transform::ROT_0);
+
+ auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
+ return mFlinger.traverseLayersInDisplay(mDisplay, 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(),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+
+ int fd = -1;
+ status_t result =
+ mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+ useIdentityTransform, forSystem, &fd);
+ if (fd >= 0) {
+ close(fd);
+ }
+
+ EXPECT_EQ(NO_ERROR, result);
+
+ LayerCase::cleanup(this);
+}
+
+/* ------------------------------------------------------------------------
+ * Variants for each display configuration which can be tested
+ */
+
+template <typename Derived>
+struct BaseDisplayVariant {
+ static constexpr bool IS_SECURE = true;
+ static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
+
+ static void setupPreconditions(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+
+ FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical,
+ true /* isPrimary */)
+ .setCapabilities(&test->mDefaultCapabilities)
+ .inject(&test->mFlinger, test->mComposer);
+ Mock::VerifyAndClear(test->mComposer);
+
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
+ false /* isVirtual */, true /* isPrimary */)
+ .setDisplaySurface(test->mDisplaySurface)
+ .setNativeWindow(test->mNativeWindow)
+ .setSecure(Derived::IS_SECURE)
+ .setPowerMode(Derived::INIT_POWER_MODE)
+ .inject();
+ Mock::VerifyAndClear(test->mNativeWindow);
+ test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
+ }
+
+ template <typename Case>
+ static void setupCommonCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
+ .Times(1);
+ EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1);
+ EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
+
+ EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+ // TODO: remove once we verify that we can just grab the fence from the
+ // FramebufferSurface.
+ EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
+ return base::unique_fd();
+ }));
+
+ EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
+ EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
+
+ Case::CompositionType::setupHwcSetCallExpectations(test);
+ Case::CompositionType::setupHwcGetCallExpectations(test);
+ }
+
+ template <typename Case>
+ static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillRepeatedly(
+ [](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+ ANativeWindowBuffer*, 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),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ return NO_ERROR;
+ });
+ }
+
+ static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mDisplaySurface, beginFrame(true)).Times(1);
+ }
+
+ static void setupEmptyFrameCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mDisplaySurface, beginFrame(false)).Times(1);
+ }
+
+ static void setupHwcCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mDisplaySurface,
+ prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+ .Times(1);
+ }
+
+ static void setupRECompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mDisplaySurface,
+ prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
+ .Times(1);
+ EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
+ .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
+
+ EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _))
+ .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
+ Return(0)));
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillRepeatedly(
+ [](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
+ ANativeWindowBuffer*, 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),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+ return NO_ERROR;
+ });
+ }
+
+ template <typename Case>
+ static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
+ Case::Layer::setupRECompositionCallExpectations(test);
+ }
+
+ template <typename Case>
+ static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
+ Case::Layer::setupREScreenshotCompositionCallExpectations(test);
+ }
+};
+
+struct DefaultDisplaySetupVariant : public BaseDisplayVariant<DefaultDisplaySetupVariant> {};
+
+struct InsecureDisplaySetupVariant : public BaseDisplayVariant<InsecureDisplaySetupVariant> {
+ static constexpr bool IS_SECURE = false;
+
+ template <typename Case>
+ static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
+ Case::Layer::setupInsecureRECompositionCallExpectations(test);
+ }
+
+ template <typename Case>
+ static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
+ Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test);
+ }
+};
+
+struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDisplaySetupVariant> {
+ static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_OFF;
+
+ template <typename Case>
+ static void setupCommonCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+
+ // TODO: This seems like an unnecessary call if display is powered off.
+ EXPECT_CALL(*test->mComposer,
+ setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
+ .Times(1);
+
+ // TODO: This seems like an unnecessary call if display is powered off.
+ Case::CompositionType::setupHwcSetCallExpectations(test);
+ }
+
+ static void setupHwcCompositionCallExpectations(CompositionTest*) {}
+
+ static void setupRECompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+
+ // TODO: This seems like an unnecessary call if display is powered off.
+ EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
+ .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
+ }
+
+ template <typename Case>
+ static void setupRELayerCompositionCallExpectations(CompositionTest*) {}
+};
+
+/* ------------------------------------------------------------------------
+ * Variants for each layer configuration which can be tested
+ */
+
+template <typename LayerProperties>
+struct BaseLayerProperties {
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr PixelFormat FORMAT = PIXEL_FORMAT_RGBA_8888;
+ static constexpr uint64_t USAGE =
+ GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_SW_WRITE_NEVER;
+ static constexpr android_dataspace DATASPACE = HAL_DATASPACE_UNKNOWN;
+ static constexpr uint32_t SCALING_MODE = 0;
+ static constexpr uint32_t TRANSFORM = 0;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+ static constexpr float COLOR[] = {1.f, 1.f, 1.f, 1.f};
+ static constexpr IComposerClient::BlendMode BLENDMODE =
+ IComposerClient::BlendMode::PREMULTIPLIED;
+
+ static void enqueueBuffer(CompositionTest*, sp<BufferQueueLayer> layer) {
+ auto producer = layer->getProducer();
+
+ IGraphicBufferProducer::QueueBufferOutput qbo;
+ status_t result = producer->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &qbo);
+ if (result != NO_ERROR) {
+ ALOGE("Failed to connect() (%d)", result);
+ return;
+ }
+
+ int slot;
+ sp<Fence> fence;
+ result = producer->dequeueBuffer(&slot, &fence, LayerProperties::WIDTH,
+ LayerProperties::HEIGHT, LayerProperties::FORMAT,
+ LayerProperties::USAGE, nullptr, nullptr);
+ if (result != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ ALOGE("Failed to dequeueBuffer() (%d)", result);
+ return;
+ }
+
+ sp<GraphicBuffer> buffer;
+ result = producer->requestBuffer(slot, &buffer);
+ if (result != NO_ERROR) {
+ ALOGE("Failed to requestBuffer() (%d)", result);
+ return;
+ }
+
+ IGraphicBufferProducer::QueueBufferInput qbi(systemTime(), false /* isAutoTimestamp */,
+ LayerProperties::DATASPACE,
+ Rect(LayerProperties::WIDTH,
+ LayerProperties::HEIGHT),
+ LayerProperties::SCALING_MODE,
+ LayerProperties::TRANSFORM, Fence::NO_FENCE);
+ result = producer->queueBuffer(slot, qbi, &qbo);
+ if (result != NO_ERROR) {
+ ALOGE("Failed to queueBuffer (%d)", result);
+ return;
+ }
+ }
+
+ static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
+ // TODO: Eliminate the complexity of actually creating a buffer
+ EXPECT_CALL(*test->mRenderEngine, getMaxTextureSize()).WillOnce(Return(16384));
+ EXPECT_CALL(*test->mRenderEngine, getMaxViewportDims()).WillOnce(Return(16384));
+ status_t err =
+ layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
+ LayerProperties::FORMAT);
+ ASSERT_EQ(NO_ERROR, err);
+ Mock::VerifyAndClear(test->mRenderEngine);
+
+ EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ enqueueBuffer(test, layer);
+ Mock::VerifyAndClear(test->mMessageQueue);
+
+ EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
+ bool ignoredRecomputeVisibleRegions;
+ layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
+ Mock::VerifyAndClear(test->mRenderEngine);
+ }
+
+ static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+ setupLatchedBuffer(test, layer);
+ }
+
+ static void setupBufferLayerPostFrameCallExpectations(CompositionTest* test) {
+ // BufferLayer::onPostComposition(), when there is no present fence
+ EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY, _))
+ .WillOnce(DoAll(SetArgPointee<1>(DEFAULT_CONFIG_ID), Return(Error::NONE)));
+ }
+
+ static void setupHwcSetGeometryCallExpectations(CompositionTest* test) {
+ // TODO: Coverage of other values
+ EXPECT_CALL(*test->mComposer,
+ setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
+ .Times(1);
+ // TODO: Coverage of other values for origin
+ EXPECT_CALL(*test->mComposer,
+ setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
+ IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
+ LayerProperties::HEIGHT})))
+ .Times(1);
+ EXPECT_CALL(*test->mComposer,
+ setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
+ .Times(1);
+ // TODO: Coverage of other values
+ EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
+ // TODO: Coverage of other values
+ EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
+
+ // These expectations retire on saturation as the code path these
+ // expectations are for appears to make an extra call to them.
+ // TODO: Investigate this extra call
+ EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
+ .Times(AtLeast(1))
+ .RetiresOnSaturation();
+ }
+
+ static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+ IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
+ LayerProperties::HEIGHT})))
+ .Times(1);
+ }
+
+ static void setupHwcSetSourceCropColorCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+ IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
+ .Times(1);
+ }
+
+ static void setupHwcSetPerFrameCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
+ std::vector<IComposerClient::Rect>({IComposerClient::Rect(
+ {0, 0, LayerProperties::WIDTH,
+ LayerProperties::HEIGHT})})))
+ .Times(1);
+ }
+
+ static void setupHwcSetPerFrameColorCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+
+ // TODO: use COLOR
+ EXPECT_CALL(*test->mComposer,
+ setLayerColor(HWC_DISPLAY, HWC_LAYER,
+ IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+ .Times(1);
+ }
+
+ static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+
+ setupBufferLayerPostFrameCallExpectations(test);
+ }
+
+ static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ ANativeWindowBuffer*, 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),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ // screen capture adds an additional color layer as an alpha
+ // prefill, so gtet the back layer.
+ renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
+ EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
+ EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
+ EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
+ EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
+ EXPECT_EQ(false, layer.source.buffer.isOpaque);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+ return NO_ERROR;
+ });
+ }
+
+ static void setupREBufferCompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+ }
+
+ static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
+ setupREBufferCompositionCallExpectations(test);
+ }
+
+ static void setupREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+ }
+
+ static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+ }
+
+ static void setupREColorCompositionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ ANativeWindowBuffer*, 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),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ // screen capture adds an additional color layer as an alpha
+ // prefill, so get the back layer.
+ renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+ EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+ LayerProperties::COLOR[2]),
+ layer.source.solidColor);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+ return NO_ERROR;
+ });
+ }
+
+ static void setupREColorScreenshotCompositionCallExpectations(CompositionTest* test) {
+ setupREColorCompositionCallExpectations(test);
+ }
+};
+
+struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {};
+
+struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {};
+
+struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> {
+ using Base = BaseLayerProperties<SidebandLayerProperties>;
+ static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
+
+ static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+ sp<NativeHandle> stream =
+ NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
+ false);
+ test->mFlinger.setLayerSidebandStream(layer, stream);
+ }
+
+ static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+ IComposerClient::FRect({0.f, 0.f, -1.f, -1.f})))
+ .Times(1);
+ }
+
+ static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerSidebandStream(HWC_DISPLAY, HWC_LAYER,
+ reinterpret_cast<native_handle_t*>(
+ DEFAULT_SIDEBAND_STREAM)))
+ .WillOnce(Return(Error::NONE));
+
+ EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+ }
+
+ static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
+};
+
+struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
+ using Base = BaseLayerProperties<SecureLayerProperties>;
+
+ static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+
+ static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ ANativeWindowBuffer*, 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),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ // screen capture adds an additional color layer as an alpha
+ // prefill, so get the back layer.
+ renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(1.0f, layer.alpha);
+ return NO_ERROR;
+ });
+ }
+
+ static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
+ setupInsecureREBufferCompositionCommonCallExpectations(test);
+ Base::setupBufferLayerPostFrameCallExpectations(test);
+ }
+
+ static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) {
+ setupInsecureREBufferCompositionCommonCallExpectations(test);
+ }
+};
+
+struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
+ using Base = BaseLayerProperties<CursorLayerProperties>;
+
+ static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+ Base::setupLayerState(test, layer);
+ test->mFlinger.setLayerPotentialCursor(layer, true);
+ }
+};
+
+struct NoLayerVariant {
+ using FlingerLayerType = sp<BufferQueueLayer>;
+
+ static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
+ static void injectLayer(CompositionTest*, FlingerLayerType) {}
+ static void cleanupInjectedLayers(CompositionTest*) {}
+
+ static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+ static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
+template <typename LayerProperties>
+struct BaseLayerVariant {
+ template <typename L, typename F>
+ static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
+ EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(0);
+
+ sp<L> layer = factory();
+
+ Mock::VerifyAndClear(test->mComposer);
+ Mock::VerifyAndClear(test->mRenderEngine);
+ Mock::VerifyAndClear(test->mMessageQueue);
+
+ auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+ layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
+ layerDrawingState.active.w = 100;
+ layerDrawingState.active.h = 100;
+ layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+ LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
+ layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
+ layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
+
+ return layer;
+ }
+
+ static void injectLayer(CompositionTest* test, sp<Layer> layer) {
+ EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
+ .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
+
+ std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers;
+ outputLayers.emplace_back(test->mDisplay->getCompositionDisplay()
+ ->getOrCreateOutputLayer(DEFAULT_DISPLAY_ID,
+ layer->getCompositionLayer(),
+ layer));
+
+ test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ Mock::VerifyAndClear(test->mComposer);
+
+ Vector<sp<Layer>> layers;
+ layers.add(layer);
+ test->mDisplay->setVisibleLayersSortedByZ(layers);
+ test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+ }
+
+ static void cleanupInjectedLayers(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
+ .WillOnce(Return(Error::NONE));
+
+ test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(
+ std::vector<std::unique_ptr<compositionengine::OutputLayer>>());
+ test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+ }
+};
+
+template <typename LayerProperties>
+struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> {
+ using Base = BaseLayerVariant<LayerProperties>;
+ using FlingerLayerType = sp<ColorLayer>;
+
+ static FlingerLayerType createLayer(CompositionTest* test) {
+ FlingerLayerType layer = Base::template createLayerWithFactory<ColorLayer>(test, [test]() {
+ return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
+ String8("test-layer"), LayerProperties::WIDTH,
+ LayerProperties::HEIGHT,
+ LayerProperties::LAYER_FLAGS, LayerMetadata()));
+ });
+
+ auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+ layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ return layer;
+ }
+
+ static void setupRECompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREColorCompositionCallExpectations(test);
+ }
+
+ static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREColorScreenshotCompositionCallExpectations(test);
+ }
+
+ static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
+ LayerProperties::setupHwcSetGeometryCallExpectations(test);
+ LayerProperties::setupHwcSetSourceCropColorCallExpectations(test);
+ }
+
+ static void setupCallExpectationsForDirtyFrame(CompositionTest* test) {
+ LayerProperties::setupHwcSetPerFrameCallExpectations(test);
+ LayerProperties::setupHwcSetPerFrameColorCallExpectations(test);
+ }
+};
+
+template <typename LayerProperties>
+struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
+ using Base = BaseLayerVariant<LayerProperties>;
+ using FlingerLayerType = sp<BufferQueueLayer>;
+
+ static FlingerLayerType createLayer(CompositionTest* test) {
+ test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
+
+ FlingerLayerType layer =
+ Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
+ return new BufferQueueLayer(
+ LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
+ String8("test-layer"), LayerProperties::WIDTH,
+ LayerProperties::HEIGHT,
+ LayerProperties::LAYER_FLAGS, LayerMetadata()));
+ });
+
+ LayerProperties::setupLayerState(test, layer);
+
+ return layer;
+ }
+
+ static void cleanupInjectedLayers(CompositionTest* test) {
+ EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(2);
+ Base::cleanupInjectedLayers(test);
+ }
+
+ static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
+ LayerProperties::setupHwcSetGeometryCallExpectations(test);
+ LayerProperties::setupHwcSetSourceCropBufferCallExpectations(test);
+ }
+
+ static void setupCallExpectationsForDirtyFrame(CompositionTest* test) {
+ LayerProperties::setupHwcSetPerFrameCallExpectations(test);
+ LayerProperties::setupHwcSetPerFrameBufferCallExpectations(test);
+ }
+
+ static void setupRECompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREBufferCompositionCallExpectations(test);
+ }
+
+ static void setupInsecureRECompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupInsecureREBufferCompositionCallExpectations(test);
+ }
+
+ static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupREBufferScreenshotCompositionCallExpectations(test);
+ }
+
+ static void setupInsecureREScreenshotCompositionCallExpectations(CompositionTest* test) {
+ LayerProperties::setupInsecureREBufferScreenshotCompositionCallExpectations(test);
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Variants to control how the composition type is changed
+ */
+
+struct NoCompositionTypeVariant {
+ static void setupHwcSetCallExpectations(CompositionTest*) {}
+
+ static void setupHwcGetCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1);
+ }
+};
+
+template <IComposerClient::Composition CompositionType>
+struct KeepCompositionTypeVariant {
+ static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType);
+
+ static void setupHwcSetCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
+ .Times(1);
+ }
+
+ static void setupHwcGetCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1);
+ }
+};
+
+template <IComposerClient::Composition InitialCompositionType,
+ IComposerClient::Composition FinalCompositionType>
+struct ChangeCompositionTypeVariant {
+ static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType);
+
+ static void setupHwcSetCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer,
+ setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
+ .Times(1);
+ }
+
+ static void setupHwcGetCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::Layer>{
+ static_cast<Hwc2::Layer>(HWC_LAYER)}),
+ SetArgPointee<2>(std::vector<IComposerClient::Composition>{
+ FinalCompositionType}),
+ Return(Error::NONE)));
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Variants to select how the composition is expected to be handled
+ */
+
+struct CompositionResultBaseVariant {
+ static void setupLayerState(CompositionTest*, sp<Layer>) {}
+
+ template <typename Case>
+ static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
+ Case::Layer::setupCallExpectationsForDirtyGeometry(test);
+ }
+
+ template <typename Case>
+ static void setupCallExpectationsForDirtyFrame(CompositionTest* test) {
+ Case::Layer::setupCallExpectationsForDirtyFrame(test);
+ }
+};
+
+struct NoCompositionResultVariant : public CompositionResultBaseVariant {
+ template <typename Case>
+ static void setupCallExpectations(CompositionTest* test) {
+ Case::Display::setupEmptyFrameCompositionCallExpectations(test);
+ Case::Display::setupHwcCompositionCallExpectations(test);
+ }
+};
+
+struct HwcCompositionResultVariant : public CompositionResultBaseVariant {
+ template <typename Case>
+ static void setupCallExpectations(CompositionTest* test) {
+ Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+ Case::Display::setupHwcCompositionCallExpectations(test);
+ }
+};
+
+struct RECompositionResultVariant : public CompositionResultBaseVariant {
+ template <typename Case>
+ static void setupCallExpectations(CompositionTest* test) {
+ Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+ Case::Display::setupRECompositionCallExpectations(test);
+ Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
+ }
+};
+
+struct ForcedClientCompositionResultVariant : public RECompositionResultVariant {
+ static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+ layer->forceClientComposition(test->mDisplay);
+ }
+
+ template <typename Case>
+ static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+
+ template <typename Case>
+ static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
+struct EmptyScreenshotResultVariant {
+ static void setupLayerState(CompositionTest*, sp<Layer>) {}
+
+ template <typename Case>
+ static void setupCallExpectations(CompositionTest*) {}
+};
+
+struct REScreenshotResultVariant : public EmptyScreenshotResultVariant {
+ using Base = EmptyScreenshotResultVariant;
+
+ template <typename Case>
+ static void setupCallExpectations(CompositionTest* test) {
+ Base::template setupCallExpectations<Case>(test);
+ Case::Display::template setupRELayerScreenshotCompositionCallExpectations<Case>(test);
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Composition test case, containing all the variants being tested
+ */
+
+template <typename DisplayCase, typename LayerCase, typename CompositionTypeCase,
+ typename CompositionResultCase>
+struct CompositionCase {
+ using ThisCase =
+ CompositionCase<DisplayCase, LayerCase, CompositionTypeCase, CompositionResultCase>;
+ using Display = DisplayCase;
+ using Layer = LayerCase;
+ using CompositionType = CompositionTypeCase;
+ using CompositionResult = CompositionResultCase;
+
+ static void setupCommon(CompositionTest* test) {
+ Display::setupPreconditions(test);
+
+ auto layer = Layer::createLayer(test);
+ Layer::injectLayer(test, layer);
+ CompositionResult::setupLayerState(test, layer);
+ }
+
+ static void setupForDirtyGeometry(CompositionTest* test) {
+ setupCommon(test);
+
+ Display::template setupCommonCompositionCallExpectations<ThisCase>(test);
+ CompositionResult::template setupCallExpectationsForDirtyGeometry<ThisCase>(test);
+ CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test);
+ CompositionResult::template setupCallExpectations<ThisCase>(test);
+ }
+
+ static void setupForDirtyFrame(CompositionTest* test) {
+ setupCommon(test);
+
+ Display::template setupCommonCompositionCallExpectations<ThisCase>(test);
+ CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test);
+ CompositionResult::template setupCallExpectations<ThisCase>(test);
+ }
+
+ static void setupForScreenCapture(CompositionTest* test) {
+ setupCommon(test);
+
+ Display::template setupCommonScreensCaptureCallExpectations<ThisCase>(test);
+ CompositionResult::template setupCallExpectations<ThisCase>(test);
+ }
+
+ static void cleanup(CompositionTest* test) {
+ Layer::cleanupInjectedLayers(test);
+
+ for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) {
+ hwcDisplay->mutableLayers().clear();
+ }
+
+ test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>());
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Composition cases to test
+ */
+
+TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant,
+ NoCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant,
+ NoCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, noLayersDoesMinimalWorkToCaptureScreen) {
+ captureScreenComposition<
+ CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant,
+ EmptyScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Simple buffer layers
+ */
+
+TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedNormalBufferLayer) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
+ IComposerClient::Composition::CLIENT>,
+ RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenNormalBufferLayer) {
+ captureScreenComposition<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Single-color layers
+ */
+
+TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedColorLayer) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+ ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
+ IComposerClient::Composition::CLIENT>,
+ RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenColorLayer) {
+ captureScreenComposition<
+ CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Layers with sideband buffers
+ */
+
+TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedSidebandBufferLayer) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND,
+ IComposerClient::Composition::CLIENT>,
+ RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenSidebandBufferLayer) {
+ captureScreenComposition<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Layers with ISurfaceComposerClient::eSecure, on a secure display
+ */
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedSecureBufferLayer) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
+ IComposerClient::Composition::CLIENT>,
+ RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) {
+ captureScreenComposition<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Layers with ISurfaceComposerClient::eSecure, on a non-secure display
+ */
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) {
+ captureScreenComposition<
+ CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Cursor layers
+ */
+
+TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, REComposedCursorLayer) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR,
+ IComposerClient::Composition::CLIENT>,
+ RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenCursorLayer) {
+ captureScreenComposition<
+ CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
+ * Simple buffer layer on a display which is powered off.
+ */
+
+TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) {
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
+ IComposerClient::Composition::CLIENT>,
+ RECompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenNormalBufferLayerOnPoweredOffDisplay) {
+ captureScreenComposition<CompositionCase<
+ PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
new file mode 100644
index 0000000..92bdebd
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include "AsyncCallRecorder.h"
+#include "Scheduler/DispSyncSource.h"
+#include "mock/MockDispSync.h"
+
+namespace android {
+namespace {
+
+using namespace std::chrono_literals;
+using testing::Return;
+
+class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
+protected:
+ DispSyncSourceTest();
+ ~DispSyncSourceTest() override;
+
+ void createDispSync();
+ void createDispSyncSource();
+
+ void onVSyncEvent(nsecs_t when) override;
+
+ std::unique_ptr<mock::DispSync> mDispSync;
+ std::unique_ptr<DispSyncSource> mDispSyncSource;
+
+ AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
+
+ static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
+ static constexpr int mIterations = 100;
+};
+
+DispSyncSourceTest::DispSyncSourceTest() {
+ 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());
+}
+
+DispSyncSourceTest::~DispSyncSourceTest() {
+ 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 DispSyncSourceTest::onVSyncEvent(nsecs_t when) {
+ ALOGD("onVSyncEvent: %" PRId64, when);
+
+ mVSyncEventCallRecorder.recordCall(when);
+}
+
+void DispSyncSourceTest::createDispSync() {
+ mDispSync = std::make_unique<mock::DispSync>();
+}
+
+void DispSyncSourceTest::createDispSyncSource() {
+ createDispSync();
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
+ "DispSyncSourceTest");
+ mDispSyncSource->setCallback(this);
+}
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(DispSyncSourceTest, createDispSync) {
+ createDispSync();
+ EXPECT_TRUE(mDispSync);
+}
+
+TEST_F(DispSyncSourceTest, createDispSyncSource) {
+ createDispSyncSource();
+ EXPECT_TRUE(mDispSyncSource);
+}
+
+TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
+ createDispSyncSource();
+ EXPECT_TRUE(mDispSyncSource);
+
+ // DispSyncSource starts with Vsync disabled
+ mDispSync->triggerCallback();
+ EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+TEST_F(DispSyncSourceTest, waitForCallbacks) {
+ createDispSyncSource();
+ EXPECT_TRUE(mDispSyncSource);
+
+ mDispSyncSource->setVSyncEnabled(true);
+ EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
+
+ for (int i = 0; i < mIterations; i++) {
+ mDispSync->triggerCallback();
+ EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ }
+}
+
+TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) {
+ createDispSyncSource();
+ EXPECT_TRUE(mDispSyncSource);
+
+ mDispSyncSource->setVSyncEnabled(true);
+ EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
+
+ for (int i = 0; i < mIterations; i++) {
+ mDispSync->triggerCallback();
+ EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ }
+
+ EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666));
+ mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count());
+
+ EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count());
+
+ for (int i = 0; i < mIterations; i++) {
+ mDispSync->triggerCallback();
+ EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ }
+}
+
+TEST_F(DispSyncSourceTest, pauseCallbacks) {
+ createDispSyncSource();
+ EXPECT_TRUE(mDispSyncSource);
+
+ mDispSyncSource->setVSyncEnabled(true);
+ EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
+ mDispSync->triggerCallback();
+ EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+
+ mDispSyncSource->pauseVsyncCallback(true);
+ mDispSync->triggerCallback();
+ EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ mDispSyncSource->pauseVsyncCallback(false);
+ mDispSync->triggerCallback();
+ EXPECT_TRUE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
new file mode 100644
index 0000000..55995d0
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android {
+namespace {
+
+const unsigned char kInternalEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+ "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+ "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+ "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+ "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+ "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+
+const unsigned char kExternalEdid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+ "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+ "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+ "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+ "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+ "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+ "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+
+// Extended EDID with timing extension.
+const unsigned char kExternalEedid[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+ "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+ "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+ "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+ "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+ "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+ "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+ "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+ "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+ "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+ "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+ "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+ "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+ "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+
+template <size_t N>
+DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
+ return DisplayIdentificationData(bytes, bytes + N - 1);
+}
+
+} // namespace
+
+const DisplayIdentificationData& getInternalEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getExternalEdid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEdid);
+ return data;
+}
+
+const DisplayIdentificationData& getExternalEedid() {
+ static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEedid);
+ return data;
+}
+
+TEST(DisplayIdentificationTest, isEdid) {
+ EXPECT_FALSE(isEdid({}));
+
+ EXPECT_TRUE(isEdid(getInternalEdid()));
+ EXPECT_TRUE(isEdid(getExternalEdid()));
+ EXPECT_TRUE(isEdid(getExternalEedid()));
+}
+
+TEST(DisplayIdentificationTest, parseEdid) {
+ auto edid = parseEdid(getInternalEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(0x4ca3u, edid->manufacturerId);
+ EXPECT_STREQ("SEC", edid->pnpId.data());
+ // ASCII text should be used as fallback if display name and serial number are missing.
+ EXPECT_EQ("121AT11-801", edid->displayName);
+
+ edid = parseEdid(getExternalEdid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(0x22f0u, edid->manufacturerId);
+ EXPECT_STREQ("HWP", edid->pnpId.data());
+ EXPECT_EQ("HP ZR30w", edid->displayName);
+
+ edid = parseEdid(getExternalEedid());
+ ASSERT_TRUE(edid);
+ EXPECT_EQ(0x4c2du, edid->manufacturerId);
+ EXPECT_STREQ("SAM", edid->pnpId.data());
+ EXPECT_EQ("SAMSUNG", edid->displayName);
+}
+
+TEST(DisplayIdentificationTest, parseInvalidEdid) {
+ EXPECT_FALSE(isEdid({}));
+ EXPECT_FALSE(parseEdid({}));
+
+ // Display name must be printable.
+ auto data = getExternalEdid();
+ data[97] = '\x1b';
+ auto edid = parseEdid(data);
+ ASSERT_TRUE(edid);
+ // Serial number should be used as fallback if display name is invalid.
+ EXPECT_EQ("CN4202137Q", edid->displayName);
+
+ // Parsing should succeed even if EDID is truncated.
+ data.pop_back();
+ edid = parseEdid(data);
+ ASSERT_TRUE(edid);
+ EXPECT_EQ("CN4202137Q", edid->displayName);
+}
+
+TEST(DisplayIdentificationTest, getPnpId) {
+ EXPECT_FALSE(getPnpId(0));
+ EXPECT_FALSE(getPnpId(static_cast<uint16_t>(-1)));
+
+ EXPECT_STREQ("SEC", getPnpId(0x4ca3u).value_or(PnpId{}).data());
+ EXPECT_STREQ("HWP", getPnpId(0x22f0u).value_or(PnpId{}).data());
+ EXPECT_STREQ("SAM", getPnpId(0x4c2du).value_or(PnpId{}).data());
+}
+
+TEST(DisplayIdentificationTest, parseDisplayIdentificationData) {
+ const auto primaryInfo = parseDisplayIdentificationData(0, getInternalEdid());
+ ASSERT_TRUE(primaryInfo);
+
+ const auto secondaryInfo = parseDisplayIdentificationData(1, getExternalEdid());
+ ASSERT_TRUE(secondaryInfo);
+
+ const auto tertiaryInfo = parseDisplayIdentificationData(2, getExternalEedid());
+ ASSERT_TRUE(tertiaryInfo);
+
+ // Display IDs should be unique.
+ EXPECT_NE(primaryInfo->id, secondaryInfo->id);
+ EXPECT_NE(primaryInfo->id, tertiaryInfo->id);
+ EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
+}
+
+TEST(DisplayIdentificationTest, getFallbackDisplayId) {
+ // Manufacturer ID should be invalid.
+ ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));
+ ASSERT_FALSE(getPnpId(getFallbackDisplayId(0xffu)));
+}
+
+TEST(DisplayIdentificationTest, getVirtualDisplayId) {
+ // Manufacturer ID should be invalid.
+ ASSERT_FALSE(getPnpId(getVirtualDisplayId(0)));
+ ASSERT_FALSE(getPnpId(getVirtualDisplayId(0xffff'ffffu)));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
index e6ac6bf..1c8e5cc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
+
+#include "DisplayHardware/DisplayIdentification.h"
namespace android {
-namespace mock {
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+const DisplayIdentificationData& getInternalEdid();
+const DisplayIdentificationData& getExternalEdid();
+const DisplayIdentificationData& getExternalEedid();
-} // namespace mock
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9b308bf..9bf29a2 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -17,20 +17,27 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <type_traits>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DebugUtils.h>
+#include "DisplayIdentificationTest.h"
+#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/MockDispSync.h"
#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
#include "mock/MockNativeWindowSurface.h"
#include "mock/MockSurfaceInterceptor.h"
-#include "mock/RenderEngine/MockRenderEngine.h"
#include "mock/gui/MockGraphicBufferConsumer.h"
#include "mock/gui/MockGraphicBufferProducer.h"
#include "mock/system/window/MockNativeWindow.h"
@@ -39,9 +46,9 @@
namespace {
using testing::_;
-using testing::ByMove;
using testing::DoAll;
using testing::Mock;
+using testing::ResultOf;
using testing::Return;
using testing::SetArgPointee;
@@ -73,9 +80,11 @@
#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
-BOOL_SUBSTITUTE(Critical);
BOOL_SUBSTITUTE(Async);
+BOOL_SUBSTITUTE(Critical);
+BOOL_SUBSTITUTE(Primary);
BOOL_SUBSTITUTE(Secure);
+BOOL_SUBSTITUTE(Virtual);
/* ------------------------------------------------------------------------
*
@@ -86,6 +95,8 @@
DisplayTransactionTest();
~DisplayTransactionTest() override;
+ void setupScheduler();
+
// --------------------------------------------------------------------
// Mock/Fake injection
@@ -96,7 +107,7 @@
// --------------------------------------------------------------------
// Postcondition helpers
- bool hasHwcDisplay(hwc2_display_t displayId);
+ bool hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId);
bool hasTransactionFlagSet(int flag);
bool hasDisplayDevice(sp<IBinder> displayToken);
sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
@@ -108,24 +119,27 @@
// --------------------------------------------------------------------
// Test instances
+ TestableScheduler* mScheduler;
TestableSurfaceFlinger mFlinger;
mock::EventThread* mEventThread = new mock::EventThread();
+ mock::EventThread* mSFEventThread = new mock::EventThread();
mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+ sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+ sp<GraphicBuffer> mBuffer = new GraphicBuffer();
// These mocks are created by the test, but are destroyed by SurfaceFlinger
// by virtue of being stored into a std::unique_ptr. However we still need
// to keep a reference to them for use in setting up call expectations.
- RE::mock::RenderEngine* mRenderEngine = new RE::mock::RenderEngine();
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
Hwc2::mock::Composer* mComposer = nullptr;
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
+ mock::DispSync* mPrimaryDispSync = new mock::DispSync();
// These mocks are created only when expected to be created via a factory.
sp<mock::GraphicBufferConsumer> mConsumer;
sp<mock::GraphicBufferProducer> mProducer;
- mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
- sp<mock::NativeWindow> mNativeWindow;
- RE::mock::Surface* mRenderSurface = nullptr;
+ surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
};
DisplayTransactionTest::DisplayTransactionTest() {
@@ -135,6 +149,7 @@
// Default to no wide color display support configured
mFlinger.mutableHasWideColorDisplay() = false;
+ mFlinger.mutableUseColorManagement() = false;
mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
// Default to using HWC virtual displays
@@ -149,10 +164,9 @@
return nullptr;
});
- mFlinger.mutableEventControlThread().reset(mEventControlThread);
- mFlinger.mutableEventThread().reset(mEventThread);
+ setupScheduler();
mFlinger.mutableEventQueue().reset(mMessageQueue);
- mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine));
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
injectMockComposer(0);
@@ -164,6 +178,22 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
+void DisplayTransactionTest::setupScheduler() {
+ mScheduler = new TestableScheduler();
+ mScheduler->mutableEventControlThread().reset(mEventControlThread);
+ mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
+ EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
+
+ sp<Scheduler::ConnectionHandle> sfConnectionHandle =
+ mScheduler->addConnection(std::unique_ptr<EventThread>(mSFEventThread));
+ mFlinger.mutableSfConnectionHandle() = std::move(sfConnectionHandle);
+ sp<Scheduler::ConnectionHandle> appConnectionHandle =
+ mScheduler->addConnection(std::unique_ptr<EventThread>(mEventThread));
+ mFlinger.mutableAppConnectionHandle() = std::move(appConnectionHandle);
+ mFlinger.mutableScheduler().reset(mScheduler);
+}
+
void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
mComposer = new Hwc2::mock::Composer();
EXPECT_CALL(*mComposer, getCapabilities())
@@ -191,15 +221,15 @@
// This setup is only expected once per test.
ASSERT_TRUE(mNativeWindowSurface == nullptr);
- mNativeWindowSurface = new mock::NativeWindowSurface();
- mNativeWindow = new mock::NativeWindow();
+ mNativeWindowSurface = new surfaceflinger::mock::NativeWindowSurface();
- mFlinger.setCreateNativeWindowSurface(
- [this](auto) { return std::unique_ptr<NativeWindowSurface>(mNativeWindowSurface); });
+ mFlinger.setCreateNativeWindowSurface([this](auto) {
+ return std::unique_ptr<surfaceflinger::NativeWindowSurface>(mNativeWindowSurface);
+ });
}
-bool DisplayTransactionTest::hasHwcDisplay(hwc2_display_t displayId) {
- return mFlinger.mutableHwcDisplaySlots().count(displayId) == 1;
+bool DisplayTransactionTest::hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId) {
+ return mFlinger.mutableHwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
}
bool DisplayTransactionTest::hasTransactionFlagSet(int flag) {
@@ -207,11 +237,11 @@
}
bool DisplayTransactionTest::hasDisplayDevice(sp<IBinder> displayToken) {
- return mFlinger.mutableDisplays().indexOfKey(displayToken) >= 0;
+ return mFlinger.mutableDisplays().count(displayToken) == 1;
}
sp<DisplayDevice> DisplayTransactionTest::getDisplayDevice(sp<IBinder> displayToken) {
- return mFlinger.mutableDisplays().valueFor(displayToken);
+ return mFlinger.mutableDisplays()[displayToken];
}
bool DisplayTransactionTest::hasCurrentDisplayState(sp<IBinder> displayToken) {
@@ -234,18 +264,67 @@
*
*/
-template <DisplayDevice::DisplayType type, DisplayDevice::DisplayType hwcId, int width, int height,
- Critical critical, Async async, Secure secure, int grallocUsage>
+template <typename PhysicalDisplay>
+struct PhysicalDisplayId {};
+
+template <DisplayId::Type displayId>
+using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
+
+struct NoDisplayId {};
+
+template <typename>
+struct IsPhysicalDisplayId : std::bool_constant<false> {};
+
+template <typename PhysicalDisplay>
+struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {};
+
+template <typename>
+struct DisplayIdGetter;
+
+template <typename PhysicalDisplay>
+struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+ static std::optional<DisplayId> get() {
+ if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
+ ? HWC_DISPLAY_PRIMARY
+ : HWC_DISPLAY_EXTERNAL);
+ }
+
+ const auto info =
+ parseDisplayIdentificationData(PhysicalDisplay::PORT,
+ PhysicalDisplay::GET_IDENTIFICATION_DATA());
+ return info ? std::make_optional(info->id) : std::nullopt;
+ }
+};
+
+template <DisplayId::Type displayId>
+struct DisplayIdGetter<VirtualDisplayId<displayId>> {
+ static std::optional<DisplayId> get() { return DisplayId{displayId}; }
+};
+
+template <>
+struct DisplayIdGetter<NoDisplayId> {
+ static std::optional<DisplayId> get() { return {}; }
+};
+
+// DisplayIdType can be:
+// 1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
+// 2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
+// 3) NoDisplayId for virtual display without HWC backing.
+template <typename DisplayIdType, int width, int height, Critical critical, Async async,
+ Secure secure, Primary primary, int grallocUsage>
struct DisplayVariant {
+ using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+
// The display width and height
static constexpr int WIDTH = width;
static constexpr int HEIGHT = height;
static constexpr int GRALLOC_USAGE = grallocUsage;
- // The type for this display
- static constexpr DisplayDevice::DisplayType TYPE = type;
- static constexpr DisplayDevice::DisplayType HWCOMPOSER_ID = hwcId;
+ // Whether the display is virtual or physical
+ static constexpr Virtual VIRTUAL =
+ IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
// When creating native window surfaces for the framebuffer, whether those should be critical
static constexpr Critical CRITICAL = critical;
@@ -256,9 +335,29 @@
// Whether the display should be treated as secure
static constexpr Secure SECURE = secure;
+ // Whether the display is primary
+ static constexpr Primary PRIMARY = primary;
+
static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
- auto injector = FakeDisplayDeviceInjector(test->mFlinger, TYPE, HWCOMPOSER_ID);
+ auto injector =
+ FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(),
+ static_cast<bool>(VIRTUAL), static_cast<bool>(PRIMARY));
+
injector.setSecure(static_cast<bool>(SECURE));
+ injector.setNativeWindow(test->mNativeWindow);
+
+ // Creating a DisplayDevice requires getting default dimensions from the
+ // native window along with some other initial setup.
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
return injector;
}
@@ -266,19 +365,17 @@
static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
.WillOnce(Return(test->mNativeWindow));
- EXPECT_CALL(*test->mNativeWindow, perform(19)).WillRepeatedly(Return(NO_ERROR));
- // For simplicity, we only expect to create a single render surface for
- // each test.
- ASSERT_TRUE(test->mRenderSurface == nullptr);
- test->mRenderSurface = new RE::mock::Surface();
- EXPECT_CALL(*test->mRenderEngine, createSurface())
- .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(test->mRenderSurface))));
- EXPECT_CALL(*test->mRenderSurface, setAsync(static_cast<bool>(ASYNC))).Times(1);
- EXPECT_CALL(*test->mRenderSurface, setCritical(static_cast<bool>(CRITICAL))).Times(1);
- EXPECT_CALL(*test->mRenderSurface, setNativeWindow(test->mNativeWindow.get())).Times(1);
- EXPECT_CALL(*test->mRenderSurface, queryWidth()).WillOnce(Return(WIDTH));
- EXPECT_CALL(*test->mRenderSurface, queryHeight()).WillOnce(Return(HEIGHT));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
}
static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -297,7 +394,8 @@
}
};
-template <hwc2_display_t hwcDisplayId, HWC2::DisplayType hwcDisplayType, typename DisplayVariant>
+template <hwc2_display_t hwcDisplayId, HWC2::DisplayType hwcDisplayType, typename DisplayVariant,
+ typename PhysicalDisplay = void>
struct HwcDisplayVariant {
// The display id supplied by the HWC
static constexpr hwc2_display_t HWC_DISPLAY_ID = hwcDisplayId;
@@ -315,8 +413,11 @@
}
// Called by tests to inject a HWC display setup
- static void injectHwcDisplay(DisplayTransactionTest* test) {
- FakeHwcDisplayInjector(DisplayVariant::TYPE, HWC_DISPLAY_TYPE)
+ static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
+ const auto displayId = DisplayVariant::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
+ static_cast<bool>(DisplayVariant::PRIMARY))
.setHwcDisplayId(HWC_DISPLAY_ID)
.setWidth(DisplayVariant::WIDTH)
.setHeight(DisplayVariant::HEIGHT)
@@ -324,6 +425,14 @@
.inject(&test->mFlinger, test->mComposer);
}
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplay(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+ injectHwcDisplayWithNoDefaultCapabilities(test);
+ }
+
static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getDisplayType(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(static_cast<IComposerClient::DisplayType>(
@@ -353,6 +462,19 @@
getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
IComposerClient::Attribute::DPI_Y, _))
.WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+
+ if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
+ SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(Return(Error::UNSUPPORTED));
+ }
}
// Called by tests to set up HWC call expectations
@@ -362,55 +484,69 @@
}
};
-struct NonHwcDisplayVariant {
- static void injectHwcDisplay(DisplayTransactionTest*) {}
-
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
- }
-};
-
// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-template <hwc2_display_t hwcDisplayId, DisplayDevice::DisplayType type, int width, int height,
+template <hwc2_display_t hwcDisplayId, typename PhysicalDisplay, int width, int height,
Critical critical>
struct PhysicalDisplayVariant
- : public DisplayVariant<type, type, width, height, critical, Async::FALSE, Secure::TRUE,
- GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- public HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
- DisplayVariant<type, type, width, height, critical, Async::FALSE,
- Secure::TRUE, GRALLOC_USAGE_PHYSICAL_DISPLAY>> {};
+ : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
+ Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
+ DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
+ critical, Async::FALSE, Secure::TRUE,
+ PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ PhysicalDisplay> {};
-// An invalid display
-using InvalidDisplayVariant =
- DisplayVariant<DisplayDevice::DISPLAY_ID_INVALID, DisplayDevice::DISPLAY_ID_INVALID, 0, 0,
- Critical::FALSE, Async::FALSE, Secure::FALSE, 0>;
+template <bool hasIdentificationData>
+struct PrimaryDisplay {
+ static constexpr Primary PRIMARY = Primary::TRUE;
+ static constexpr uint8_t PORT = 255;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
+};
+
+template <bool hasIdentificationData>
+struct ExternalDisplay {
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 254;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+struct TertiaryDisplay {
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 253;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
// A primary display is a physical display that is critical
using PrimaryDisplayVariant =
- PhysicalDisplayVariant<1001, DisplayDevice::DISPLAY_PRIMARY, 3840, 2160, Critical::TRUE>;
+ PhysicalDisplayVariant<1001, PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
// An external display is physical display that is not critical.
using ExternalDisplayVariant =
- PhysicalDisplayVariant<1002, DisplayDevice::DISPLAY_EXTERNAL, 1920, 1280, Critical::FALSE>;
+ PhysicalDisplayVariant<1002, ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
using TertiaryDisplayVariant =
- PhysicalDisplayVariant<1003, DisplayDevice::DISPLAY_EXTERNAL, 1600, 1200, Critical::FALSE>;
+ PhysicalDisplayVariant<1003, TertiaryDisplay, 1600, 1200, Critical::FALSE>;
// A virtual display not supported by the HWC.
constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
template <int width, int height, Secure secure>
struct NonHwcVirtualDisplayVariant
- : public DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_ID_INVALID,
- width, height, Critical::FALSE, Async::TRUE, secure,
- GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>,
- public NonHwcDisplayVariant {
- using Base = DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_ID_INVALID,
- width, height, Critical::FALSE, Async::TRUE, secure,
- GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+ : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
+ using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+
+ static void injectHwcDisplay(DisplayTransactionTest*) {}
+
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
+ }
static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
Base::setupNativeWindowSurfaceCreationCallExpectations(test);
@@ -423,14 +559,14 @@
template <int width, int height, Secure secure>
struct HwcVirtualDisplayVariant
- : public DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_VIRTUAL, width,
- height, Critical::FALSE, Async::TRUE, secure,
- GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- public HwcDisplayVariant<1010, HWC2::DisplayType::Virtual,
- NonHwcVirtualDisplayVariant<width, height, secure>> {
- using Base =
- DisplayVariant<DisplayDevice::DISPLAY_VIRTUAL, DisplayDevice::DISPLAY_VIRTUAL, width,
- height, Critical::FALSE, Async::TRUE, secure, GRALLOC_USAGE_HW_COMPOSER>;
+ : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+ HwcDisplayVariant<
+ 1010, HWC2::DisplayType::Virtual,
+ DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
using Self = HwcVirtualDisplayVariant<width, height, secure>;
static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
@@ -453,6 +589,7 @@
static void injectConfigChange(DisplayTransactionTest* test) {
test->mFlinger.mutableHasWideColorDisplay() = false;
+ test->mFlinger.mutableUseColorManagement() = false;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
}
@@ -471,11 +608,14 @@
static constexpr bool WIDE_COLOR_SUPPORTED = true;
static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
test->mFlinger.mutableHasWideColorDisplay() = true;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
}
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
+
EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
Return(Error::NONE)));
@@ -499,6 +639,7 @@
static constexpr bool WIDE_COLOR_SUPPORTED = false;
static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
test->mFlinger.mutableHasWideColorDisplay() = true;
}
@@ -512,6 +653,7 @@
// For this variant, the display is not a HWC display, so no HDR support should
// be configured.
struct NonHwcDisplayHdrSupportVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
static constexpr bool HDR10_SUPPORTED = false;
static constexpr bool HDR_HLG_SUPPORTED = false;
static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -520,10 +662,27 @@
}
};
+template <typename Display>
+struct Hdr10PlusSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = true;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
+ Hdr::HDR10_PLUS,
+ Hdr::HDR10,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
// For this variant, the composer should respond with a non-empty list of HDR
// modes containing HDR10, so HDR10 support should be configured.
template <typename Display>
struct Hdr10SupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
static constexpr bool HDR10_SUPPORTED = true;
static constexpr bool HDR_HLG_SUPPORTED = false;
static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -538,6 +697,7 @@
// modes containing HLG, so HLG support should be configured.
template <typename Display>
struct HdrHlgSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
static constexpr bool HDR10_SUPPORTED = false;
static constexpr bool HDR_HLG_SUPPORTED = true;
static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -552,6 +712,7 @@
// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
template <typename Display>
struct HdrDolbyVisionSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
static constexpr bool HDR10_SUPPORTED = false;
static constexpr bool HDR_HLG_SUPPORTED = false;
static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
@@ -566,6 +727,7 @@
// modes, so no HDR support should be configured.
template <typename Display>
struct HdrNotSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
static constexpr bool HDR10_SUPPORTED = false;
static constexpr bool HDR_HLG_SUPPORTED = false;
static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
@@ -578,7 +740,7 @@
struct NonHwcPerFrameMetadataSupportVariant {
static constexpr int PER_FRAME_METADATA_KEYS = 0;
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
}
};
@@ -586,9 +748,8 @@
struct NoPerFrameMetadataSupportVariant {
static constexpr int PER_FRAME_METADATA_KEYS = 0;
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>()),
- Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
}
};
@@ -596,20 +757,19 @@
struct Smpte2086PerFrameMetadataSupportVariant {
static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
- PerFrameMetadataKey::WHITE_POINT_X,
- PerFrameMetadataKey::WHITE_POINT_Y,
- PerFrameMetadataKey::MAX_LUMINANCE,
- PerFrameMetadataKey::MIN_LUMINANCE,
- })),
- Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ PerFrameMetadataKey::WHITE_POINT_X,
+ PerFrameMetadataKey::WHITE_POINT_Y,
+ PerFrameMetadataKey::MAX_LUMINANCE,
+ PerFrameMetadataKey::MIN_LUMINANCE,
+ })));
}
};
@@ -617,15 +777,24 @@
struct Cta861_3_PerFrameMetadataSupportVariant {
static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
- PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
- })),
- Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+ PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ })));
}
};
+template <typename Display>
+struct Hdr10_Plus_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::HDR10_PLUS_SEI,
+ })));
+ }
+};
/* ------------------------------------------------------------------------
* Typical display configurations to test
*/
@@ -664,6 +833,10 @@
Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
HdrNotSupportedVariant<PrimaryDisplayVariant>,
NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10PlusDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using Hdr10DisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
Hdr10SupportedVariant<PrimaryDisplayVariant>,
@@ -684,9 +857,7 @@
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
HdrNotSupportedVariant<PrimaryDisplayVariant>,
Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant,
- NonHwcDisplayHdrSupportVariant,
- NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>;
+
/* ------------------------------------------------------------------------
*
* SurfaceFlinger::onHotplugReceived
@@ -694,8 +865,8 @@
TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
constexpr int currentSequenceId = 123;
- constexpr hwc2_display_t displayId1 = 456;
- constexpr hwc2_display_t displayId2 = 654;
+ constexpr hwc2_display_t hwcDisplayId1 = 456;
+ constexpr hwc2_display_t hwcDisplayId2 = 654;
// --------------------------------------------------------------------
// Preconditions
@@ -718,8 +889,8 @@
// Invocation
// Simulate two hotplug events (a connect and a disconnect)
- mFlinger.onHotplugReceived(currentSequenceId, displayId1, HWC2::Connection::Connected);
- mFlinger.onHotplugReceived(currentSequenceId, displayId2, HWC2::Connection::Disconnected);
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, HWC2::Connection::Connected);
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, HWC2::Connection::Disconnected);
// --------------------------------------------------------------------
// Postconditions
@@ -730,9 +901,9 @@
// All events should be in the pending event queue.
const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
ASSERT_EQ(2u, pendingEvents.size());
- EXPECT_EQ(displayId1, pendingEvents[0].display);
+ EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
EXPECT_EQ(HWC2::Connection::Connected, pendingEvents[0].connection);
- EXPECT_EQ(displayId2, pendingEvents[1].display);
+ EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
EXPECT_EQ(HWC2::Connection::Disconnected, pendingEvents[1].connection);
}
@@ -846,8 +1017,8 @@
// The display should have been added to the current state
ASSERT_TRUE(hasCurrentDisplayState(displayToken));
const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_EQ(DisplayDevice::DISPLAY_VIRTUAL, display.type);
- EXPECT_EQ(false, display.isSecure);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_FALSE(display.isSecure);
EXPECT_EQ(name.string(), display.displayName);
// --------------------------------------------------------------------
@@ -877,8 +1048,8 @@
// The display should have been added to the current state
ASSERT_TRUE(hasCurrentDisplayState(displayToken));
const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_EQ(DisplayDevice::DISPLAY_VIRTUAL, display.type);
- EXPECT_EQ(true, display.isSecure);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_TRUE(display.isSecure);
EXPECT_EQ(name.string(), display.displayName);
// --------------------------------------------------------------------
@@ -952,8 +1123,8 @@
// Preconditions
// vsync is enabled and available
- mFlinger.mutablePrimaryHWVsyncEnabled() = true;
- mFlinger.mutableHWVsyncAvailable() = true;
+ mScheduler->mutablePrimaryHWVsyncEnabled() = true;
+ mScheduler->mutableHWVsyncAvailable() = true;
// A display exists
auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
@@ -965,8 +1136,8 @@
// The call disable vsyncs
EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1);
- // The call clears the current render engine surface
- EXPECT_CALL(*mRenderEngine, resetCurrentSurface());
+ // The call ends any display resyncs
+ EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
// --------------------------------------------------------------------
// Invocation
@@ -977,8 +1148,8 @@
// Postconditions
// vsyncs should be off and not available.
- EXPECT_FALSE(mFlinger.mutablePrimaryHWVsyncEnabled());
- EXPECT_FALSE(mFlinger.mutableHWVsyncAvailable());
+ EXPECT_FALSE(mScheduler->mutablePrimaryHWVsyncEnabled());
+ EXPECT_FALSE(mScheduler->mutableHWVsyncAvailable());
// The display should have been removed from the display map.
EXPECT_FALSE(hasDisplayDevice(existing.token()));
@@ -991,6 +1162,193 @@
}
/* ------------------------------------------------------------------------
+ * DisplayDevice::GetBestColorMode
+ */
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+ static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+
+ GetBestColorModeTest()
+ : DisplayTransactionTest(),
+ mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID, false /* isVirtual */,
+ true /* isPrimary */)) {}
+
+ void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+ void addHwcColorModesMapping(ui::ColorMode colorMode,
+ std::vector<ui::RenderIntent> renderIntents) {
+ mHwcColorModes[colorMode] = renderIntents;
+ }
+
+ void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+ void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+ void getBestColorMode() {
+ mInjector.setHwcColorModes(mHwcColorModes);
+ mInjector.setHasWideColorGamut(mHasWideColorGamut);
+ mInjector.setNativeWindow(mNativeWindow);
+
+ // Creating a DisplayDevice requires getting default dimensions from the
+ // native window.
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ auto displayDevice = mInjector.inject();
+
+ displayDevice->getCompositionDisplay()
+ ->getDisplayColorProfile()
+ ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+ &mOutColorMode, &mOutRenderIntent);
+ }
+
+ ui::Dataspace mOutDataspace;
+ ui::ColorMode mOutColorMode;
+ ui::RenderIntent mOutRenderIntent;
+
+private:
+ ui::Dataspace mInputDataspace;
+ ui::RenderIntent mInputRenderIntent;
+ bool mHasWideColorGamut = false;
+ std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+ FakeDisplayDeviceInjector mInjector;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * SurfaceFlinger::getDisplayNativePrimaries
+ */
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+ GetDisplayNativePrimaries();
+ void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+ void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+ static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+ SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+ ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ primaries.red.X = startingVal++;
+ primaries.red.Y = startingVal++;
+ primaries.red.Z = startingVal++;
+ primaries.green.X = startingVal++;
+ primaries.green.Y = startingVal++;
+ primaries.green.Z = startingVal++;
+ primaries.blue.X = startingVal++;
+ primaries.blue.Y = startingVal++;
+ primaries.blue.Z = startingVal++;
+ primaries.white.X = startingVal++;
+ primaries.white.Y = startingVal++;
+ primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+ const ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ EXPECT_EQ(primaries.red.X, startingVal++);
+ EXPECT_EQ(primaries.red.Y, startingVal++);
+ EXPECT_EQ(primaries.red.Z, startingVal++);
+ EXPECT_EQ(primaries.green.X, startingVal++);
+ EXPECT_EQ(primaries.green.Y, startingVal++);
+ EXPECT_EQ(primaries.green.Z, startingVal++);
+ EXPECT_EQ(primaries.blue.X, startingVal++);
+ EXPECT_EQ(primaries.blue.Y, startingVal++);
+ EXPECT_EQ(primaries.blue.Z, startingVal++);
+ EXPECT_EQ(primaries.white.X, startingVal++);
+ EXPECT_EQ(primaries.white.Y, startingVal++);
+ EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+ auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+ injector.inject();
+ auto internalDisplayToken = injector.token();
+
+ ui::DisplayPrimaries expectedPrimaries;
+ populateDummyDisplayNativePrimaries(expectedPrimaries);
+ mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+ sp<BBinder> notInternalDisplayToken = new BBinder();
+
+ ui::DisplayPrimaries primaries;
+ populateDummyDisplayNativePrimaries(primaries);
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+ // Check primaries argument wasn't modified in case of failure
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+/* ------------------------------------------------------------------------
* SurfaceFlinger::setupNewDisplayDeviceInternal
*/
@@ -1003,7 +1361,8 @@
template <typename Case>
void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
const sp<BBinder> displayToken = new BBinder();
- const sp<mock::DisplaySurface> displaySurface = new mock::DisplaySurface();
+ const sp<compositionengine::mock::DisplaySurface> displaySurface =
+ new compositionengine::mock::DisplaySurface();
const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
// --------------------------------------------------------------------
@@ -1032,19 +1391,27 @@
// --------------------------------------------------------------------
// Invocation
- auto state = DisplayDeviceState(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE));
- auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::TYPE, state,
- displaySurface, producer);
+ DisplayDeviceState state;
+ state.displayId = static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
+ : Case::Display::DISPLAY_ID::get();
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ auto device =
+ mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::DISPLAY_ID::get(),
+ state, displaySurface, producer);
// --------------------------------------------------------------------
// Postconditions
ASSERT_TRUE(device != nullptr);
- EXPECT_EQ(Case::Display::TYPE, device->getDisplayType());
+ EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
+ EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
@@ -1069,17 +1436,24 @@
}
TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
- // We need to resize this so that the HWC thinks the virtual display
- // is something it created.
- mFlinger.mutableHwcDisplayData().resize(3);
+ using Case = HwcVirtualDisplayCase;
- setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
+ // Insert display data so that the HWC thinks it created the virtual display.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+
+ setupNewDisplayDeviceInternalTest<Case>();
}
TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
}
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
+ setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
+}
+
TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
}
@@ -1109,6 +1483,9 @@
template <typename Case>
void setupCommonPreconditions();
+ template <typename Case, bool connected>
+ static void expectHotplugReceived(mock::EventThread*);
+
template <typename Case>
void setupCommonCallExpectationsForConnectProcessing();
@@ -1146,6 +1523,17 @@
injectFakeNativeWindowSurfaceFactory();
}
+template <typename Case, bool connected>
+void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+ const auto convert = [](auto physicalDisplayId) {
+ return std::make_optional(DisplayId{physicalDisplayId});
+ };
+
+ EXPECT_CALL(*eventThread,
+ onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
+ .Times(1);
+}
+
template <typename Case>
void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
Case::Display::setupHwcHotplugCallExpectations(this);
@@ -1160,13 +1548,16 @@
Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
- EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1);
+ expectHotplugReceived<Case, true>(mEventThread);
+ expectHotplugReceived<Case, true>(mSFEventThread);
}
template <typename Case>
void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
- EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, false)).Times(1);
+
+ expectHotplugReceived<Case, false>(mEventThread);
+ expectHotplugReceived<Case, false>(mSFEventThread);
}
template <typename Case>
@@ -1175,30 +1566,35 @@
ASSERT_TRUE(hasDisplayDevice(displayToken));
const auto& device = getDisplayDevice(displayToken);
EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(Case::Display::TYPE == DisplayDevice::DISPLAY_PRIMARY, device->isPrimary());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
// The display should have been set up in the current display state
ASSERT_TRUE(hasCurrentDisplayState(displayToken));
const auto& current = getCurrentDisplayState(displayToken);
- EXPECT_EQ(Case::Display::TYPE, current.type);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
+ : Case::Display::DISPLAY_ID::get(),
+ current.displayId);
// The display should have been set up in the drawing display state
ASSERT_TRUE(hasDrawingDisplayState(displayToken));
const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(Case::Display::TYPE, draw.type);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
+ : Case::Display::DISPLAY_ID::get(),
+ draw.displayId);
}
template <typename Case>
void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
// HWComposer should have an entry for the display
- EXPECT_TRUE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+ EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
- // The display should be set up as a built-in display.
- static_assert(0 <= Case::Display::TYPE &&
- Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
- "Must use a valid physical display type index for the fixed-size array");
- auto& displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE];
- ASSERT_TRUE(displayToken != nullptr);
+ // SF should have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
+ auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId];
verifyDisplayIsConnected<Case>(displayToken);
}
@@ -1264,7 +1660,7 @@
// Postconditions
// HWComposer should not have an entry for the display
- EXPECT_FALSE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
}
template <typename Case>
@@ -1286,6 +1682,8 @@
// Call Expectations
EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
+ .Times(0);
setupCommonCallExpectationsForDisconnectProcessing<Case>();
@@ -1298,13 +1696,12 @@
// Postconditions
// HWComposer should not have an entry for the display
- EXPECT_FALSE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
- // The display should not be set up as a built-in display.
- ASSERT_TRUE(0 <= Case::Display::TYPE &&
- Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES);
- auto displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE];
- EXPECT_TRUE(displayToken == nullptr);
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
// The existing token should have been removed
verifyDisplayIsNotConnected(existing.token());
@@ -1334,6 +1731,13 @@
PrimaryDisplayVariant::injectHwcDisplay(this);
ExternalDisplayVariant::injectHwcDisplay(this);
+ // TODO: This is an unnecessary call.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
+ SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+
EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
@@ -1391,13 +1795,12 @@
// Postconditions
// HWComposer should not have an entry for the display
- EXPECT_FALSE(hasHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
- // The display should not be set up as a primary built-in display.
- ASSERT_TRUE(0 <= Case::Display::TYPE &&
- Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES);
- auto displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE];
- EXPECT_TRUE(displayToken == nullptr);
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
}
TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
@@ -1436,10 +1839,10 @@
// The existing token should have been removed
verifyDisplayIsNotConnected(existing.token());
- static_assert(0 <= Case::Display::TYPE &&
- Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
- "Display type must be a built-in display");
- EXPECT_NE(existing.token(), mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE]);
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
+ EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]);
// A new display should be connected in its place
@@ -1468,10 +1871,13 @@
// A virtual display was added to the current state, and it has a
// surface(producer)
sp<BBinder> displayToken = new BBinder();
- DisplayDeviceState info(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE));
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
- info.surface = surface;
- mFlinger.mutableCurrentState().displays.add(displayToken, info);
+ state.surface = surface;
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
// --------------------------------------------------------------------
// Call Expectations
@@ -1479,6 +1885,10 @@
Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ EXPECT_CALL(*mComposer, getDisplayCapabilities(Case::Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+
EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
@@ -1491,7 +1901,7 @@
EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
- EXPECT_CALL(*mProducer, connect(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
@@ -1516,6 +1926,10 @@
EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
.WillOnce(Return(Error::NONE));
EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // Cleanup
+ mFlinger.mutableCurrentState().displays.removeItem(displayToken);
+ mFlinger.mutableDrawingState().displays.removeItem(displayToken);
}
TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
@@ -1532,8 +1946,11 @@
// A virtual display was added to the current state, but it does not have a
// surface.
sp<BBinder> displayToken = new BBinder();
- DisplayDeviceState info(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE));
- mFlinger.mutableCurrentState().displays.add(displayToken, info);
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
// --------------------------------------------------------------------
// Call Expectations
@@ -1552,7 +1969,7 @@
// The drawing display state will be set from the current display state.
ASSERT_TRUE(hasDrawingDisplayState(displayToken));
const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(Case::Display::TYPE, draw.type);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
}
TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
@@ -1562,7 +1979,9 @@
// Preconditions
// A virtual display is set up but is removed from the current state.
- mFlinger.mutableHwcDisplayData().resize(3);
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
Case::Display::injectHwcDisplay(this);
auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
existing.inject();
@@ -1709,12 +2128,19 @@
// A display is set up
auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new mock::DisplaySurface();
- auto renderSurface = new RE::mock::Surface();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.setNativeWindow(nativeWindow);
display.setDisplaySurface(displaySurface);
- display.setRenderSurface(std::unique_ptr<RE::Surface>(renderSurface));
+ // Setup injection expections
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
display.inject();
// There is a change to the viewport state
@@ -1726,11 +2152,7 @@
// --------------------------------------------------------------------
// Call Expectations
- EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1);
EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
- EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1);
- EXPECT_CALL(*renderSurface, queryWidth()).WillOnce(Return(newWidth));
- EXPECT_CALL(*renderSurface, queryHeight()).WillOnce(Return(oldHeight));
// --------------------------------------------------------------------
// Invocation
@@ -1750,12 +2172,19 @@
// A display is set up
auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new mock::DisplaySurface();
- auto renderSurface = new RE::mock::Surface();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.setNativeWindow(nativeWindow);
display.setDisplaySurface(displaySurface);
- display.setRenderSurface(std::unique_ptr<RE::Surface>(renderSurface));
+ // Setup injection expections
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
display.inject();
// There is a change to the viewport state
@@ -1767,11 +2196,7 @@
// --------------------------------------------------------------------
// Call Expectations
- EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1);
EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
- EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1);
- EXPECT_CALL(*renderSurface, queryWidth()).WillOnce(Return(oldWidth));
- EXPECT_CALL(*renderSurface, queryHeight()).WillOnce(Return(newHeight));
// --------------------------------------------------------------------
// Invocation
@@ -1811,40 +2236,6 @@
EXPECT_FALSE(hasCurrentDisplayState(displayToken));
}
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithInvalidDisplay) {
- using Case = InvalidDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // An invalid display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The invalid display has some state
- display.mutableCurrentDisplayState().layerStack = 654u;
-
- // The requested display state tries to change the display state.
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display layer stack value is unchanged.
- EXPECT_EQ(654u, getCurrentDisplayState(display.token()).layerStack);
-}
-
TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
using Case = SimplePrimaryDisplayCase;
@@ -2307,6 +2698,8 @@
// processing.
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+
// --------------------------------------------------------------------
// Invocation
@@ -2358,14 +2751,23 @@
*/
// Used when we simulate a display that supports doze.
+template <typename Display>
struct DozeIsSupportedVariant {
static constexpr bool DOZE_SUPPORTED = true;
static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
IComposerClient::PowerMode::DOZE;
static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
IComposerClient::PowerMode::DOZE_SUSPEND;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>(
+ {Hwc2::DisplayCapability::DOZE})),
+ Return(Error::NONE)));
+ }
};
+template <typename Display>
// Used when we simulate a display that does not support doze.
struct DozeNotSupportedVariant {
static constexpr bool DOZE_SUPPORTED = false;
@@ -2373,6 +2775,12 @@
IComposerClient::PowerMode::ON;
static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
IComposerClient::PowerMode::ON;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
};
struct EventThreadBaseSupportedVariant {
@@ -2420,6 +2828,24 @@
}
};
+struct DispSyncIsSupportedVariant {
+ static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mPrimaryDispSync, reset()).Times(1);
+ EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1);
+ EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1);
+ }
+
+ static void setupEndResyncCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1);
+ }
+};
+
+struct DispSyncNotSupportedVariant {
+ static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
+
+ static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
+};
+
// --------------------------------------------------------------------
// Note:
//
@@ -2441,6 +2867,7 @@
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupBeginResyncCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -2470,6 +2897,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::DispSync::setupEndResyncCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -2505,6 +2933,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupBeginResyncCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -2523,6 +2952,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupBeginResyncCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
@@ -2532,6 +2962,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::DispSync::setupEndResyncCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
}
};
@@ -2554,15 +2985,16 @@
// --------------------------------------------------------------------
template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
- typename TransitionVariant>
+ typename DispSyncVariant, typename TransitionVariant>
struct DisplayPowerCase {
using Display = DisplayVariant;
using Doze = DozeVariant;
using EventThread = EventThreadVariant;
+ using DispSync = DispSyncVariant;
using Transition = TransitionVariant;
static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) {
- Display::injectHwcDisplay(test);
+ Display::injectHwcDisplayWithNoDefaultCapabilities(test);
auto display = Display::makeFakeExistingDisplayInjector(test);
display.inject();
display.mutableDisplayDevice()->setPowerMode(mode);
@@ -2570,7 +3002,7 @@
}
static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
- test->mFlinger.mutablePrimaryHWVsyncEnabled() = enabled;
+ test->mScheduler->mutablePrimaryHWVsyncEnabled() = enabled;
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
@@ -2605,16 +3037,19 @@
// A sample configuration for the primary display.
// In addition to having event thread support, we emulate doze support.
template <typename TransitionVariant>
-using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant,
- EventThreadIsSupportedVariant, TransitionVariant>;
+using PrimaryDisplayPowerCase =
+ DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+ EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+ TransitionVariant>;
// A sample configuration for the external display.
// In addition to not having event thread support, we emulate not having doze
// support.
template <typename TransitionVariant>
using ExternalDisplayPowerCase =
- DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant,
- EventThreadNotSupportedVariant, TransitionVariant>;
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+ EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+ TransitionVariant>;
class SetPowerModeInternalTest : public DisplayTransactionTest {
public:
@@ -2636,6 +3071,7 @@
// --------------------------------------------------------------------
// Preconditions
+ Case::Doze::setupComposerCallExpectations(this);
auto display =
Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
Case::setInitialPrimaryHWVsyncEnabled(this,
@@ -2671,7 +3107,7 @@
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // The diplay is already set to HWC_POWER_MODE_NORMAL
+ // The display is already set to HWC_POWER_MODE_NORMAL
display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL);
// --------------------------------------------------------------------
@@ -2685,28 +3121,29 @@
EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
}
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirtualDisplay) {
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
using Case = HwcVirtualDisplayCase;
// --------------------------------------------------------------------
// Preconditions
- // We need to resize this so that the HWC thinks the virtual display
- // is something it created.
- mFlinger.mutableHwcDisplayData().resize(3);
+ // Insert display data so that the HWC thinks it created the virtual display.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(displayId);
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
// A virtual display device is set up
Case::Display::injectHwcDisplay(this);
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.inject();
- // The display is set to HWC_POWER_MODE_OFF
- getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_OFF);
+ // The display is set to HWC_POWER_MODE_NORMAL
+ getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_NORMAL);
// --------------------------------------------------------------------
// Invocation
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_OFF);
// --------------------------------------------------------------------
// Postconditions
diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
index b346454..9dc4193 100644
--- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
@@ -23,7 +23,7 @@
#include <log/log.h>
#include "AsyncCallRecorder.h"
-#include "EventControlThread.h"
+#include "Scheduler/EventControlThread.h"
namespace android {
namespace {
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 80fdb80..406ec81 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -25,7 +25,7 @@
#include <utils/Errors.h>
#include "AsyncCallRecorder.h"
-#include "EventThread.h"
+#include "Scheduler/EventThread.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -36,21 +36,28 @@
namespace android {
namespace {
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
+
class MockVSyncSource : public VSyncSource {
public:
MOCK_METHOD1(setVSyncEnabled, void(bool));
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+ MOCK_METHOD1(pauseVsyncCallback, void(bool));
};
} // namespace
class EventThreadTest : public testing::Test {
protected:
- class MockEventThreadConnection : public android::impl::EventThread::Connection {
+ class MockEventThreadConnection : public EventThreadConnection {
public:
- explicit MockEventThreadConnection(android::impl::EventThread* eventThread)
- : android::impl::EventThread::Connection(eventThread) {}
+ MockEventThreadConnection(android::impl::EventThread* eventThread,
+ ResyncCallback&& resyncCallback,
+ ResetIdleTimerCallback&& resetIdleTimerCallback)
+ : EventThreadConnection(eventThread, std::move(resyncCallback),
+ std::move(resetIdleTimerCallback)) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -65,22 +72,29 @@
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+ void expectVSyncPauseVsyncCallbackCallReceived(bool expectedPause);
VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
void expectInterceptCallReceived(nsecs_t expectedTimestamp);
void expectVsyncEventReceivedByConnection(const char* name,
ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
- void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected);
+ void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+ bool expectedConnected);
+ void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+ int32_t expectedConfigId);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+ AsyncCallRecorder<void (*)(bool)> mVSyncPauseVsyncCallbackCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
+ AsyncCallRecorder<void (*)()> mResetIdleTimerCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
MockVSyncSource mVSyncSource;
+ VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<android::impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
};
@@ -99,27 +113,42 @@
EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
.WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+ EXPECT_CALL(mVSyncSource, pauseVsyncCallback(_))
+ .WillRepeatedly(Invoke(mVSyncPauseVsyncCallbackCallRecorder.getInvocable()));
+
createThread();
mConnection = createConnection(mConnectionEventCallRecorder);
+
+ // A display must be connected for VSYNC events to be delivered.
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
EventThreadTest::~EventThreadTest() {
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());
+
+ // EventThread should unregister itself as VSyncSource callback.
+ EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
}
void EventThreadTest::createThread() {
mThread =
std::make_unique<android::impl::EventThread>(&mVSyncSource,
- mResyncCallRecorder.getInvocable(),
mInterceptVSyncCallRecorder.getInvocable(),
"unit-test-event-thread");
+
+ // EventThread should register itself as VSyncSource callback.
+ mCallback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(mCallback);
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
ConnectionEventRecorder& recorder) {
- sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get());
+ sp<MockEventThreadConnection> connection =
+ new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
+ mResetIdleTimerCallRecorder.getInvocable());
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -136,6 +165,12 @@
EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
}
+void EventThreadTest::expectVSyncPauseVsyncCallbackCallReceived(bool expectedPause) {
+ auto args = mVSyncPauseVsyncCallbackCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedPause, std::get<0>(args.value()));
+}
+
VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall();
return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
@@ -169,16 +204,26 @@
expectedCount);
}
-void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType,
+void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
- EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(expectedConnected, event.hotplug.connected);
}
+void EventThreadTest::expectConfigChangedEventReceivedByConnection(
+ PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
+ EXPECT_EQ(expectedConfigId, event.config.configId);
+}
+
namespace {
/* ------------------------------------------------------------------------
@@ -190,33 +235,43 @@
EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mResetIdleTimerCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
}
+TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
+
+ // Signal that we want the next vsync event to be posted to the connection.
+ mThread->requestNextVsync(mConnection, false);
+
+ // EventThread should not enable vsync callbacks.
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
// Signal that we want the next vsync event to be posted to the connection
- mThread->requestNextVsync(mConnection);
+ mThread->requestNextVsync(mConnection, true);
- // EventThread should immediately request a resync.
+ // EventThread should immediately reset the idle timer and request a resync.
+ EXPECT_TRUE(mResetIdleTimerCallRecorder.waitForCall().has_value());
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Use the received callback to signal a first vsync event.
// The interceptor should receive the event, as well as the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
// The interceptor should receive the event, but the the connection should
// not as it was only interested in the first.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -241,16 +296,13 @@
createConnection(secondConnectionEventRecorder);
mThread->setVsyncRate(1, secondConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the second connection. The first connection should not
// get the event.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -260,25 +312,22 @@
TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
mThread->setVsyncRate(1, mConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
- callback->onVSyncEvent(789);
+ mCallback->onVSyncEvent(789);
expectInterceptCallReceived(789);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -286,29 +335,26 @@
TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
mThread->setVsyncRate(2, mConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// The first event will be seen by the interceptor, and not the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
// The third event will be seen by the interceptor, and not the connection.
- callback->onVSyncEvent(789);
+ mCallback->onVSyncEvent(789);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
- callback->onVSyncEvent(101112);
+ mCallback->onVSyncEvent(101112);
expectInterceptCallReceived(101112);
expectVsyncEventReceivedByConnection(101112, 4u);
}
@@ -316,17 +362,14 @@
TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
mThread->setVsyncRate(1, mConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// Destroy the only (strong) reference to the connection.
mConnection = nullptr;
// The first event will be seen by the interceptor, and not the connection.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -339,21 +382,18 @@
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// The first event will be seen by the interceptor, and by the connection,
// which then returns an error.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
// A subsequent event will be seen by the interceptor and not by the
// connection.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
@@ -366,21 +406,18 @@
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
- // EventThread should enable vsync callbacks, and set a callback interface
- // pointer to use them with the VSync source.
+ // EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- auto callback = expectVSyncSetCallbackCallReceived();
- ASSERT_TRUE(callback);
// The first event will be seen by the interceptor, and by the connection,
// which then returns an non-fatal error.
- callback->onVSyncEvent(123);
+ mCallback->onVSyncEvent(123);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
// A subsequent event will be seen by the interceptor, and by the connection,
// which still then returns an non-fatal error.
- callback->onVSyncEvent(456);
+ mCallback->onVSyncEvent(456);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
@@ -393,29 +430,44 @@
expectVSyncSetPhaseOffsetCallReceived(321);
}
-TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false);
- expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false);
+TEST_F(EventThreadTest, pauseVsyncCallbackForwardsToVSyncSource) {
+ mThread->pauseVsyncCallback(true);
+ expectVSyncPauseVsyncCallbackCallReceived(true);
}
-TEST_F(EventThreadTest, postHotplugPrimaryConnect) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true);
- expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true);
+TEST_F(EventThreadTest, resumeVsyncCallbackForwardsToVSyncSource) {
+ mThread->pauseVsyncCallback(false);
+ expectVSyncPauseVsyncCallbackCallReceived(false);
+}
+
+TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
+}
+
+TEST_F(EventThreadTest, postHotplugInternalConnect) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false);
- expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false);
+ mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugExternalConnect) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true);
- expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true);
+ mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
-TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false);
- EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+TEST_F(EventThreadTest, postConfigChangedPrimary) {
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7);
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7);
+}
+
+TEST_F(EventThreadTest, postConfigChangedExternal) {
+ mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5);
+ expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
new file mode 100644
index 0000000..0739f15
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+
+#include "Scheduler/PhaseOffsets.h"
+
+namespace android {
+namespace scheduler {
+
+class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
+ nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+
+public:
+ FakePhaseOffsets() = default;
+ ~FakePhaseOffsets() = default;
+
+ nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
+ nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ PhaseOffsets::Offsets getCurrentOffsets() const override {
+ return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
+ }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {}
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& /*result*/) const override {}
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
new file mode 100644
index 0000000..ea39bf5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2018 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 "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "AsyncCallRecorder.h"
+#include "Scheduler/IdleTimer.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace scheduler {
+
+class IdleTimerTest : public testing::Test {
+protected:
+ IdleTimerTest() = default;
+ ~IdleTimerTest() override = default;
+
+ // This timeout should be used when a 3ms callback is expected.
+ // While the tests typically request a callback after 3ms, the scheduler
+ // does not always cooperate, at it can take significantly longer (observed
+ // 30ms).
+ static constexpr auto waitTimeForExpected3msCallback = 100ms;
+
+ // This timeout should be used when an 3ms callback is not expected.
+ // Note that there can be false-negatives if the callback happens later.
+ static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
+
+ AsyncCallRecorder<void (*)()> mResetTimerCallback;
+ AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
+
+ std::unique_ptr<IdleTimer> mIdleTimer;
+
+ void clearPendingCallbacks() {
+ while (mExpiredTimerCallback.waitForCall(0us).has_value()) {
+ }
+ }
+};
+
+namespace {
+TEST_F(IdleTimerTest, createAndDestroyTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {});
+}
+
+TEST_F(IdleTimerTest, startStopTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ auto startTime = std::chrono::steady_clock::now();
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ // The idle timer fires after 30ms, so there should be no callback within
+ // 25ms (waiting for a callback for the full 30ms would be problematic).
+ bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
+ // Under ideal conditions there should be no event. But occasionally
+ // it is possible that the wait just prior takes more than 30ms, and
+ // a callback is observed. We check the elapsed time since before the IdleTimer
+ // thread was started as a sanity check to not have a flakey test.
+ EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(25));
+ EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, resetTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ // Observe any event that happens in about 25ms. We don't care if one was
+ // observed or not.
+ mExpiredTimerCallback.waitForCall(25ms).has_value();
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ // There may have been a race with the reset. Clear any callbacks we
+ // received right afterwards.
+ clearPendingCallbacks();
+ // A single callback should be generated after 30ms
+ EXPECT_TRUE(
+ mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+ // After one event, it should be idle, and not generate another.
+ EXPECT_FALSE(
+ mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback * 10).has_value());
+ mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, resetBackToBackTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ // A single callback should be generated after 30ms
+ EXPECT_TRUE(
+ mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+ mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, startNotCalledTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ // The start hasn't happened, so the callback does not happen.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+ mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, idleTimerIdlesTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+ // A callback should be generated after 3ms
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ // After one event, it should be idle, and not generate another.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ // Once reset, it should generate another
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+ mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
+ mIdleTimer->stop();
+ clearPendingCallbacks();
+ mIdleTimer->reset();
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+}
+
+TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+ mIdleTimer->stop();
+ clearPendingCallbacks();
+ mIdleTimer->reset();
+
+ // No more idle events should be observed
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
new file mode 100644
index 0000000..3fb1a6e
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -0,0 +1,174 @@
+#undef LOG_TAG
+#define LOG_TAG "LayerHistoryUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include "Scheduler/LayerHistory.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android {
+
+class LayerHistoryTest : public testing::Test {
+public:
+ LayerHistoryTest();
+ ~LayerHistoryTest() override;
+
+protected:
+ std::unique_ptr<LayerHistory> mLayerHistory;
+};
+
+LayerHistoryTest::LayerHistoryTest() {
+ mLayerHistory = std::make_unique<LayerHistory>();
+}
+LayerHistoryTest::~LayerHistoryTest() {}
+
+namespace {
+TEST_F(LayerHistoryTest, simpleInsertAndGet) {
+ mLayerHistory->insert("TestLayer", 0);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
+ EXPECT_EQ(1, testMap.size());
+ auto element = testMap.find("TestLayer");
+ EXPECT_EQ("TestLayer", element->first);
+ EXPECT_EQ(0, element->second);
+
+ // Testing accessing object at an empty container will return an empty map.
+ const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
+ EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, multipleInserts) {
+ mLayerHistory->insert("TestLayer0", 0);
+ mLayerHistory->insert("TestLayer1", 1);
+ mLayerHistory->insert("TestLayer2", 2);
+ mLayerHistory->insert("TestLayer3", 3);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
+ // Because the counter was not incremented, all elements were inserted into the first
+ // container.
+ EXPECT_EQ(4, testMap.size());
+ auto element = testMap.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
+
+ element = testMap.find("TestLayer1");
+ EXPECT_EQ("TestLayer1", element->first);
+ EXPECT_EQ(1, element->second);
+
+ element = testMap.find("TestLayer2");
+ EXPECT_EQ("TestLayer2", element->first);
+ EXPECT_EQ(2, element->second);
+
+ element = testMap.find("TestLayer3");
+ EXPECT_EQ("TestLayer3", element->first);
+ EXPECT_EQ(3, element->second);
+
+ // Testing accessing object at an empty container will return an empty map.
+ const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
+ EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, incrementingCounter) {
+ mLayerHistory->insert("TestLayer0", 0);
+ mLayerHistory->incrementCounter();
+ mLayerHistory->insert("TestLayer1", 1);
+ mLayerHistory->insert("TestLayer2", 2);
+ mLayerHistory->incrementCounter();
+ mLayerHistory->insert("TestLayer3", 3);
+
+ // Because the counter was incremented, the elements were inserted into different
+ // containers.
+ // We expect the get method to access the slot at the current counter of the index
+ // is 0.
+ const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+ EXPECT_EQ(1, testMap1.size());
+ auto element = testMap1.find("TestLayer3");
+ EXPECT_EQ("TestLayer3", element->first);
+ EXPECT_EQ(3, element->second);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
+ EXPECT_EQ(2, testMap2.size());
+ element = testMap2.find("TestLayer1");
+ EXPECT_EQ("TestLayer1", element->first);
+ EXPECT_EQ(1, element->second);
+ element = testMap2.find("TestLayer2");
+ EXPECT_EQ("TestLayer2", element->first);
+ EXPECT_EQ(2, element->second);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
+ EXPECT_EQ(1, testMap3.size());
+ element = testMap3.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
+
+ // Testing accessing object at an empty container will return an empty map.
+ const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
+ EXPECT_EQ(0, emptyMap.size());
+}
+
+TEST_F(LayerHistoryTest, clearTheMap) {
+ mLayerHistory->insert("TestLayer0", 0);
+
+ const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+ EXPECT_EQ(1, testMap1.size());
+ auto element = testMap1.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
+
+ mLayerHistory->incrementCounter();
+ // The array currently contains 30 elements.
+ for (int i = 1; i < 30; ++i) {
+ mLayerHistory->insert("TestLayer0", i);
+ mLayerHistory->incrementCounter();
+ }
+ // Expect the map to be cleared.
+ const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(0);
+ EXPECT_EQ(0, testMap2.size());
+
+ mLayerHistory->insert("TestLayer30", 30);
+ const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(0);
+ element = testMap3.find("TestLayer30");
+ EXPECT_EQ("TestLayer30", element->first);
+ EXPECT_EQ(30, element->second);
+ // The original element in this location does not exist anymore.
+ element = testMap3.find("TestLayer0");
+ EXPECT_EQ(testMap3.end(), element);
+}
+
+TEST_F(LayerHistoryTest, testingGet) {
+ // The array currently contains 30 elements.
+ for (int i = 0; i < 30; ++i) {
+ const auto name = "TestLayer" + std::to_string(i);
+ mLayerHistory->insert(name, i);
+ mLayerHistory->incrementCounter();
+ }
+
+ // The counter should be set to 0, and the map at 0 should be cleared.
+ const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+ EXPECT_EQ(0, testMap1.size());
+
+ // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE
+ const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3);
+ EXPECT_EQ(1, testMap2.size());
+ auto element = testMap2.find("TestLayer27");
+ EXPECT_EQ("TestLayer27", element->first);
+ EXPECT_EQ(27, element->second);
+
+ // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first,
+ // so requesting element 40 would be the same as requesting element 10.
+ const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40);
+ EXPECT_EQ(1, testMap3.size());
+ element = testMap3.find("TestLayer20");
+ EXPECT_EQ("TestLayer20", element->first);
+ EXPECT_EQ(20, element->second);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
new file mode 100644
index 0000000..75a061b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <binder/Parcel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+class LayerMetadataTest : public testing::Test {
+public:
+ LayerMetadataTest();
+ ~LayerMetadataTest() override;
+};
+
+LayerMetadataTest::LayerMetadataTest() {
+ 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());
+}
+
+LayerMetadataTest::~LayerMetadataTest() {
+ 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());
+}
+
+TEST_F(LayerMetadataTest, testLayerMetadata) {
+ LayerMetadata metadata;
+
+ ASSERT_EQ(0, metadata.mMap.size());
+
+ // Test non-set
+ ASSERT_EQ(3, metadata.getInt32(4, 3));
+
+ // Make sure it's still unset
+ ASSERT_EQ(5, metadata.getInt32(4, 5));
+
+ metadata.setInt32(4, 2);
+ ASSERT_EQ(2, metadata.getInt32(4, 0));
+
+ // data is too small
+ metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'};
+ ASSERT_EQ(0, metadata.getInt32(2, 0));
+
+ Parcel p;
+ metadata.writeToParcel(&p);
+ LayerMetadata reconstructed;
+ reconstructed.setInt32(3, 1); // to make sure it gets replaced
+ p.setDataPosition(0);
+ reconstructed.readFromParcel(&p);
+ ASSERT_EQ(metadata.mMap, reconstructed.mMap);
+}
+
+TEST_F(LayerMetadataTest, merge) {
+ LayerMetadata metadata;
+ metadata.setInt32(4, 2);
+ metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'};
+
+ LayerMetadata second;
+ std::vector<uint8_t> someData{'c', 'd', '\0'};
+ second.mMap[2] = someData;
+ second.setInt32(6, 5);
+ second.mMap[4].clear(); // will not delete if eraseEmpty is false
+ bool changed = metadata.merge(second);
+
+ ASSERT_TRUE(changed);
+ ASSERT_EQ(3, metadata.mMap.size());
+ ASSERT_EQ(someData, second.mMap[2]);
+ ASSERT_EQ(5, metadata.getInt32(6, 0));
+ ASSERT_TRUE(metadata.mMap.at(4).empty());
+
+ LayerMetadata withErase;
+ withErase.mMap[6].clear();
+ changed = metadata.merge(withErase, true /* eraseEmpty */);
+ ASSERT_TRUE(changed);
+ ASSERT_EQ(2, metadata.mMap.size());
+ ASSERT_EQ(someData, second.mMap[2]);
+ ASSERT_EQ(true, metadata.has(4));
+
+ // test for change detection
+ LayerMetadata third;
+ third.mMap[2] = someData;
+ third.mMap[5].clear();
+ changed = metadata.merge(third);
+ ASSERT_FALSE(changed);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
new file mode 100644
index 0000000..b7e55f4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/RefreshRateStats.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/MockTimeStats.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+using testing::AtLeast;
+
+namespace android {
+namespace scheduler {
+
+class RefreshRateStatsTest : public testing::Test {
+protected:
+ static constexpr int CONFIG_ID_90 = 0;
+ static constexpr int CONFIG_ID_60 = 1;
+ static constexpr int64_t VSYNC_90 = 11111111;
+ static constexpr int64_t VSYNC_60 = 16666667;
+
+ RefreshRateStatsTest();
+ ~RefreshRateStatsTest();
+
+ void init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs);
+
+ std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+ std::shared_ptr<android::mock::TimeStats> mTimeStats;
+ std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+};
+
+RefreshRateStatsTest::RefreshRateStatsTest() {
+ 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());
+}
+
+RefreshRateStatsTest::~RefreshRateStatsTest() {
+ 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 RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
+ mTimeStats = std::make_shared<android::mock::TimeStats>();
+ mRefreshRateConfigs = std::make_shared<RefreshRateConfigs>(configs);
+ mRefreshRateStats = std::make_unique<RefreshRateStats>(mRefreshRateConfigs, mTimeStats);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ init(configs);
+
+ // There is one default config, so the refresh rates should have one item.
+ EXPECT_EQ(1, mRefreshRateStats->getTotalTimes().size());
+}
+
+TEST_F(RefreshRateStatsTest, oneConfigTest) {
+ auto display = new Hwc2::mock::Display();
+
+ auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config.setVsyncPeriod(VSYNC_90);
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ configs.push_back(config.build());
+
+ init(configs);
+
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
+
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(2, times.size());
+ EXPECT_NE(0u, times.count("ScreenOff"));
+ EXPECT_EQ(1u, times.count("90fps"));
+ EXPECT_EQ(0, times["90fps"]);
+ // Setting up tests on mobile harness can be flaky with time passing, so testing for
+ // exact time changes can result in flaxy numbers. To avoid that remember old
+ // numbers to make sure the correct values are increasing in the next test.
+ int screenOff = times["ScreenOff"];
+ int ninety = times["90fps"];
+
+ // Screen is off by default.
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_LT(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(0, times["90fps"]);
+
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(screenOff, times["ScreenOff"]);
+ EXPECT_LT(ninety, times["90fps"]);
+
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_LT(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+ // does not update refresh rates that come from the config.
+ EXPECT_LT(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+}
+
+TEST_F(RefreshRateStatsTest, twoConfigsTest) {
+ auto display = new Hwc2::mock::Display();
+
+ auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config90.setVsyncPeriod(VSYNC_90);
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ configs.push_back(config90.build());
+
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ configs.push_back(config60.build());
+
+ init(configs);
+
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
+
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(3, times.size());
+ EXPECT_NE(0u, times.count("ScreenOff"));
+ EXPECT_EQ(1u, times.count("60fps"));
+ EXPECT_EQ(0, times["60fps"]);
+ EXPECT_EQ(1u, times.count("90fps"));
+ EXPECT_EQ(0, times["90fps"]);
+ // Setting up tests on mobile harness can be flaky with time passing, so testing for
+ // exact time changes can result in flaxy numbers. To avoid that remember old
+ // numbers to make sure the correct values are increasing in the next test.
+ int screenOff = times["ScreenOff"];
+ int sixty = times["60fps"];
+ int ninety = times["90fps"];
+
+ // Screen is off by default.
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_LT(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_LT(ninety, times["90fps"]);
+
+ // When power mode is normal, time for configs updates.
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_LT(sixty, times["60fps"]);
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(screenOff, times["ScreenOff"]);
+ EXPECT_LT(ninety, times["90fps"]);
+ EXPECT_EQ(sixty, times["60fps"]);
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_EQ(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_LT(sixty, times["60fps"]);
+
+ // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+ // does not update refresh rates that come from the config.
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_LT(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(sixty, times["60fps"]);
+
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ times = mRefreshRateStats->getTotalTimes();
+ EXPECT_LT(screenOff, times["ScreenOff"]);
+ EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(sixty, times["60fps"]);
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
new file mode 100644
index 0000000..ec76538
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -0,0 +1,181 @@
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/Scheduler.h"
+#include "mock/MockEventThread.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android {
+
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = 999;
+
+class SchedulerTest : public testing::Test {
+protected:
+ class MockEventThreadConnection : public android::EventThreadConnection {
+ public:
+ explicit MockEventThreadConnection(EventThread* eventThread)
+ : EventThreadConnection(eventThread, ResyncCallback(), ResetIdleTimerCallback()) {}
+ ~MockEventThreadConnection() = default;
+
+ MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
+ MOCK_METHOD1(setVsyncRate, status_t(uint32_t count));
+ MOCK_METHOD0(requestNextVsync, void());
+ };
+
+ /**
+ * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
+ * the same.
+ */
+ class MockScheduler : public android::Scheduler {
+ public:
+ MockScheduler(std::unique_ptr<EventThread> eventThread)
+ : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {}
+
+ std::unique_ptr<EventThread> makeEventThread(
+ const char* /* connectionName */, DispSync* /* dispSync */,
+ nsecs_t /* phaseOffsetNs */,
+ impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
+ return std::move(mEventThread);
+ }
+
+ MockScheduler() = default;
+ ~MockScheduler() override = default;
+
+ std::unique_ptr<EventThread> mEventThread;
+ };
+
+ SchedulerTest();
+ ~SchedulerTest() override;
+
+ sp<Scheduler::ConnectionHandle> mConnectionHandle;
+ mock::EventThread* mEventThread;
+ std::unique_ptr<MockScheduler> mScheduler;
+ sp<MockEventThreadConnection> mEventThreadConnection;
+};
+
+SchedulerTest::SchedulerTest() {
+ 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());
+
+ std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
+ mEventThread = eventThread.get();
+ mScheduler = std::make_unique<MockScheduler>(std::move(eventThread));
+ EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
+
+ mEventThreadConnection = new MockEventThreadConnection(mEventThread);
+
+ // createConnection call to scheduler makes a createEventConnection call to EventThread. Make
+ // sure that call gets executed and returns an EventThread::Connection object.
+ EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ .WillRepeatedly(Return(mEventThreadConnection));
+
+ mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
+ impl::EventThread::InterceptVSyncsCallback());
+ EXPECT_TRUE(mConnectionHandle != nullptr);
+}
+
+SchedulerTest::~SchedulerTest() {
+ 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());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(SchedulerTest, testNullPtr) {
+ // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any
+ // exceptions, just gracefully continues.
+ sp<IDisplayEventConnection> returnedValue;
+ ASSERT_NO_FATAL_FAILURE(
+ returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
+ EXPECT_TRUE(returnedValue == nullptr);
+ EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
+ EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(nullptr, PHYSICAL_DISPLAY_ID, false));
+ ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr));
+ ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr));
+ std::string testString;
+ ASSERT_NO_FATAL_FAILURE(mScheduler->dump(nullptr, testString));
+ EXPECT_TRUE(testString == "");
+ ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(nullptr, 10));
+}
+
+TEST_F(SchedulerTest, invalidConnectionHandle) {
+ // Passing an invalid ConnectionHandle is a valid argument. The code doesn't throw any
+ // exceptions, just gracefully continues.
+ sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20);
+
+ sp<IDisplayEventConnection> returnedValue;
+ ASSERT_NO_FATAL_FAILURE(
+ returnedValue =
+ mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
+ EXPECT_TRUE(returnedValue == nullptr);
+ EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
+ EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
+
+ // 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->hotplugReceived(connectionHandle, PHYSICAL_DISPLAY_ID, false));
+
+ EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle));
+
+ EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle));
+
+ std::string testString;
+ EXPECT_CALL(*mEventThread, dump(_)).Times(0);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString));
+ EXPECT_TRUE(testString == "");
+
+ EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10));
+}
+
+TEST_F(SchedulerTest, validConnectionHandle) {
+ sp<IDisplayEventConnection> returnedValue;
+ ASSERT_NO_FATAL_FAILURE(
+ returnedValue =
+ mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
+ EXPECT_TRUE(returnedValue != nullptr);
+ ASSERT_EQ(returnedValue, mEventThreadConnection);
+
+ EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr);
+ EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr);
+
+ EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
+ ASSERT_NO_FATAL_FAILURE(
+ mScheduler->hotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+
+ EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+
+ EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+
+ std::string testString("dump");
+ EXPECT_CALL(*mEventThread, dump(testString)).Times(1);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, testString));
+ EXPECT_TRUE(testString != "");
+
+ EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
+ ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+}
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
new file mode 100644
index 0000000..5865579
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 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 "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <array>
+
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+class SchedulerUtilsTest : public testing::Test {
+public:
+ SchedulerUtilsTest() = default;
+ ~SchedulerUtilsTest() override = default;
+};
+
+namespace {
+TEST_F(SchedulerUtilsTest, calculate_mean) {
+ std::array<int64_t, 30> testArray{};
+ // Calling the function on empty array returns 0.
+ EXPECT_EQ(0, calculate_mean(testArray));
+
+ testArray[0] = 33;
+ EXPECT_EQ(1, calculate_mean(testArray));
+ testArray[1] = 33;
+ testArray[2] = 33;
+ EXPECT_EQ(3, calculate_mean(testArray));
+ testArray[3] = 42;
+ EXPECT_EQ(4, calculate_mean(testArray));
+ testArray[4] = 33;
+ EXPECT_EQ(5, calculate_mean(testArray));
+ testArray[5] = 42;
+ EXPECT_EQ(7, calculate_mean(testArray));
+ for (int i = 6; i < 30; i++) {
+ testArray[i] = 33;
+ }
+ EXPECT_EQ(33, calculate_mean(testArray));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_median) {
+ std::vector<int64_t> testVector;
+ // Calling the function on empty vector returns 0.
+ EXPECT_EQ(0, calculate_median(&testVector));
+
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(33);
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_mode) {
+ std::vector<int64_t> testVector;
+ // Calling the function on empty vector returns 0.
+ EXPECT_EQ(0, calculate_mode(testVector));
+
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(33);
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(33);
+ // 5 occurences of 33.
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ // 5 occurences of 33, 5 occurences of 42. We choose the first one.
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ // 5 occurences of 33, 6 occurences of 42.
+ EXPECT_EQ(42, calculate_mode(testVector));
+ testVector.push_back(42);
+ // 5 occurences of 33, 7 occurences of 42.
+ EXPECT_EQ(42, calculate_mode(testVector));
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
new file mode 100644
index 0000000..dcbf973
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+
+#include "Scheduler/EventThread.h"
+#include "Scheduler/Scheduler.h"
+
+namespace android {
+
+class TestableScheduler : public Scheduler {
+public:
+ TestableScheduler() : Scheduler([](bool) {}) {}
+
+ // Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection
+ // and adds it to the list of connectins. Returns the ConnectionHandle for the
+ // Scheduler::Connection. This allows plugging in mock::EventThread.
+ sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) {
+ sp<EventThreadConnection> eventThreadConnection =
+ new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ ResetIdleTimerCallback());
+ const int64_t id = sNextId++;
+ mConnections.emplace(id,
+ std::make_unique<Scheduler::Connection>(new ConnectionHandle(id),
+ eventThreadConnection,
+ std::move(eventThread)));
+ return mConnections[id]->handle;
+ }
+
+ /* ------------------------------------------------------------------------
+ * Read-write access to private data to set up preconditions and assert
+ * post-conditions.
+ */
+ auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
+ auto& mutableEventControlThread() { return mEventControlThread; }
+ auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
+ auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+
+ ~TestableScheduler() {
+ // All these pointer and container clears help ensure that GMock does
+ // not report a leaked object, since the Scheduler instance may
+ // still be referenced by something despite our best efforts to destroy
+ // it after each test is done.
+ mutableEventControlThread().reset();
+ mutablePrimaryDispSync().reset();
+ mConnections.clear();
+ };
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4c5fa99..46fd964 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -16,46 +16,214 @@
#pragma once
+#include <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ColorLayer.h"
+#include "ContainerLayer.h"
#include "DisplayDevice.h"
+#include "FakePhaseOffsets.h"
+#include "Layer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
+#include "SurfaceFlingerFactory.h"
+#include "SurfaceInterceptor.h"
+
+#include "TimeStats/TimeStats.h"
namespace android {
class EventThread;
-namespace RE {
+namespace renderengine {
+
class RenderEngine;
-}
+
+} // namespace renderengine
namespace Hwc2 {
+
class Composer;
-}
+
+} // namespace Hwc2
+
+namespace surfaceflinger::test {
+
+class Factory final : public surfaceflinger::Factory {
+public:
+ ~Factory() = default;
+
+ std::unique_ptr<DispSync> createDispSync(const char*, bool, int64_t) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ std::unique_ptr<EventControlThread> createEventControlThread(
+ std::function<void(bool)>) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ std::unique_ptr<MessageQueue> createMessageQueue() override {
+ // TODO: Use test-fixture controlled factory
+ return std::make_unique<android::impl::MessageQueue>();
+ }
+
+ std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ return std::make_unique<scheduler::FakePhaseOffsets>();
+ }
+
+ std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override {
+ // TODO: Use test-fixture controlled factory
+ return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+ }
+
+ sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
+ // TODO: Use test-fixture controlled factory
+ return new StartPropertySetThread(timestampPropertyValue);
+ }
+
+ sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
+ // TODO: Use test-fixture controlled factory
+ return new DisplayDevice(std::move(creationArgs));
+ }
+
+ sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::string requestorName) override {
+ // TODO: Use test-fixture controlled factory
+ return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+ }
+
+ void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGraphicBufferConsumer>* outConsumer,
+ bool consumerIsSurfaceFlinger) override {
+ if (!mCreateBufferQueue) return;
+ mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+ }
+
+ std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+ const sp<IGraphicBufferProducer>& producer) override {
+ if (!mCreateNativeWindowSurface) return nullptr;
+ return mCreateNativeWindowSurface(producer);
+ }
+
+ std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
+ return compositionengine::impl::createCompositionEngine();
+ }
+
+ sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ sp<ColorLayer> createColorLayer(const LayerCreationArgs&) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override {
+ // TODO: Use test-fixture controlled factory
+ return nullptr;
+ }
+
+ std::shared_ptr<TimeStats> createTimeStats() override {
+ // TODO: Use test-fixture controlled factory
+ return std::make_shared<android::impl::TimeStats>();
+ }
+
+ using CreateBufferQueueFunction =
+ std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
+ sp<IGraphicBufferConsumer>* /* outConsumer */,
+ bool /* consumerIsSurfaceFlinger */)>;
+ CreateBufferQueueFunction mCreateBufferQueue;
+
+ using CreateNativeWindowSurfaceFunction =
+ std::function<std::unique_ptr<surfaceflinger::NativeWindowSurface>(
+ const sp<IGraphicBufferProducer>&)>;
+ CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
+
+ using CreateCompositionEngineFunction =
+ std::function<std::unique_ptr<compositionengine::CompositionEngine>()>;
+ CreateCompositionEngineFunction mCreateCompositionEngine;
+};
+
+} // namespace surfaceflinger::test
class TestableSurfaceFlinger {
public:
// Extend this as needed for accessing SurfaceFlinger private (and public)
// functions.
- void setupRenderEngine(std::unique_ptr<RE::RenderEngine> renderEngine) {
- mFlinger->getBE().mRenderEngine = std::move(renderEngine);
+ void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
+ mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
}
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
- mFlinger->getBE().mHwc.reset(new HWComposer(std::move(composer)));
+ mFlinger->mCompositionEngine->setHwComposer(
+ std::make_unique<impl::HWComposer>(std::move(composer)));
}
- using CreateBufferQueueFunction = SurfaceFlinger::CreateBufferQueueFunction;
+ using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
- mFlinger->mCreateBufferQueue = f;
+ mFactory.mCreateBufferQueue = f;
}
- using CreateNativeWindowSurfaceFunction = SurfaceFlinger::CreateNativeWindowSurfaceFunction;
+ using CreateNativeWindowSurfaceFunction =
+ surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction;
void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) {
- mFlinger->mCreateNativeWindowSurface = f;
+ mFactory.mCreateNativeWindowSurface = f;
+ }
+
+ void setInternalDisplayPrimaries(const ui::DisplayPrimaries& primaries) {
+ 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; }
+
+ void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+ layer->mDrawingState.sidebandStream = sidebandStream;
+ layer->mSidebandStream = sidebandStream;
+ layer->getCompositionLayer()->editState().frontEnd.sidebandStream = sidebandStream;
+ }
+
+ void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) {
+ auto outputLayer = layer->findOutputLayerForDisplay(mFlinger->getDefaultDisplayDevice());
+ LOG_ALWAYS_FATAL_IF(!outputLayer);
+ auto& state = outputLayer->editState();
+ LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc);
+ (*state.hwc).hwcCompositionType = static_cast<Hwc2::IComposerClient::Composition>(type);
+ };
+
+ void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) {
+ layer->mPotentialCursor = potentialCursor;
+ }
+
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -64,19 +232,23 @@
return mFlinger->createDisplay(displayName, secure);
}
- auto destroyDisplay(const sp<IBinder>& display) { return mFlinger->destroyDisplay(display); }
+ auto destroyDisplay(const sp<IBinder>& displayToken) {
+ return mFlinger->destroyDisplay(displayToken);
+ }
auto resetDisplayState() { return mFlinger->resetDisplayState(); }
- auto setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId,
+ auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
+ const std::optional<DisplayId>& displayId,
const DisplayDeviceState& state,
- const sp<DisplaySurface>& dispSurface,
+ const sp<compositionengine::DisplaySurface>& dispSurface,
const sp<IGraphicBufferProducer>& producer) {
- return mFlinger->setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface,
+ return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
producer);
}
auto handleTransactionLocked(uint32_t transactionFlags) {
+ Mutex::Autolock _l(mFlinger->mStateLock);
return mFlinger->handleTransactionLocked(transactionFlags);
}
@@ -85,12 +257,39 @@
return mFlinger->onHotplugReceived(sequenceId, display, connection);
}
- auto setDisplayStateLocked(const DisplayState& s) { return mFlinger->setDisplayStateLocked(s); }
+ auto setDisplayStateLocked(const DisplayState& s) {
+ Mutex::Autolock _l(mFlinger->mStateLock);
+ return mFlinger->setDisplayStateLocked(s);
+ }
- auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); }
+ // Allow reading display state without locking, as if called on the SF main thread.
+ auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->onInitializeDisplays();
+ }
- auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) {
- return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld);
+ // Allow reading display state without locking, as if called on the SF main thread.
+ auto setPowerModeInternal(const sp<DisplayDevice>& display,
+ int mode) NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->setPowerModeInternal(display, mode);
+ }
+
+ auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what); }
+
+ auto captureScreenImplLocked(const RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer,
+ bool useIdentityTransform, bool forSystem, int* outSyncFd) {
+ return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
+ useIdentityTransform, forSystem, outSyncFd);
+ }
+
+ auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
+ const LayerVector::Visitor& visitor) {
+ return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+ }
+
+ auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
+ ui::DisplayPrimaries &primaries) {
+ return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
/* ------------------------------------------------------------------------
@@ -99,8 +298,10 @@
const auto& getAnimFrameTracker() const { return mFlinger->mAnimFrameTracker; }
const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; }
- const auto& getHWVsyncAvailable() const { return mFlinger->mHWVsyncAvailable; }
const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; }
+ auto& getHwComposer() const {
+ return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
+ }
const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
@@ -111,25 +312,31 @@
auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; }
- auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; }
+ auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; }
+
auto& mutableCurrentState() { return mFlinger->mCurrentState; }
- auto& mutableDisplays() { return mFlinger->mDisplays; }
auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
+ auto& mutableDisplays() { return mFlinger->mDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
- auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
auto& mutableEventQueue() { return mFlinger->mEventQueue; }
- auto& mutableEventThread() { return mFlinger->mEventThread; }
- auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; }
+ auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
auto& mutableInterceptor() { return mFlinger->mInterceptor; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
- auto& mutablePrimaryHWVsyncEnabled() { return mFlinger->mPrimaryHWVsyncEnabled; }
+ auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; }
+ auto& mutableTexturePool() { return mFlinger->mTexturePool; }
auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
+ auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
- auto& mutableHwcDisplayData() { return mFlinger->getBE().mHwc->mDisplayData; }
- auto& mutableHwcDisplaySlots() { return mFlinger->getBE().mHwc->mHwcDisplaySlots; }
+ auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
+ auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
+ auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
+ auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+ auto& mutableScheduler() { return mFlinger->mScheduler; }
+ auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
+ auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
@@ -137,30 +344,25 @@
// still be referenced by something despite our best efforts to destroy
// it after each test is done.
mutableDisplays().clear();
- mutableEventControlThread().reset();
+ mutableCurrentState().displays.clear();
+ mutableDrawingState().displays.clear();
mutableEventQueue().reset();
- mutableEventThread().reset();
mutableInterceptor().reset();
- mFlinger->getBE().mHwc.reset();
- mFlinger->getBE().mRenderEngine.reset();
+ mutableScheduler().reset();
+ mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+ mFlinger->mCompositionEngine->setRenderEngine(
+ std::unique_ptr<renderengine::RenderEngine>());
}
/* ------------------------------------------------------------------------
* Wrapper classes for Read-write access to private data to set up
* preconditions and assert post-conditions.
*/
- class FakePowerAdvisor : public Hwc2::PowerAdvisor {
- public:
- FakePowerAdvisor() = default;
- ~FakePowerAdvisor() override = default;
- void setExpensiveRenderingExpected(hwc2_display_t, bool) override { }
- };
-
- struct HWC2Display : public HWC2::Display {
- HWC2Display(Hwc2::Composer& composer, Hwc2::PowerAdvisor& advisor,
+ struct HWC2Display : public HWC2::impl::Display {
+ HWC2Display(Hwc2::Composer& composer,
const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
HWC2::DisplayType type)
- : HWC2::Display(composer, advisor, capabilities, id, type) {}
+ : HWC2::impl::Display(composer, capabilities, id, type) {}
~HWC2Display() {
// Prevents a call to disable vsyncs.
mType = HWC2::DisplayType::Invalid;
@@ -168,6 +370,7 @@
auto& mutableIsConnected() { return this->mIsConnected; }
auto& mutableConfigs() { return this->mConfigs; }
+ auto& mutableLayers() { return this->mLayers; }
};
class FakeHwcDisplayInjector {
@@ -179,8 +382,9 @@
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
- FakeHwcDisplayInjector(DisplayDevice::DisplayType type, HWC2::DisplayType hwcDisplayType)
- : mType(type), mHwcDisplayType(hwcDisplayType) {}
+ FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType,
+ bool isPrimary)
+ : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
auto& setHwcDisplayId(hwc2_display_t displayId) {
mHwcDisplayId = displayId;
@@ -222,14 +426,7 @@
return *this;
}
- auto& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
- mPowerAdvisor = powerAdvisor;
- return *this;
- }
-
void inject(TestableSurfaceFlinger* flinger, Hwc2::Composer* composer) {
- static FakePowerAdvisor defaultPowerAdvisor;
- if (mPowerAdvisor == nullptr) mPowerAdvisor = &defaultPowerAdvisor;
static const std::unordered_set<HWC2::Capability> defaultCapabilities;
if (mCapabilities == nullptr) mCapabilities = &defaultCapabilities;
@@ -237,8 +434,8 @@
// not refer to an instance owned by FakeHwcDisplayInjector. This
// class has temporary lifetime, while the constructed HWC2::Display
// is much longer lived.
- auto display = std::make_unique<HWC2Display>(*composer, *mPowerAdvisor, *mCapabilities,
- mHwcDisplayId, mHwcDisplayType);
+ auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
+ mHwcDisplayType);
auto config = HWC2::Display::Config::Builder(*display, mActiveConfig);
config.setWidth(mWidth);
@@ -249,17 +446,22 @@
display->mutableConfigs().emplace(mActiveConfig, config.build());
display->mutableIsConnected() = true;
- ASSERT_TRUE(flinger->mutableHwcDisplayData().size() > static_cast<size_t>(mType));
- flinger->mutableHwcDisplayData()[mType].reset();
- flinger->mutableHwcDisplayData()[mType].hwcDisplay = display.get();
- flinger->mutableHwcDisplaySlots().emplace(mHwcDisplayId, mType);
+ flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+
+ if (mHwcDisplayType == HWC2::DisplayType::Physical) {
+ flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
+ (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
+ : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
+ }
flinger->mFakeHwcDisplays.push_back(std::move(display));
}
private:
- DisplayDevice::DisplayType mType;
- HWC2::DisplayType mHwcDisplayType;
+ const DisplayId mDisplayId;
+ const HWC2::DisplayType mHwcDisplayType;
+ const bool mIsPrimary;
+
hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
int32_t mWidth = DEFAULT_WIDTH;
int32_t mHeight = DEFAULT_HEIGHT;
@@ -268,14 +470,17 @@
int32_t mDpiY = DEFAULT_DPI;
int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr;
- Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
};
class FakeDisplayDeviceInjector {
public:
- FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger, DisplayDevice::DisplayType type,
- int hwcId)
- : mFlinger(flinger), mType(type), mHwcId(hwcId) {}
+ FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
+ const std::optional<DisplayId>& displayId, bool isVirtual,
+ bool isPrimary)
+ : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), mDisplayToken, displayId) {
+ mCreationArgs.isVirtual = isVirtual;
+ mCreationArgs.isPrimary = isPrimary;
+ }
sp<IBinder> token() const { return mDisplayToken; }
@@ -295,44 +500,53 @@
return mFlinger.mutableCurrentState().displays.valueFor(mDisplayToken);
}
- auto& mutableDisplayDevice() { return mFlinger.mutableDisplays().valueFor(mDisplayToken); }
+ auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
auto& setNativeWindow(const sp<ANativeWindow>& nativeWindow) {
- mNativeWindow = nativeWindow;
+ mCreationArgs.nativeWindow = nativeWindow;
return *this;
}
- auto& setDisplaySurface(const sp<DisplaySurface>& displaySurface) {
- mDisplaySurface = displaySurface;
- return *this;
- }
-
- auto& setRenderSurface(std::unique_ptr<RE::Surface> renderSurface) {
- mRenderSurface = std::move(renderSurface);
+ auto& setDisplaySurface(const sp<compositionengine::DisplaySurface>& displaySurface) {
+ mCreationArgs.displaySurface = displaySurface;
return *this;
}
auto& setSecure(bool secure) {
- mSecure = secure;
+ mCreationArgs.isSecure = secure;
+ return *this;
+ }
+
+ auto& setPowerMode(int mode) {
+ mCreationArgs.initialPowerMode = mode;
+ return *this;
+ }
+
+ auto& setHwcColorModes(
+ const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>
+ hwcColorModes) {
+ mCreationArgs.hwcColorModes = hwcColorModes;
+ return *this;
+ }
+
+ auto& setHasWideColorGamut(bool hasWideColorGamut) {
+ mCreationArgs.hasWideColorGamut = hasWideColorGamut;
return *this;
}
sp<DisplayDevice> inject() {
- std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hdrAndRenderIntents;
- sp<DisplayDevice> device =
- new DisplayDevice(mFlinger.mFlinger.get(), mType, mHwcId, mSecure,
- mDisplayToken, mNativeWindow, mDisplaySurface,
- std::move(mRenderSurface), 0, 0,
- DisplayState::eOrientationDefault, false, HdrCapabilities(),
- 0, hdrAndRenderIntents, HWC_POWER_MODE_NORMAL);
- mFlinger.mutableDisplays().add(mDisplayToken, device);
+ DisplayDeviceState state;
+ state.displayId = mCreationArgs.isVirtual ? std::nullopt : mCreationArgs.displayId;
+ state.isSecure = mCreationArgs.isSecure;
- DisplayDeviceState state(mType, mSecure);
+ sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs));
+ mFlinger.mutableDisplays().emplace(mDisplayToken, device);
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
- if (mType >= DisplayDevice::DISPLAY_PRIMARY && mType < DisplayDevice::DISPLAY_VIRTUAL) {
- mFlinger.mutableBuiltinDisplays()[mType] = mDisplayToken;
+ if (!mCreationArgs.isVirtual) {
+ LOG_ALWAYS_FATAL_IF(!state.displayId);
+ mFlinger.mutablePhysicalDisplayTokens()[*state.displayId] = mDisplayToken;
}
return device;
@@ -341,15 +555,11 @@
private:
TestableSurfaceFlinger& mFlinger;
sp<BBinder> mDisplayToken = new BBinder();
- DisplayDevice::DisplayType mType;
- int mHwcId;
- sp<ANativeWindow> mNativeWindow;
- sp<DisplaySurface> mDisplaySurface;
- std::unique_ptr<RE::Surface> mRenderSurface;
- bool mSecure = false;
+ DisplayDeviceCreationArgs mCreationArgs;
};
- sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(SurfaceFlinger::SkipInitialization);
+ surfaceflinger::test::Factory mFactory;
+ sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
// We need to keep a reference to these so they are properly destroyed.
std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
new file mode 100644
index 0000000..f35758d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2018 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"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <random>
+#include <unordered_set>
+
+#include "TimeStats/TimeStats.h"
+
+#include "libsurfaceflinger_unittest_main.h"
+
+using namespace android::surfaceflinger;
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+using testing::Contains;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
+// clang-format off
+#define FMT_PROTO true
+#define FMT_STRING false
+#define LAYER_ID_0 0
+#define LAYER_ID_1 1
+#define LAYER_ID_INVALID -1
+#define NUM_LAYERS 1
+#define NUM_LAYERS_INVALID "INVALID"
+
+enum InputCommand : int32_t {
+ ENABLE = 0,
+ DISABLE = 1,
+ CLEAR = 2,
+ DUMP_ALL = 3,
+ DUMP_MAXLAYERS_1 = 4,
+ DUMP_MAXLAYERS_INVALID = 5,
+ INPUT_COMMAND_BEGIN = ENABLE,
+ INPUT_COMMAND_END = DUMP_MAXLAYERS_INVALID,
+ INPUT_COMMAND_RANGE = INPUT_COMMAND_END - INPUT_COMMAND_BEGIN + 1,
+};
+
+enum TimeStamp : int32_t {
+ POST = 0,
+ ACQUIRE = 1,
+ ACQUIRE_FENCE = 2,
+ LATCH = 3,
+ DESIRED = 4,
+ PRESENT = 5,
+ PRESENT_FENCE = 6,
+ TIME_STAMP_BEGIN = POST,
+ TIME_STAMP_END = PRESENT,
+ TIME_STAMP_RANGE = TIME_STAMP_END - TIME_STAMP_BEGIN + 1,
+};
+
+static const TimeStamp NORMAL_SEQUENCE[] = {
+ TimeStamp::POST,
+ TimeStamp::ACQUIRE,
+ TimeStamp::LATCH,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT,
+};
+
+static const TimeStamp NORMAL_SEQUENCE_2[] = {
+ TimeStamp::POST,
+ TimeStamp::ACQUIRE_FENCE,
+ TimeStamp::LATCH,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT_FENCE,
+};
+
+static const TimeStamp UNORDERED_SEQUENCE[] = {
+ TimeStamp::ACQUIRE,
+ TimeStamp::LATCH,
+ TimeStamp::POST,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT,
+};
+
+static const TimeStamp INCOMPLETE_SEQUENCE[] = {
+ TimeStamp::POST,
+};
+// clang-format on
+
+class TimeStatsTest : public testing::Test {
+public:
+ TimeStatsTest() {
+ 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());
+ }
+
+ ~TimeStatsTest() {
+ 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());
+ }
+
+ std::string inputCommand(InputCommand cmd, bool useProto);
+
+ void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+
+ int32_t genRandomInt32(int32_t begin, int32_t end);
+
+ template <size_t N>
+ void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
+ nsecs_t ts) {
+ for (size_t i = 0; i < N; i++, ts += 1000000) {
+ setTimeStamp(sequence[i], id, frameNumber, ts);
+ }
+ }
+
+ std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
+ std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
+};
+
+std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
+ std::string result;
+ Vector<String16> args;
+
+ switch (cmd) {
+ case InputCommand::ENABLE:
+ args.push_back(String16("-enable"));
+ break;
+ case InputCommand::DISABLE:
+ args.push_back(String16("-disable"));
+ break;
+ case InputCommand::CLEAR:
+ args.push_back(String16("-clear"));
+ break;
+ case InputCommand::DUMP_ALL:
+ args.push_back(String16("-dump"));
+ break;
+ case InputCommand::DUMP_MAXLAYERS_1:
+ args.push_back(String16("-dump"));
+ args.push_back(String16("-maxlayers"));
+ args.push_back(String16(std::to_string(NUM_LAYERS).c_str()));
+ break;
+ case InputCommand::DUMP_MAXLAYERS_INVALID:
+ args.push_back(String16("-dump"));
+ args.push_back(String16("-maxlayers"));
+ args.push_back(String16(NUM_LAYERS_INVALID));
+ break;
+ default:
+ ALOGD("Invalid control command");
+ }
+
+ EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, result));
+ return result;
+}
+
+static std::string genLayerName(int32_t layerID) {
+ return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+}
+
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+ switch (type) {
+ case TimeStamp::POST:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+ break;
+ case TimeStamp::ACQUIRE:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::ACQUIRE_FENCE:
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setAcquireFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ break;
+ case TimeStamp::LATCH:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setLatchTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::DESIRED:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::PRESENT:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::PRESENT_FENCE:
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ break;
+ default:
+ ALOGD("Invalid timestamp type");
+ }
+}
+
+int32_t TimeStatsTest::genRandomInt32(int32_t begin, int32_t end) {
+ std::uniform_int_distribution<int32_t> distr(begin, end);
+ return distr(mRandomEngine);
+}
+
+TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ ASSERT_TRUE(mTimeStats->isEnabled());
+
+ EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
+ ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, canIncreaseGlobalStats) {
+ constexpr size_t TOTAL_FRAMES = 5;
+ constexpr size_t MISSED_FRAMES = 4;
+ constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+ }
+ for (size_t i = 0; i < MISSED_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+ }
+ for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+ }
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_TRUE(globalProto.has_total_frames());
+ EXPECT_EQ(TOTAL_FRAMES, globalProto.total_frames());
+ ASSERT_TRUE(globalProto.has_missed_frames());
+ EXPECT_EQ(MISSED_FRAMES, globalProto.missed_frames());
+ ASSERT_TRUE(globalProto.has_client_composition_frames());
+ EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)));
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_OFF));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(6000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(8000000)));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.present_to_present_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.present_to_present().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ EXPECT_EQ(2, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+ ASSERT_EQ(6, layerProto.deltas_size());
+ for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+ ASSERT_EQ(1, deltaProto.histograms_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ if ("post2acquire" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("post2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(4, histogramProto.time_millis());
+ } else if ("acquire2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(3, histogramProto.time_millis());
+ } else if ("latch2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("desired2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("present2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else {
+ FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+ }
+ }
+}
+
+TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_INVALID, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_INVALID, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertMultipleLayersTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(2, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertUnorderedLayerTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(UNORDERED_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+ ASSERT_EQ(6, layerProto.deltas_size());
+ for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+ ASSERT_EQ(1, deltaProto.histograms_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ if ("post2acquire" == deltaProto.delta_name()) {
+ EXPECT_EQ(0, histogramProto.time_millis());
+ } else if ("post2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("acquire2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("latch2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("desired2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("present2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else {
+ FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+ }
+ }
+}
+
+TEST_F(TimeStatsTest, recordRefreshRateNewConfigs) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint32_t fpsOne = 30;
+ uint32_t fpsTwo = 90;
+ uint64_t millisOne = 5000;
+ uint64_t millisTwo = 7000;
+
+ mTimeStats->recordRefreshRate(fpsOne, ms2ns(millisOne));
+ mTimeStats->recordRefreshRate(fpsTwo, ms2ns(millisTwo));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ SFTimeStatsDisplayConfigBucketProto expectedBucketOne;
+ SFTimeStatsDisplayConfigProto* expectedConfigOne = expectedBucketOne.mutable_config();
+ expectedConfigOne->set_fps(fpsOne);
+ expectedBucketOne.set_duration_millis(millisOne);
+
+ SFTimeStatsDisplayConfigBucketProto expectedBucketTwo;
+ SFTimeStatsDisplayConfigProto* expectedConfigTwo = expectedBucketTwo.mutable_config();
+ expectedConfigTwo->set_fps(fpsTwo);
+ expectedBucketTwo.set_duration_millis(millisTwo);
+
+ EXPECT_THAT(globalProto.display_config_stats(), SizeIs(2));
+
+ std::unordered_set<uint32_t> seen_fps;
+ for (const auto& bucket : globalProto.display_config_stats()) {
+ seen_fps.emplace(bucket.config().fps());
+ if (fpsOne == bucket.config().fps()) {
+ EXPECT_EQ(millisOne, bucket.duration_millis());
+ } else if (fpsTwo == bucket.config().fps()) {
+ EXPECT_EQ(millisTwo, bucket.duration_millis());
+ } else {
+ FAIL() << "Unknown fps: " << bucket.config().fps();
+ }
+ }
+ EXPECT_THAT(seen_fps, UnorderedElementsAre(fpsOne, fpsTwo));
+}
+
+TEST_F(TimeStatsTest, recordRefreshRateUpdatesConfig) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint32_t fps = 30;
+ uint64_t millisOne = 5000;
+ uint64_t millisTwo = 7000;
+
+ mTimeStats->recordRefreshRate(fps, ms2ns(millisOne));
+ mTimeStats->recordRefreshRate(fps, ms2ns(millisTwo));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+ EXPECT_THAT(globalProto.display_config_stats(), SizeIs(1));
+ EXPECT_EQ(fps, globalProto.display_config_stats().Get(0).config().fps());
+ EXPECT_EQ(millisOne + millisTwo, globalProto.display_config_stats().Get(0).duration_millis());
+}
+
+TEST_F(TimeStatsTest, canRemoveTimeRecord) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(0, 2));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canRecoverFromIncompleteTimeRecordError) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint64_t frameNumber = 1;
+ nsecs_t ts = 1000000;
+ insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ for (size_t i = 0; i < impl::TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+ frameNumber++;
+ ts += 1000000;
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
+ }
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, layerTimeStatsOnDestroy) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(0));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canClearTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.total_frames());
+ EXPECT_EQ(0, globalProto.missed_frames());
+ EXPECT_EQ(0, globalProto.client_composition_frames());
+ EXPECT_EQ(0, globalProto.present_to_present_size());
+ EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 3, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(
+ globalProto.ParseFromString(inputCommand(InputCommand::DUMP_MAXLAYERS_1, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_1), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(2, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(
+ inputCommand(InputCommand::DUMP_MAXLAYERS_INVALID, FMT_PROTO)));
+
+ ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canSurviveMonkey) {
+ if (g_noSlowTests) {
+ GTEST_SKIP();
+ }
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < 10000000; ++i) {
+ const int32_t layerID = genRandomInt32(-1, 10);
+ const int32_t frameNumber = genRandomInt32(1, 10);
+ switch (genRandomInt32(0, 100)) {
+ case 0:
+ ALOGV("removeTimeRecord");
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+ continue;
+ case 1:
+ ALOGV("onDestroy");
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+ continue;
+ }
+ TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
+ const int32_t ts = genRandomInt32(1, 1000000000);
+ ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
+ setTimeStamp(type, layerID, frameNumber, ts);
+ }
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
new file mode 100644
index 0000000..ed628b8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2018 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 <sched.h>
+
+#include <processgroup/sched_policy.h>
+#include <system/graphics.h>
+
+#include "libsurfaceflinger_unittest_main.h"
+
+// ------------------------------------------------------------------------
+// To pass extra command line arguments to the Google Test executable from
+// atest, you have to use this somewhat verbose syntax:
+//
+// clang-format off
+//
+// atest libsurfaceflinger_unittest -- --module-arg libsurfaceflinger_unittest:native-test-flag:<--flag>[:<value>]
+//
+// For example:
+//
+// atest libsurfaceflinger_unittest -- --module-arg libsurfaceflinger_unittest:native-test-flag:--no-slow
+//
+// clang-format on
+// ------------------------------------------------------------------------
+
+// Set to true if "--no-slow" is passed to the test.
+bool g_noSlowTests = false;
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // The SurfaceFlinger implementation assumes that threads resume
+ // execution as quickly as possible once they become unblocked.
+ // (These same calls are made in main_surfaceflinger.cpp)
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+ set_sched_policy(0, SP_FOREGROUND);
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--no-slow") == 0) {
+ g_noSlowTests = true;
+ }
+ }
+
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h
index e6ac6bf..e742c50 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -14,14 +14,7 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#pragma once
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
-
-} // namespace mock
-} // namespace android
+// Set to true if "--no-slow" is passed to the test.
+extern bool g_noSlowTests;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 267670a..bb92020 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -28,12 +28,12 @@
namespace mock {
using android::hardware::graphics::common::V1_0::ColorTransform;
-using android::hardware::graphics::common::V1_0::Hdr;
using android::hardware::graphics::common::V1_0::Transform;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::Dataspace;
-using android::hardware::graphics::common::V1_1::PixelFormat;
using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::hardware::graphics::common::V1_2::ColorMode;
+using android::hardware::graphics::common::V1_2::Dataspace;
+using android::hardware::graphics::common::V1_2::Hdr;
+using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::composer::V2_1::Config;
using android::hardware::graphics::composer::V2_1::Display;
@@ -41,7 +41,7 @@
using android::hardware::graphics::composer::V2_1::IComposer;
using android::hardware::graphics::composer::V2_1::IComposerCallback;
using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_2::IComposerClient;
+using android::hardware::graphics::composer::V2_3::IComposerClient;
class Composer : public Hwc2::Composer {
public:
@@ -74,9 +74,10 @@
MOCK_METHOD2(getDisplayType, Error(Display, IComposerClient::DisplayType*));
MOCK_METHOD2(getDozeSupport, Error(Display, bool*));
MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*));
- MOCK_METHOD2(getPerFrameMetadataKeys,
- Error(Display, std::vector<IComposerClient::PerFrameMetadataKey>*));
+ MOCK_METHOD1(getPerFrameMetadataKeys,
+ std::vector<IComposerClient::PerFrameMetadataKey>(Display));
MOCK_METHOD2(getDataspaceSaturationMatrix, Error(Dataspace, mat4*));
+ MOCK_METHOD3(getDisplayIdentificationData, Error(Display, uint8_t*, std::vector<uint8_t>*));
MOCK_METHOD3(getReleaseFences, Error(Display, std::vector<Layer>*, std::vector<int>*));
MOCK_METHOD2(presentDisplay, Error(Display, int*));
MOCK_METHOD2(setActiveConfig, Error(Display, Config));
@@ -111,6 +112,17 @@
MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t));
MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
+ MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
+ MOCK_METHOD4(getDisplayedContentSamplingAttributes,
+ Error(Display, PixelFormat*, Dataspace*, uint8_t*));
+ MOCK_METHOD4(setDisplayContentSamplingEnabled, Error(Display, bool, uint8_t, uint64_t));
+ MOCK_METHOD4(getDisplayedContentSample,
+ Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
+ MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+ MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
+ Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
+ MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+ MOCK_METHOD2(getDisplayBrightnessSupport, Error(Display, bool*));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
similarity index 77%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
index e6ac6bf..2ec37c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/DisplayHardware/MockDisplay.h"
namespace android {
+namespace Hwc2 {
namespace mock {
// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+Display::Display() = default;
+Display::~Display() = default;
} // namespace mock
-} // namespace android
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
new file mode 100644
index 0000000..6dc28bc
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+
+#include "DisplayHardware/HWC2.h"
+
+using HWC2::Error;
+using HWC2::Layer;
+
+namespace android {
+namespace Hwc2 {
+namespace mock {
+
+class Display : public HWC2::Display {
+public:
+ Display();
+ ~Display();
+
+ MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+ MOCK_CONST_METHOD0(isConnected, bool());
+ MOCK_METHOD1(setConnected, void(bool));
+ MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<HWC2::DisplayCapability>&());
+
+ MOCK_METHOD0(acceptChanges, Error());
+ MOCK_METHOD1(createLayer, Error(Layer**));
+ MOCK_METHOD1(destroyLayer, Error(Layer*));
+ MOCK_CONST_METHOD1(getActiveConfig, Error(std::shared_ptr<const Config>*));
+ MOCK_CONST_METHOD1(getActiveConfigIndex, Error(int* outIndex));
+ MOCK_METHOD1(getChangedCompositionTypes, Error(std::unordered_map<Layer*, HWC2::Composition>*));
+ MOCK_CONST_METHOD1(getColorModes, Error(std::vector<android::ui::ColorMode>*));
+
+ MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+ MOCK_CONST_METHOD2(getRenderIntents,
+ Error(android::ui::ColorMode, std::vector<android::ui::RenderIntent>*));
+ MOCK_METHOD2(getDataspaceSaturationMatrix, Error(android::ui::Dataspace, android::mat4*));
+ MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
+
+ MOCK_CONST_METHOD1(getName, Error(std::string*));
+ MOCK_METHOD2(getRequests,
+ Error(HWC2::DisplayRequest*, std::unordered_map<Layer*, HWC2::LayerRequest>*));
+ MOCK_CONST_METHOD1(getType, Error(HWC2::DisplayType*));
+ MOCK_CONST_METHOD1(supportsDoze, Error(bool*));
+ MOCK_CONST_METHOD1(getHdrCapabilities, Error(android::HdrCapabilities*));
+ MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
+ Error(android::ui::PixelFormat*, android::ui::Dataspace*, uint8_t*));
+ MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, Error(bool, uint8_t, uint64_t));
+ MOCK_CONST_METHOD3(getDisplayedContentSample,
+ Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
+ MOCK_CONST_METHOD1(getReleaseFences,
+ Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
+ MOCK_METHOD1(present, Error(android::sp<android::Fence>*));
+ MOCK_METHOD1(setActiveConfig, Error(const std::shared_ptr<const HWC2::Display::Config>&));
+ MOCK_METHOD4(setClientTarget,
+ Error(uint32_t, const android::sp<android::GraphicBuffer>&,
+ const android::sp<android::Fence>&, android::ui::Dataspace));
+ MOCK_METHOD2(setColorMode, Error(android::ui::ColorMode, android::ui::RenderIntent));
+ MOCK_METHOD2(setColorTransform, Error(const android::mat4&, android_color_transform_t));
+ MOCK_METHOD2(setOutputBuffer,
+ Error(const android::sp<android::GraphicBuffer>&,
+ const android::sp<android::Fence>&));
+ MOCK_METHOD1(setPowerMode, Error(HWC2::PowerMode));
+ MOCK_METHOD1(setVsyncEnabled, Error(HWC2::Vsync));
+ MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*));
+ MOCK_METHOD4(presentOrValidate,
+ Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
+ MOCK_CONST_METHOD1(setDisplayBrightness, Error(float));
+};
+
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index dc6d83b..7c65f95 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,7 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
- MOCK_METHOD2(setExpensiveRenderingExpected, void(hwc2_display_t displayId, bool expected));
+ MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
new file mode 100644
index 0000000..f6c4f62
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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 "mock/MockDispSync.h"
+#include <thread>
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+DispSync::DispSync() = default;
+DispSync::~DispSync() = default;
+
+status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback,
+ nsecs_t /*lastCallbackTime*/) {
+ if (mCallback.callback != nullptr) {
+ return BAD_VALUE;
+ }
+
+ mCallback = {callback, phase};
+ return NO_ERROR;
+}
+status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) {
+ if (mCallback.callback != callback) {
+ return BAD_VALUE;
+ }
+
+ mCallback = {nullptr, 0};
+ return NO_ERROR;
+}
+
+status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
+ if (mCallback.callback != callback) {
+ return BAD_VALUE;
+ }
+
+ mCallback.phase = phase;
+ return NO_ERROR;
+}
+
+void DispSync::triggerCallback() {
+ if (mCallback.callback == nullptr) return;
+
+ mCallback.callback->onDispSyncEvent(
+ std::chrono::steady_clock::now().time_since_epoch().count());
+}
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
new file mode 100644
index 0000000..afcda5b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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 <gmock/gmock.h>
+
+#include "Scheduler/DispSync.h"
+
+namespace android {
+namespace mock {
+
+class DispSync : public android::DispSync {
+public:
+ DispSync();
+ ~DispSync() override;
+
+ MOCK_METHOD0(reset, void());
+ MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD0(beginResync, void());
+ MOCK_METHOD1(addResyncSample, bool(nsecs_t));
+ MOCK_METHOD0(endResync, void());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(getPeriod, nsecs_t());
+ MOCK_METHOD1(setRefreshSkipCount, void(int));
+ MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int));
+ MOCK_METHOD1(setIgnorePresentFences, void(bool));
+ MOCK_METHOD0(expectedPresentTime, nsecs_t());
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+
+ status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
+ nsecs_t lastCallbackTime) override;
+ status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override;
+ status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
+
+ nsecs_t getCallbackPhase() { return mCallback.phase; }
+
+ void triggerCallback();
+
+private:
+ struct CallbackType {
+ Callback* callback = nullptr;
+ nsecs_t phase;
+ };
+ CallbackType mCallback;
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
index 8ac09a9..6ef352a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h
@@ -18,7 +18,7 @@
#include <gmock/gmock.h>
-#include "EventControlThread.h"
+#include "Scheduler/EventControlThread.h"
namespace android {
namespace mock {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index e6ea663..cb4a300 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -18,7 +18,7 @@
#include <gmock/gmock.h>
-#include "EventThread.h"
+#include "Scheduler/EventThread.h"
namespace android {
namespace mock {
@@ -28,12 +28,19 @@
EventThread();
~EventThread() override;
- MOCK_CONST_METHOD0(createEventConnection, sp<BnDisplayEventConnection>());
+ MOCK_CONST_METHOD2(createEventConnection,
+ sp<EventThreadConnection>(ResyncCallback, ResetIdleTimerCallback));
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
- MOCK_METHOD2(onHotplugReceived, void(int, bool));
- MOCK_CONST_METHOD1(dump, void(String8&));
+ MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
+ MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
+ MOCK_METHOD1(registerDisplayEventConnection,
+ status_t(const sp<android::EventThreadConnection> &));
+ MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
+ MOCK_METHOD2(requestNextVsync, void(const sp<android::EventThreadConnection> &, bool));
+ MOCK_METHOD1(pauseVsyncCallback, void(bool));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index cf07cf7..e22d3e8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,7 +18,8 @@
#include <gmock/gmock.h>
-#include "MessageQueue.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/MessageQueue.h"
namespace android {
namespace mock {
@@ -29,10 +30,12 @@
~MessageQueue() override;
MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
- MOCK_METHOD1(setEventThread, void(android::EventThread*));
+ MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback));
+ MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
MOCK_METHOD0(waitMessage, void());
MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
MOCK_METHOD0(invalidate, void());
+ MOCK_METHOD0(invalidateForHWC, void());
MOCK_METHOD0(refresh, void());
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp
index 25ff39b..9cc6d38 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2018 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
@@ -16,12 +16,10 @@
#include "mock/MockNativeWindowSurface.h"
-namespace android {
-namespace mock {
+namespace android::surfaceflinger::mock {
// Explicit default instantiation is recommended.
NativeWindowSurface::NativeWindowSurface() = default;
NativeWindowSurface::~NativeWindowSurface() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::surfaceflinger::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h
index 88d1a9f..7bc1151 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h
@@ -20,19 +20,17 @@
#include <system/window.h> // for ANativeWindow
-#include "SurfaceFlinger.h" // for base NativeWindowSurface
+#include "NativeWindowSurface.h"
-namespace android {
-namespace mock {
+namespace android::surfaceflinger::mock {
-class NativeWindowSurface : public android::NativeWindowSurface {
+class NativeWindowSurface : public surfaceflinger::NativeWindowSurface {
public:
NativeWindowSurface();
- ~NativeWindowSurface();
+ ~NativeWindowSurface() override;
MOCK_CONST_METHOD0(getNativeWindow, sp<ANativeWindow>());
MOCK_METHOD0(preallocateBuffers, void());
};
-} // namespace mock
-} // namespace android
+} // namespace android::surfaceflinger::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
similarity index 77%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
index e6ac6bf..d686939 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/MockTimeStats.h"
namespace android {
namespace mock {
// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+TimeStats::TimeStats() = default;
+TimeStats::~TimeStats() = default;
} // namespace mock
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
new file mode 100644
index 0000000..08fdb9d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+
+#include "TimeStats/TimeStats.h"
+
+namespace android {
+namespace mock {
+
+class TimeStats : public android::TimeStats {
+public:
+ TimeStats();
+ ~TimeStats() override;
+
+ MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
+ MOCK_METHOD0(isEnabled, bool());
+ MOCK_METHOD0(incrementTotalFrames, void());
+ MOCK_METHOD0(incrementMissedFrames, void());
+ MOCK_METHOD0(incrementClientCompositionFrames, void());
+ MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+ MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
+ MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(onDestroy, void(int32_t));
+ MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
+ MOCK_METHOD1(setPowerMode, void(int32_t));
+ MOCK_METHOD2(recordRefreshRate, void(uint32_t, nsecs_t));
+ MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
deleted file mode 100644
index ac08293..0000000
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2018 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 <gmock/gmock.h>
-
-#include "RenderEngine/Image.h"
-#include "RenderEngine/Mesh.h"
-#include "RenderEngine/RenderEngine.h"
-#include "RenderEngine/Surface.h"
-#include "RenderEngine/Texture.h"
-
-namespace android {
-namespace RE {
-namespace mock {
-
-class RenderEngine : public RE::RenderEngine {
-public:
- RenderEngine();
- ~RenderEngine() override;
-
- MOCK_METHOD0(createSurface, std::unique_ptr<RE::Surface>());
- MOCK_METHOD0(createImage, std::unique_ptr<RE::Image>());
- MOCK_CONST_METHOD0(primeCache, void());
- MOCK_METHOD1(dump, void(String8&));
- MOCK_CONST_METHOD0(supportsImageCrop, bool());
- MOCK_CONST_METHOD0(isCurrent, bool());
- MOCK_METHOD1(setCurrentSurface, bool(const RE::Surface&));
- MOCK_METHOD0(resetCurrentSurface, void());
- MOCK_METHOD0(flush, base::unique_fd());
- MOCK_METHOD0(finish, bool());
- MOCK_METHOD1(waitFence, bool(base::unique_fd*));
- bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
- MOCK_METHOD4(clearWithColor, void(float, float, float, float));
- MOCK_METHOD6(fillRegionWithColor, void(const Region&, uint32_t, float, float, float, float));
- MOCK_METHOD4(setScissor, void(uint32_t, uint32_t, uint32_t, uint32_t));
- MOCK_METHOD0(disableScissor, void());
- MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
- MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
- MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const RE::Image&));
- MOCK_METHOD5(readPixels, void(size_t, size_t, size_t, size_t, uint32_t*));
- MOCK_CONST_METHOD0(checkErrors, void());
- MOCK_METHOD6(setViewportAndProjection,
- void(size_t, size_t, Rect, size_t, bool, Transform::orientation_flags));
- MOCK_METHOD4(setupLayerBlending, void(bool, bool, bool, const half4&));
- MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
- MOCK_METHOD0(setupLayerBlackedOut, void());
- MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
- MOCK_METHOD1(setupColorTransform, void(const mat4&));
- MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
- MOCK_METHOD0(disableTexturing, void());
- MOCK_METHOD0(disableBlending, void());
- MOCK_METHOD1(setSourceY410BT2020, void(bool));
- MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace));
- MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace));
- MOCK_METHOD1(setDisplayMaxLuminance, void(const float));
- MOCK_METHOD2(bindNativeBufferAsFrameBuffer,
- void(ANativeWindowBuffer*, RE::BindNativeBufferAsFramebuffer*));
- MOCK_METHOD1(unbindNativeBufferAsFrameBuffer, void(RE::BindNativeBufferAsFramebuffer*));
- MOCK_METHOD1(drawMesh, void(const Mesh&));
- MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
- MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
-};
-
-class Surface : public RE::Surface {
-public:
- Surface();
- ~Surface() override;
-
- MOCK_METHOD1(setCritical, void(bool));
- MOCK_METHOD1(setAsync, void(bool));
- MOCK_METHOD1(setNativeWindow, void(ANativeWindow*));
- MOCK_CONST_METHOD0(swapBuffers, void());
- MOCK_CONST_METHOD0(queryRedSize, int32_t());
- MOCK_CONST_METHOD0(queryGreenSize, int32_t());
- MOCK_CONST_METHOD0(queryBlueSize, int32_t());
- MOCK_CONST_METHOD0(queryAlphaSize, int32_t());
- MOCK_CONST_METHOD0(queryWidth, int32_t());
- MOCK_CONST_METHOD0(queryHeight, int32_t());
-};
-
-class Image : public RE::Image {
-public:
- Image();
- ~Image() override;
-
- MOCK_METHOD4(setNativeWindowBuffer,
- bool(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth,
- int32_t cropHeight));
-};
-
-} // namespace mock
-} // namespace RE
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h b/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
index 561fd58..4950a4b 100644
--- a/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
+++ b/services/surfaceflinger/tests/unittests/mock/system/window/MockNativeWindow.h
@@ -36,6 +36,7 @@
MOCK_METHOD1(queueBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
MOCK_CONST_METHOD2(query, int(int, int*));
MOCK_METHOD1(perform, int(int));
+ MOCK_METHOD2(perform, int(int, int));
MOCK_METHOD1(cancelBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
diff --git a/services/thermalservice/Android.bp b/services/thermalservice/Android.bp
deleted file mode 100644
index d754560..0000000
--- a/services/thermalservice/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-subdirs = [
- "libthermalcallback"
-]
-
-cc_library {
- name: "libthermalservice",
-
- srcs: [
- "aidl/android/os/IThermalEventListener.aidl",
- "aidl/android/os/IThermalService.aidl",
- "aidl/android/os/Temperature.cpp",
- ],
- aidl: {
- include_dirs: ["frameworks/native/services/thermalservice/aidl"],
- export_aidl_headers: true,
- },
- export_include_dirs: ["aidl"],
-
- shared_libs: [
- "libbinder",
- "libutils",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wunused",
- "-Wunreachable-code",
- ],
-}
-
-cc_binary {
- name: "thermalserviced",
-
- srcs: [
- "ThermalService.cpp",
- "thermalserviced.cpp",
- ],
-
- include_dirs: ["frameworks/native"],
-
- shared_libs: [
- "libthermalservice",
- "libbinder",
- "libutils",
- "libthermalcallback",
- "android.hardware.thermal@1.1",
- "libhidlbase",
- "libhidltransport",
- "liblog",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wunused",
- "-Wunreachable-code",
- ],
-
- init_rc: ["thermalservice.rc"],
-}
diff --git a/services/thermalservice/ThermalService.cpp b/services/thermalservice/ThermalService.cpp
deleted file mode 100644
index 6e09a83..0000000
--- a/services/thermalservice/ThermalService.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ThermalService.h"
-#include <android/os/IThermalService.h>
-#include <android/os/IThermalEventListener.h>
-#include <android/os/Temperature.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-
-namespace android {
-namespace os {
-
-/**
- * Notify registered listeners of a thermal throttling start/stop event.
- * @param temperature the temperature at which the event was generated
- */
-binder::Status ThermalService::notifyThrottling(
- const bool isThrottling, const Temperature& temperature) {
- Mutex::Autolock _l(mListenersLock);
-
- mThrottled = isThrottling;
- mThrottleTemperature = temperature;
-
- for (size_t i = 0; i < mListeners.size(); i++) {
- mListeners[i]->notifyThrottling(isThrottling, temperature);
- }
- return binder::Status::ok();
-}
-
-/**
- * Query whether the system is currently thermal throttling.
- * @return true if currently thermal throttling, else false
- */
-binder::Status ThermalService::isThrottling(bool* _aidl_return) {
- Mutex::Autolock _l(mListenersLock);
- *_aidl_return = mThrottled;
- return binder::Status::ok();
-}
-
-/**
- * Register a new thermal event listener.
- * @param listener the client's IThermalEventListener instance to which
- * notifications are to be sent
- */
-binder::Status ThermalService::registerThermalEventListener(
- const sp<IThermalEventListener>& listener) {
- {
- if (listener == NULL)
- return binder::Status::ok();
- Mutex::Autolock _l(mListenersLock);
- // check whether this is a duplicate
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (IInterface::asBinder(mListeners[i]) ==
- IInterface::asBinder(listener)) {
- return binder::Status::ok();
- }
- }
-
- mListeners.add(listener);
- IInterface::asBinder(listener)->linkToDeath(this);
- }
-
- return binder::Status::ok();
-}
-
-/**
- * Unregister a previously-registered thermal event listener.
- * @param listener the client's IThermalEventListener instance to which
- * notifications are to no longer be sent
- */
-binder::Status ThermalService::unregisterThermalEventListener(
- const sp<IThermalEventListener>& listener) {
- if (listener == NULL)
- return binder::Status::ok();
- Mutex::Autolock _l(mListenersLock);
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (IInterface::asBinder(mListeners[i]) ==
- IInterface::asBinder(listener)) {
- IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
- mListeners.removeAt(i);
- break;
- }
- }
-
- return binder::Status::ok();
-}
-
-void ThermalService::binderDied(const wp<IBinder>& who) {
- Mutex::Autolock _l(mListenersLock);
-
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (IInterface::asBinder(mListeners[i]) == who) {
- mListeners.removeAt(i);
- break;
- }
- }
-}
-
-/**
- * Publish the supplied ThermalService to servicemanager.
- */
-void ThermalService::publish(
- const sp<ThermalService>& service) {
- defaultServiceManager()->addService(String16("thermalservice"),
- service);
-}
-
-} // namespace os
-} // namespace android
diff --git a/services/thermalservice/ThermalService.h b/services/thermalservice/ThermalService.h
deleted file mode 100644
index 17dfcbc..0000000
--- a/services/thermalservice/ThermalService.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_THERMALSERVICE_THERMALSERVICE_H
-#define ANDROID_THERMALSERVICE_THERMALSERVICE_H
-
-#include <android/os/BnThermalService.h>
-#include <android/os/IThermalEventListener.h>
-#include <android/os/Temperature.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-namespace android {
-namespace os {
-
-class ThermalService : public BnThermalService,
- public IBinder::DeathRecipient {
-public:
- ThermalService() : mThrottled(false) {};
- void publish(const sp<ThermalService>& service);
- binder::Status notifyThrottling(
- const bool isThrottling, const Temperature& temperature);
-
-private:
- Mutex mListenersLock;
- Vector<sp<IThermalEventListener> > mListeners;
- bool mThrottled;
- Temperature mThrottleTemperature;
-
- binder::Status registerThermalEventListener(
- const sp<IThermalEventListener>& listener);
- binder::Status unregisterThermalEventListener(
- const sp<IThermalEventListener>& listener);
- binder::Status isThrottling(bool* _aidl_return);
- void binderDied(const wp<IBinder>& who);
-};
-
-}; // namespace os
-}; // namespace android
-
-#endif // ANDROID_THERMALSERVICE_THERMALSERVICE_H
diff --git a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl b/services/thermalservice/aidl/android/os/IThermalEventListener.aidl
deleted file mode 100644
index 050325e..0000000
--- a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.os.Temperature;
-
-/**
- * Listener for thermal events.
- * {@hide}
- */
-oneway interface IThermalEventListener {
- /**
- * Called when a thermal throttling start/stop event is received.
- * @param temperature the temperature at which the event was generated.
- */
- void notifyThrottling(
- in boolean isThrottling, in Temperature temperature);
-}
diff --git a/services/thermalservice/aidl/android/os/IThermalService.aidl b/services/thermalservice/aidl/android/os/IThermalService.aidl
deleted file mode 100644
index e699202..0000000
--- a/services/thermalservice/aidl/android/os/IThermalService.aidl
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.os.IThermalEventListener;
-import android.os.Temperature;
-
-/** {@hide} */
-interface IThermalService {
- /**
- * Register a listener for thermal events.
- * @param listener the IThermalEventListener to be notified.
- * {@hide}
- */
- void registerThermalEventListener(in IThermalEventListener listener);
- /**
- * Unregister a previously-registered listener for thermal events.
- * @param listener the IThermalEventListener to no longer be notified.
- * {@hide}
- */
- void unregisterThermalEventListener(in IThermalEventListener listener);
- /**
- * Send a thermal throttling start/stop notification to all listeners.
- * @param temperature the temperature at which the event was generated.
- * {@hide}
- */
- oneway void notifyThrottling(
- in boolean isThrottling, in Temperature temperature);
- /**
- * Return whether system performance is currently thermal throttling.
- * {@hide}
- */
- boolean isThrottling();
-}
diff --git a/services/thermalservice/aidl/android/os/Temperature.aidl b/services/thermalservice/aidl/android/os/Temperature.aidl
deleted file mode 100644
index 0293c39..0000000
--- a/services/thermalservice/aidl/android/os/Temperature.aidl
+++ /dev/null
@@ -1,5 +0,0 @@
-package android.os;
-
-/* Encodes a temperature used by ThermalService. */
-
-parcelable Temperature cpp_header "android/os/Temperature.h";
diff --git a/services/thermalservice/aidl/android/os/Temperature.cpp b/services/thermalservice/aidl/android/os/Temperature.cpp
deleted file mode 100644
index df207b7..0000000
--- a/services/thermalservice/aidl/android/os/Temperature.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android/os/Temperature.h"
-
-#include <math.h>
-#include <stdint.h>
-#include <binder/Parcel.h>
-#include <hardware/thermal.h>
-#include <sys/types.h>
-#include <utils/Errors.h>
-
-namespace android {
-namespace os {
-
-Temperature::Temperature() : value_(NAN), type_(DEVICE_TEMPERATURE_UNKNOWN) {}
-
-Temperature::Temperature(const float value, const int type) :
- value_(value), type_(type) {}
-
-Temperature::~Temperature() {}
-
-/*
- * Parcel read/write code must be kept in sync with
- * frameworks/base/core/java/android/os/Temperature.java
- */
-
-status_t Temperature::readFromParcel(const Parcel* p) {
- value_ = p->readFloat();
- type_ = p->readInt32();
- return OK;
-}
-
-status_t Temperature::writeToParcel(Parcel* p) const {
- p->writeFloat(value_);
- p->writeInt32(type_);
- return OK;
-}
-
-} // namespace os
-} // namespace android
diff --git a/services/thermalservice/aidl/android/os/Temperature.h b/services/thermalservice/aidl/android/os/Temperature.h
deleted file mode 100644
index bbc5607..0000000
--- a/services/thermalservice/aidl/android/os/Temperature.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H
-#define ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H
-
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace os {
-
-class Temperature : public Parcelable {
- public:
-
- Temperature();
- Temperature(const float value, const int type);
- ~Temperature() override;
-
- float getValue() const {return value_;};
- float getType() const {return type_;};
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- // The value of the temperature as a float, or NAN if unknown.
- float value_;
- // The type of the temperature, an enum temperature_type from
- // hardware/thermal.h
- int type_;
-};
-
-} // namespace os
-} // namespace android
-
-#endif // ANDROID_THERMALSERVICE_AIDL_ANDROID_OS_TEMPERATURE_H
diff --git a/services/thermalservice/libthermalcallback/Android.bp b/services/thermalservice/libthermalcallback/Android.bp
deleted file mode 100644
index e98506e..0000000
--- a/services/thermalservice/libthermalcallback/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-cc_library_shared {
- name: "libthermalcallback",
- srcs: [
- "ThermalCallback.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- include_dirs: ["frameworks/native"],
- shared_libs: [
- "android.hardware.thermal@1.1",
- "libhidlbase",
- "libhidltransport",
- "liblog",
- "libthermalservice",
- "libutils",
- ],
-}
diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.cpp b/services/thermalservice/libthermalcallback/ThermalCallback.cpp
deleted file mode 100644
index 5e094fa..0000000
--- a/services/thermalservice/libthermalcallback/ThermalCallback.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#define LOG_TAG "android.hardware.thermal.thermalcallback@1.1-impl"
-#include <log/log.h>
-
-#include "ThermalCallback.h"
-#include "services/thermalservice/ThermalService.h"
-#include <math.h>
-#include <android/os/Temperature.h>
-#include <hardware/thermal.h>
-
-namespace android {
-namespace hardware {
-namespace thermal {
-namespace V1_1 {
-namespace implementation {
-
-using ::android::os::ThermalService;
-using ::android::hardware::thermal::V1_0::TemperatureType;
-
-// Register a binder ThermalService object for sending events
-void ThermalCallback::registerThermalService(sp<ThermalService> thermalService)
-{
- mThermalService = thermalService;
-}
-
-// Methods from IThermalCallback::V1_1 follow.
-Return<void> ThermalCallback::notifyThrottling(
- bool isThrottling,
- const android::hardware::thermal::V1_0::Temperature& temperature) {
-
- // Convert HIDL IThermal Temperature to binder IThermalService Temperature.
- if (mThermalService != nullptr) {
- float value = NAN;
- int type = DEVICE_TEMPERATURE_UNKNOWN;
-
- switch(temperature.type) {
- case TemperatureType::CPU:
- type = DEVICE_TEMPERATURE_CPU;
- break;
- case TemperatureType::GPU:
- type = DEVICE_TEMPERATURE_GPU;
- break;
- case TemperatureType::BATTERY:
- type = DEVICE_TEMPERATURE_BATTERY;
- break;
- case TemperatureType::SKIN:
- type = DEVICE_TEMPERATURE_SKIN;
- break;
- case TemperatureType::UNKNOWN:
- default:
- type = DEVICE_TEMPERATURE_UNKNOWN;
- break;
- }
-
- value = temperature.currentValue == UNKNOWN_TEMPERATURE ? NAN :
- temperature.currentValue;
-
- android::os::Temperature thermal_svc_temp(value, type);
- mThermalService->notifyThrottling(isThrottling, thermal_svc_temp);
- } else {
- ALOGE("IThermalService binder service not created, drop throttling event");
- }
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_1
-} // namespace thermal
-} // namespace hardware
-} // namespace android
diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.h b/services/thermalservice/libthermalcallback/ThermalCallback.h
deleted file mode 100644
index 3d72c68..0000000
--- a/services/thermalservice/libthermalcallback/ThermalCallback.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H
-#define ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H
-
-#include <android/hardware/thermal/1.1/IThermalCallback.h>
-#include <android/hardware/thermal/1.0/types.h>
-#include <android/os/Temperature.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-#include "services/thermalservice/ThermalService.h"
-
-namespace android {
-namespace hardware {
-namespace thermal {
-namespace V1_1 {
-namespace implementation {
-
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::os::ThermalService;
-
-class ThermalCallback : public IThermalCallback {
- public:
- // Register a binder ThermalService object for sending events
- void registerThermalService(sp<ThermalService> thermalService);
-
- // Methods from IThermalCallback::V1_1 follow.
- Return<void> notifyThrottling(
- bool isThrottling,
- const android::hardware::thermal::V1_0::Temperature& temperature)
- override;
-
- private:
- // Our registered binder ThermalService object to use for sending events
- sp<android::os::ThermalService> mThermalService;
-};
-
-} // namespace implementation
-} // namespace V1_1
-} // namespace thermal
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_THERMAL_V1_1_THERMALCALLBACK_H
diff --git a/services/thermalservice/thermalservice.rc b/services/thermalservice/thermalservice.rc
deleted file mode 100644
index 94c2c78..0000000
--- a/services/thermalservice/thermalservice.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service thermalservice /system/bin/thermalserviced
- class core
- user system
- group system
diff --git a/services/thermalservice/thermalserviced.cpp b/services/thermalservice/thermalserviced.cpp
deleted file mode 100644
index 8e27266..0000000
--- a/services/thermalservice/thermalserviced.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "thermalserviced"
-#include <log/log.h>
-
-#include "thermalserviced.h"
-#include "ThermalService.h"
-#include "libthermalcallback/ThermalCallback.h"
-
-#include <android/hardware/thermal/1.1/IThermal.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <hidl/HidlTransportSupport.h>
-
-using namespace android;
-using ::android::hardware::thermal::V1_1::IThermal;
-using ::android::hardware::thermal::V1_0::Temperature;
-using ::android::hardware::thermal::V1_1::IThermalCallback;
-using ::android::hardware::thermal::V1_1::implementation::ThermalCallback;
-using ::android::hardware::configureRpcThreadpool;
-using ::android::hardware::hidl_death_recipient;
-using ::android::hidl::base::V1_0::IBase;
-using ::android::os::ThermalService;
-
-template<typename T>
-using Return = hardware::Return<T>;
-
-namespace {
-
-// Our thermalserviced main object
-ThermalServiceDaemon* gThermalServiceDaemon;
-
-// Thermal HAL client
-sp<IThermal> gThermalHal = nullptr;
-
-// Binder death notifier informing of Thermal HAL death.
-struct ThermalServiceDeathRecipient : hidl_death_recipient {
- virtual void serviceDied(
- uint64_t cookie __unused, const wp<IBase>& who __unused) {
- gThermalHal = nullptr;
- ALOGE("IThermal HAL died");
- gThermalServiceDaemon->getThermalHal();
- }
-};
-
-sp<ThermalServiceDeathRecipient> gThermalHalDied = nullptr;
-
-} // anonymous namespace
-
-void ThermalServiceDaemon::thermalServiceStartup() {
- // Binder IThermalService startup
- mThermalService = new android::os::ThermalService;
- mThermalService->publish(mThermalService);
- // Register IThermalService object with IThermalCallback
- if (mThermalCallback != nullptr)
- mThermalCallback->registerThermalService(mThermalService);
- IPCThreadState::self()->joinThreadPool();
-}
-
-// Lookup Thermal HAL, register death notifier, register our
-// ThermalCallback with the Thermal HAL.
-void ThermalServiceDaemon::getThermalHal() {
- gThermalHal = IThermal::getService();
- if (gThermalHal == nullptr) {
- ALOGW("Unable to get Thermal HAL V1.1, vendor thermal event notification not available");
- return;
- }
-
- // Binder death notifier for Thermal HAL
- if (gThermalHalDied == nullptr)
- gThermalHalDied = new ThermalServiceDeathRecipient();
-
- if (gThermalHalDied != nullptr)
- gThermalHal->linkToDeath(gThermalHalDied, 0x451F /* cookie */);
-
- if (mThermalCallback != nullptr) {
- Return<void> ret = gThermalHal->registerThermalCallback(
- mThermalCallback);
- if (!ret.isOk())
- ALOGE("registerThermalCallback failed, status: %s",
- ret.description().c_str());
- }
-}
-
-void ThermalServiceDaemon::thermalCallbackStartup() {
- // HIDL IThermalCallback startup
- // Need at least 2 threads in thread pool since we wait for dead HAL
- // to come back on the binder death notification thread and we need
- // another thread for the incoming service now available call.
- configureRpcThreadpool(2, false /* callerWillJoin */);
- mThermalCallback = new ThermalCallback();
- // Lookup Thermal HAL and register our ThermalCallback.
- getThermalHal();
-}
-
-int main(int /*argc*/, char** /*argv*/) {
- gThermalServiceDaemon = new ThermalServiceDaemon();
- gThermalServiceDaemon->thermalCallbackStartup();
- gThermalServiceDaemon->thermalServiceStartup();
- /* NOTREACHED */
-}
diff --git a/services/thermalservice/thermalserviced.h b/services/thermalservice/thermalserviced.h
deleted file mode 100644
index 309e2fe..0000000
--- a/services/thermalservice/thermalserviced.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_THERMALSERVICE_THERMALSERVICED_H
-#define ANDROID_THERMALSERVICE_THERMALSERVICED_H
-
-#include "ThermalService.h"
-#include "libthermalcallback/ThermalCallback.h"
-
-using namespace android;
-using ::android::hardware::thermal::V1_0::Temperature;
-using ::android::hardware::thermal::V1_1::implementation::ThermalCallback;
-using ::android::os::ThermalService;
-
-class ThermalServiceDaemon {
- public:
- void thermalServiceStartup();
- void thermalCallbackStartup();
- void getThermalHal();
- ThermalServiceDaemon() {};
-
- private:
- sp<ThermalService> mThermalService;
- sp<ThermalCallback> mThermalCallback;
-};
-
-#endif // ANDROID_THERMALSERVICE_THERMALSERVICED_H
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 6122846..4e24a64 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -12,45 +12,57 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-sourceFiles = [
- "buffer_hub.cpp",
- "bufferhubd.cpp",
- "consumer_channel.cpp",
- "producer_channel.cpp",
- "detached_buffer_channel.cpp",
- "consumer_queue_channel.cpp",
- "producer_queue_channel.cpp",
-]
-
-headerLibraries = ["libdvr_headers"]
-
-staticLibraries = [
- "libperformance",
- "libbufferhub",
-]
-
sharedLibraries = [
"libbase",
- "libbinder",
"libcutils",
- "liblog",
- "libsync",
- "libutils",
+ "libgtest_prod",
"libgui",
- "libui",
+ "liblog",
"libpdx_default_transport",
+ "libsync",
+ "libui",
+ "libutils",
]
+cc_library_static {
+ name: "libbufferhubd",
+ srcs: [
+ "buffer_hub.cpp",
+ "consumer_channel.cpp",
+ "consumer_queue_channel.cpp",
+ "producer_channel.cpp",
+ "producer_queue_channel.cpp",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"libbufferhubd\"",
+ "-DTRACE=0",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ ],
+ export_include_dirs: ["include"],
+ header_libs: ["libdvr_headers"],
+ shared_libs: sharedLibraries,
+ static_libs: [
+ "libbufferhub",
+ ],
+
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
+}
+
cc_binary {
- srcs: sourceFiles,
+ srcs: ["bufferhubd.cpp"],
cflags: [
"-DLOG_TAG=\"bufferhubd\"",
"-DTRACE=0",
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
],
- header_libs: headerLibraries,
- static_libs: staticLibraries,
+ header_libs: ["libdvr_headers"],
shared_libs: sharedLibraries,
+ static_libs: [
+ "libbufferhub",
+ "libbufferhubd",
+ "libperformance",
+ ],
name: "bufferhubd",
init_rc: ["bufferhubd.rc"],
}
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index e57c8ed..6409265 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -1,9 +1,5 @@
-#include "buffer_hub.h"
-
#include <inttypes.h>
-#include <log/log.h>
#include <poll.h>
-#include <utils/Trace.h>
#include <iomanip>
#include <memory>
@@ -11,12 +7,14 @@
#include <string>
#include <thread>
+#include <log/log.h>
#include <pdx/default_transport/service_endpoint.h>
#include <private/dvr/bufferhub_rpc.h>
-#include "consumer_channel.h"
-#include "detached_buffer_channel.h"
-#include "producer_channel.h"
-#include "producer_queue_channel.h"
+#include <private/dvr/buffer_hub.h>
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <private/dvr/producer_queue_channel.h>
+#include <utils/Trace.h>
using android::pdx::Channel;
using android::pdx::ErrorStatus;
@@ -57,8 +55,6 @@
stream << " ";
stream << std::setw(10) << "Usage";
stream << " ";
- stream << std::setw(9) << "Pending";
- stream << " ";
stream << std::setw(18) << "State";
stream << " ";
stream << std::setw(18) << "Signaled";
@@ -91,8 +87,6 @@
stream << std::setw(8) << info.usage;
stream << std::dec << std::setfill(' ');
stream << " ";
- stream << std::setw(9) << info.pending_count;
- stream << " ";
stream << "0x" << std::hex << std::setfill('0');
stream << std::setw(16) << info.state;
stream << " ";
@@ -247,33 +241,11 @@
*this, &BufferHubService::OnCreateBuffer, message);
return {};
- case DetachedBufferRPC::Create::Opcode:
- DispatchRemoteMethod<DetachedBufferRPC::Create>(
- *this, &BufferHubService::OnCreateDetachedBuffer, message);
- return {};
-
case BufferHubRPC::CreateProducerQueue::Opcode:
DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
*this, &BufferHubService::OnCreateProducerQueue, message);
return {};
- case BufferHubRPC::ProducerBufferDetach::Opcode:
- // In addition to the message handler in the ProducerChannel's
- // HandleMessage method, we also need to invalid the producer channel (and
- // all associated consumer channels). Note that this has to be done after
- // HandleMessage returns to make sure the IPC request has went back to the
- // client first.
- SetChannel(channel->channel_id(), nullptr);
- return {};
-
- case DetachedBufferRPC::Promote::Opcode:
- // In addition to the message handler in the DetachedBufferChannel's
- // HandleMessage method, we also need to invalid the channel. Note that
- // this has to be done after HandleMessage returns to make sure the IPC
- // request has went back to the client first.
- SetChannel(channel->channel_id(), nullptr);
- return {};
-
default:
return DefaultHandleMessage(message);
}
@@ -310,43 +282,6 @@
}
}
-pdx::Status<void> BufferHubService::OnCreateDetachedBuffer(
- pdx::Message& message, uint32_t width, uint32_t height,
- uint32_t layer_count, uint32_t format, uint64_t usage,
- size_t user_metadata_size) {
- // Use the producer channel id as the global buffer id.
- const int buffer_id = message.GetChannelId();
- ALOGD_IF(TRACE,
- "BufferHubService::OnCreateDetachedBuffer: buffer_id=%d width=%u "
- "height=%u layer_count=%u format=%u usage=%" PRIx64
- " user_metadata_size=%zu",
- buffer_id, width, height, layer_count, format, usage,
- user_metadata_size);
-
- // See if this channel is already attached to a buffer.
- if (const auto channel = message.GetChannel<BufferHubChannel>()) {
- ALOGE(
- "BufferHubService::OnCreateDetachedBuffer: Buffer already created: "
- "buffer=%d",
- buffer_id);
- return ErrorStatus(EALREADY);
- }
-
- std::unique_ptr<DetachedBufferChannel> channel =
- DetachedBufferChannel::Create(this, buffer_id, width, height, layer_count,
- format, usage, user_metadata_size);
- if (!channel) {
- ALOGE(
- "BufferHubService::OnCreateDetachedBuffer: Failed to allocate buffer, "
- "buffer=%d.",
- buffer_id);
- return ErrorStatus(ENOMEM);
- }
-
- message.SetChannel(std::move(channel));
- return {};
-}
-
Status<QueueInfo> BufferHubService::OnCreateProducerQueue(
pdx::Message& message, const ProducerQueueConfig& producer_config,
const UsagePolicy& usage_policy) {
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
index b27f218..50cb3b7 100644
--- a/services/vr/bufferhubd/bufferhubd.cpp
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -1,17 +1,15 @@
#include <sched.h>
+#include <sys/resource.h>
#include <unistd.h>
-#include <log/log.h>
-#include <sys/resource.h>
-
#include <dvr/performance_client_api.h>
+#include <log/log.h>
#include <pdx/service_dispatcher.h>
-
-#include "buffer_hub.h"
+#include <private/dvr/buffer_hub.h>
int main(int, char**) {
int ret = -1;
- std::shared_ptr<android::pdx::Service> service;
+ std::shared_ptr<android::dvr::BufferHubService> pdx_service;
std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
// We need to be able to create endpoints with full perms.
@@ -37,9 +35,9 @@
dispatcher = android::pdx::ServiceDispatcher::Create();
CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
- service = android::dvr::BufferHubService::Create();
- CHECK_ERROR(!service, error, "Failed to create buffer hub service\n");
- dispatcher->AddService(service);
+ pdx_service = android::dvr::BufferHubService::Create();
+ CHECK_ERROR(!pdx_service, error, "Failed to create bufferhub pdx service\n");
+ dispatcher->AddService(pdx_service);
ret = dvrSetSchedulerClass(0, "graphics");
CHECK_ERROR(ret < 0, error, "Failed to set thread priority");
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index a6d2dbb..c7695bc 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -1,12 +1,10 @@
-#include "consumer_channel.h"
-
-#include <log/log.h>
-#include <utils/Trace.h>
-
#include <thread>
+#include <log/log.h>
#include <private/dvr/bufferhub_rpc.h>
-#include "producer_channel.h"
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <utils/Trace.h>
using android::pdx::BorrowedHandle;
using android::pdx::Channel;
@@ -19,10 +17,10 @@
namespace dvr {
ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id,
- int channel_id, uint64_t consumer_state_bit,
+ int channel_id, uint32_t client_state_mask,
const std::shared_ptr<Channel> producer)
: BufferHubChannel(service, buffer_id, channel_id, kConsumerType),
- consumer_state_bit_(consumer_state_bit),
+ client_state_mask_(client_state_mask),
producer_(producer) {
GetProducer()->AddConsumer(this);
}
@@ -43,7 +41,7 @@
// If producer has not hung up, copy most buffer info from the producer.
info = producer->GetBufferInfo();
} else {
- info.signaled_mask = consumer_state_bit();
+ info.signaled_mask = client_state_mask();
}
info.id = buffer_id();
return info;
@@ -92,11 +90,6 @@
*this, &ConsumerChannel::OnConsumerRelease, message);
return true;
- case BufferHubRPC::ConsumerSetIgnore::Opcode:
- DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(
- *this, &ConsumerChannel::OnConsumerSetIgnore, message);
- return true;
-
default:
return false;
}
@@ -107,7 +100,7 @@
ATRACE_NAME("ConsumerChannel::OnGetBuffer");
ALOGD_IF(TRACE, "ConsumerChannel::OnGetBuffer: buffer=%d", buffer_id());
if (auto producer = GetProducer()) {
- return {producer->GetBuffer(consumer_state_bit_)};
+ return {producer->GetBuffer(client_state_mask_)};
} else {
return ErrorStatus(EPIPE);
}
@@ -122,9 +115,8 @@
if (acquired_ || released_) {
ALOGE(
"ConsumerChannel::OnConsumerAcquire: Acquire when not posted: "
- "ignored=%d acquired=%d released=%d channel_id=%d buffer_id=%d",
- ignored_, acquired_, released_, message.GetChannelId(),
- producer->buffer_id());
+ "acquired=%d released=%d channel_id=%d buffer_id=%d",
+ acquired_, released_, message.GetChannelId(), producer->buffer_id());
return ErrorStatus(EBUSY);
} else {
auto status = producer->OnConsumerAcquire(message);
@@ -146,9 +138,8 @@
if (!acquired_ || released_) {
ALOGE(
"ConsumerChannel::OnConsumerRelease: Release when not acquired: "
- "ignored=%d acquired=%d released=%d channel_id=%d buffer_id=%d",
- ignored_, acquired_, released_, message.GetChannelId(),
- producer->buffer_id());
+ "acquired=%d released=%d channel_id=%d buffer_id=%d",
+ acquired_, released_, message.GetChannelId(), producer->buffer_id());
return ErrorStatus(EBUSY);
} else {
auto status =
@@ -162,36 +153,21 @@
}
}
-Status<void> ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) {
- ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore");
- auto producer = GetProducer();
- if (!producer)
- return ErrorStatus(EPIPE);
-
- ignored_ = ignored;
- if (ignored_ && acquired_) {
- // Update the producer if ignore is set after the consumer acquires the
- // buffer.
- ClearAvailable();
- producer->OnConsumerIgnored();
- acquired_ = false;
- released_ = true;
- }
-
- return {};
+void ConsumerChannel::OnProducerGained() {
+ // Clear the signal if exist. There is a possiblity that the signal still
+ // exist in consumer client when producer gains the buffer, e.g. newly added
+ // consumer fail to acquire the previous posted buffer in time. Then, when
+ // producer gains back the buffer, posts the buffer again and signal the
+ // consumer later, there won't be an signal change in eventfd, and thus,
+ // consumer will miss the posted buffer later. Thus, we need to clear the
+ // signal in consumer clients if the signal exist.
+ ClearAvailable();
}
-bool ConsumerChannel::OnProducerPosted() {
- if (ignored_) {
- acquired_ = false;
- released_ = true;
- return false;
- } else {
- acquired_ = false;
- released_ = false;
- SignalAvailable();
- return true;
- }
+void ConsumerChannel::OnProducerPosted() {
+ acquired_ = false;
+ released_ = false;
+ SignalAvailable();
}
void ConsumerChannel::OnProducerClosed() {
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
index 4d43001..5d7d4e9 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.cpp
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -1,8 +1,6 @@
-#include "consumer_queue_channel.h"
-
#include <pdx/channel_handle.h>
-
-#include "producer_channel.h"
+#include <private/dvr/consumer_queue_channel.h>
+#include <private/dvr/producer_channel.h>
using android::pdx::ErrorStatus;
using android::pdx::RemoteChannelHandle;
@@ -82,27 +80,35 @@
}
void ConsumerQueueChannel::RegisterNewBuffer(
- const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
- ALOGD_IF(TRACE,
- "ConsumerQueueChannel::RegisterNewBuffer: queue_id=%d buffer_id=%d "
- "slot=%zu silent=%d",
- buffer_id(), producer_channel->buffer_id(), slot, silent_);
+ const std::shared_ptr<ProducerChannel>& producer_channel,
+ size_t producer_slot) {
+ ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu silent=%d",
+ __FUNCTION__, buffer_id(), producer_channel->buffer_id(),
+ producer_slot, silent_);
// Only register buffers if the queue is not silent.
- if (!silent_) {
- pending_buffer_slots_.emplace(producer_channel, slot);
-
- // Signal the client that there is new buffer available.
- SignalAvailable();
+ if (silent_) {
+ return;
}
+
+ auto status = producer_channel->CreateConsumerStateMask();
+ if (!status.ok()) {
+ ALOGE("%s: Failed to create consumer state mask: %s", __FUNCTION__,
+ status.GetErrorMessage().c_str());
+ return;
+ }
+ uint64_t consumer_state_mask = status.get();
+
+ pending_buffer_slots_.emplace(producer_channel, producer_slot,
+ consumer_state_mask);
+ // Signal the client that there is new buffer available.
+ SignalAvailable();
}
Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
- ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
- ALOGD_IF(TRACE,
- "ConsumerQueueChannel::OnConsumerQueueImportBuffers: "
- "pending_buffer_slots=%zu",
+ ATRACE_NAME(__FUNCTION__);
+ ALOGD_IF(TRACE, "%s: pending_buffer_slots=%zu", __FUNCTION__,
pending_buffer_slots_.size());
// Indicate this is a silent queue that will not import buffers.
@@ -110,30 +116,30 @@
return ErrorStatus(EBADR);
while (!pending_buffer_slots_.empty()) {
- auto producer_channel = pending_buffer_slots_.front().first.lock();
- size_t producer_slot = pending_buffer_slots_.front().second;
+ auto producer_channel =
+ pending_buffer_slots_.front().producer_channel.lock();
+ size_t producer_slot = pending_buffer_slots_.front().producer_slot;
+ uint64_t consumer_state_mask =
+ pending_buffer_slots_.front().consumer_state_mask;
pending_buffer_slots_.pop();
// It's possible that the producer channel has expired. When this occurs,
// ignore the producer channel.
if (producer_channel == nullptr) {
- ALOGW(
- "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
- "channel has already been expired.");
+ ALOGW("%s: producer channel has already been expired.", __FUNCTION__);
continue;
}
- auto status = producer_channel->CreateConsumer(message);
+ auto status =
+ producer_channel->CreateConsumer(message, consumer_state_mask);
// If no buffers are imported successfully, clear available and return an
// error. Otherwise, return all consumer handles already imported
// successfully, but keep available bits on, so that the client can retry
// importing remaining consumer buffers.
if (!status) {
- ALOGE(
- "ConsumerQueueChannel::OnConsumerQueueImportBuffers: Failed create "
- "consumer: %s",
- status.GetErrorMessage().c_str());
+ ALOGE("%s: Failed create consumer: %s", __FUNCTION__,
+ status.GetErrorMessage().c_str());
if (buffer_handles.empty()) {
ClearAvailable();
return status.error_status();
diff --git a/services/vr/bufferhubd/detached_buffer_channel.cpp b/services/vr/bufferhubd/detached_buffer_channel.cpp
deleted file mode 100644
index a5cf68d..0000000
--- a/services/vr/bufferhubd/detached_buffer_channel.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#include "detached_buffer_channel.h"
-#include "producer_channel.h"
-
-using android::pdx::BorrowedHandle;
-using android::pdx::ErrorStatus;
-using android::pdx::Message;
-using android::pdx::RemoteChannelHandle;
-using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace android {
-namespace dvr {
-
-DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service,
- int buffer_id, int channel_id,
- IonBuffer buffer,
- IonBuffer metadata_buffer,
- size_t user_metadata_size)
- : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
- buffer_(std::move(buffer)),
- metadata_buffer_(std::move(metadata_buffer)),
- user_metadata_size_(user_metadata_size) {
-}
-
-DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service,
- int buffer_id, uint32_t width,
- uint32_t height,
- uint32_t layer_count,
- uint32_t format, uint64_t usage,
- size_t user_metadata_size)
- : BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType),
- user_metadata_size_(user_metadata_size) {
- // The size the of metadata buffer is used as the "width" parameter during
- // allocation. Thus it cannot overflow uint32_t.
- if (user_metadata_size_ >= (std::numeric_limits<uint32_t>::max() -
- BufferHubDefs::kMetadataHeaderSize)) {
- ALOGE(
- "DetachedBufferChannel::DetachedBufferChannel: metadata size too big.");
- return;
- }
-
- if (int ret = buffer_.Alloc(width, height, layer_count, format, usage)) {
- ALOGE(
- "DetachedBufferChannel::DetachedBufferChannel: Failed to allocate "
- "buffer: %s",
- strerror(-ret));
- return;
- }
-
- // Buffer metadata has two parts: 1) a fixed sized metadata header; and 2)
- // user requested metadata.
- const size_t size = BufferHubDefs::kMetadataHeaderSize + user_metadata_size_;
- if (int ret = metadata_buffer_.Alloc(size,
- /*height=*/1,
- /*layer_count=*/1,
- BufferHubDefs::kMetadataFormat,
- BufferHubDefs::kMetadataUsage)) {
- ALOGE(
- "DetachedBufferChannel::DetachedBufferChannel: Failed to allocate "
- "metadata: %s",
- strerror(-ret));
- return;
- }
-}
-
-DetachedBufferChannel::~DetachedBufferChannel() {
- ALOGD_IF(TRACE,
- "DetachedBufferChannel::~DetachedBufferChannel: channel_id=%d "
- "buffer_id=%d.",
- channel_id(), buffer_id());
- Hangup();
-}
-
-BufferHubChannel::BufferInfo DetachedBufferChannel::GetBufferInfo() const {
- return BufferInfo(buffer_id(), /*consumer_count=*/0, buffer_.width(),
- buffer_.height(), buffer_.layer_count(), buffer_.format(),
- buffer_.usage(), /*pending_count=*/0, /*state=*/0,
- /*signaled_mask=*/0, /*index=*/0);
-}
-
-void DetachedBufferChannel::HandleImpulse(Message& /*message*/) {
- ATRACE_NAME("DetachedBufferChannel::HandleImpulse");
-}
-
-bool DetachedBufferChannel::HandleMessage(Message& message) {
- ATRACE_NAME("DetachedBufferChannel::HandleMessage");
- switch (message.GetOp()) {
- case DetachedBufferRPC::Import::Opcode:
- DispatchRemoteMethod<DetachedBufferRPC::Import>(
- *this, &DetachedBufferChannel::OnImport, message);
- return true;
-
- case DetachedBufferRPC::Promote::Opcode:
- DispatchRemoteMethod<DetachedBufferRPC::Promote>(
- *this, &DetachedBufferChannel::OnPromote, message);
- return true;
-
- default:
- return false;
- }
-}
-
-Status<BufferDescription<BorrowedHandle>> DetachedBufferChannel::OnImport(
- Message& /*message*/) {
- ATRACE_NAME("DetachedBufferChannel::OnGetBuffer");
- ALOGD_IF(TRACE, "DetachedBufferChannel::OnGetBuffer: buffer=%d.",
- buffer_id());
-
- return BufferDescription<BorrowedHandle>{buffer_,
- metadata_buffer_,
- buffer_id(),
- /*buffer_state_bit=*/0,
- BorrowedHandle{},
- BorrowedHandle{}};
-}
-
-Status<RemoteChannelHandle> DetachedBufferChannel::OnPromote(
- Message& message) {
- ATRACE_NAME("DetachedBufferChannel::OnPromote");
- ALOGD_IF(TRACE, "DetachedBufferChannel::OnPromote: buffer_id=%d",
- buffer_id());
-
- // Note that the new ProducerChannel will have different channel_id, but
- // inherits the buffer_id from the DetachedBuffer.
- int channel_id;
- auto status = message.PushChannel(0, nullptr, &channel_id);
- if (!status) {
- ALOGE(
- "DetachedBufferChannel::OnPromote: Failed to push ProducerChannel: %s.",
- status.GetErrorMessage().c_str());
- return ErrorStatus(ENOMEM);
- }
-
- std::unique_ptr<ProducerChannel> channel = ProducerChannel::Create(
- service(), buffer_id(), channel_id, std::move(buffer_),
- std::move(metadata_buffer_), user_metadata_size_);
- if (!channel) {
- ALOGE(
- "DetachedBufferChannel::OnPromote: Failed to create ProducerChannel "
- "from a DetachedBufferChannel, buffer_id=%d.",
- buffer_id());
- }
-
- const auto channel_status =
- service()->SetChannel(channel_id, std::move(channel));
- if (!channel_status) {
- // Technically, this should never fail, as we just pushed the channel. Note
- // that LOG_FATAL will be stripped out in non-debug build.
- LOG_FATAL(
- "DetachedBufferChannel::OnPromote: Failed to set new producer buffer "
- "channel: %s.",
- channel_status.GetErrorMessage().c_str());
- }
-
- return status;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/bufferhubd/detached_buffer_channel.h b/services/vr/bufferhubd/detached_buffer_channel.h
deleted file mode 100644
index 8b6dab8..0000000
--- a/services/vr/bufferhubd/detached_buffer_channel.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
-#define ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
-
-#include "buffer_hub.h"
-
-#include <pdx/channel_handle.h>
-#include <pdx/file_handle.h>
-
-namespace android {
-namespace dvr {
-
-class DetachedBufferChannel : public BufferHubChannel {
- public:
- ~DetachedBufferChannel() override;
-
- template <typename... Args>
- static std::unique_ptr<DetachedBufferChannel> Create(Args&&... args) {
- auto buffer = std::unique_ptr<DetachedBufferChannel>(
- new DetachedBufferChannel(std::forward<Args>(args)...));
- return buffer->IsValid() ? std::move(buffer) : nullptr;
- }
-
- // Returns whether the object holds a valid graphic buffer.
- bool IsValid() const {
- return buffer_.IsValid() && metadata_buffer_.IsValid();
- }
-
- size_t user_metadata_size() const { return user_metadata_size_; }
-
- // Captures buffer info for use by BufferHubService::DumpState().
- BufferInfo GetBufferInfo() const override;
-
- bool HandleMessage(pdx::Message& message) override;
- void HandleImpulse(pdx::Message& message) override;
-
- private:
- // Creates a detached buffer from existing IonBuffers.
- DetachedBufferChannel(BufferHubService* service, int buffer_id,
- int channel_id, IonBuffer buffer,
- IonBuffer metadata_buffer, size_t user_metadata_size);
-
- // Allocates a new detached buffer.
- DetachedBufferChannel(BufferHubService* service, int buffer_id,
- uint32_t width, uint32_t height, uint32_t layer_count,
- uint32_t format, uint64_t usage,
- size_t user_metadata_size);
-
- pdx::Status<BufferDescription<pdx::BorrowedHandle>> OnImport(
- pdx::Message& message);
- pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message);
-
- // Gralloc buffer handles.
- IonBuffer buffer_;
- IonBuffer metadata_buffer_;
-
- // Size of user requested metadata.
- const size_t user_metadata_size_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
similarity index 87%
rename from services/vr/bufferhubd/buffer_hub.h
rename to services/vr/bufferhubd/include/private/dvr/buffer_hub.h
index e47ffa3..909d69b 100644
--- a/services/vr/bufferhubd/buffer_hub.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
@@ -41,18 +41,17 @@
// Captures buffer info for use by BufferHubService::DumpState().
struct BufferInfo {
- // Common data field shared by BufferProducer and ProducerQueue.
+ // Common data field shared by ProducerBuffer and ProducerQueue.
int id = -1;
int type = -1;
size_t consumer_count = 0;
- // Data field for buffer producer.
+ // Data field for producer buffer.
uint32_t width = 0;
uint32_t height = 0;
uint32_t layer_count = 0;
uint32_t format = 0;
uint64_t usage = 0;
- size_t pending_count = 0;
uint64_t state = 0;
uint64_t signaled_mask = 0;
uint64_t index = 0;
@@ -63,8 +62,7 @@
BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format, uint64_t usage,
- size_t pending_count, uint64_t state, uint64_t signaled_mask,
- uint64_t index)
+ uint64_t state, uint64_t signaled_mask, uint64_t index)
: id(id),
type(kProducerType),
consumer_count(consumer_count),
@@ -73,7 +71,6 @@
layer_count(layer_count),
format(format),
usage(usage),
- pending_count(pending_count),
state(state),
signaled_mask(signaled_mask),
index(index) {}
@@ -147,11 +144,6 @@
pdx::Status<void> OnCreateBuffer(pdx::Message& message, uint32_t width,
uint32_t height, uint32_t format,
uint64_t usage, size_t meta_size_bytes);
- pdx::Status<void> OnCreateDetachedBuffer(pdx::Message& message,
- uint32_t width, uint32_t height,
- uint32_t layer_count,
- uint32_t format, uint64_t usage,
- size_t user_metadata_size);
pdx::Status<QueueInfo> OnCreateProducerQueue(
pdx::Message& message, const ProducerQueueConfig& producer_config,
const UsagePolicy& usage_policy);
diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
similarity index 80%
rename from services/vr/bufferhubd/consumer_channel.h
rename to services/vr/bufferhubd/include/private/dvr/consumer_channel.h
index 55cf969..5ee551f 100644
--- a/services/vr/bufferhubd/consumer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
@@ -1,10 +1,9 @@
#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
#define ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_
-#include "buffer_hub.h"
-
#include <pdx/rpc/buffer_wrapper.h>
#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/buffer_hub.h>
namespace android {
namespace dvr {
@@ -17,17 +16,18 @@
using Message = pdx::Message;
ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id,
- uint64_t consumer_state_bit,
+ uint32_t client_state_mask,
const std::shared_ptr<Channel> producer);
~ConsumerChannel() override;
bool HandleMessage(Message& message) override;
void HandleImpulse(Message& message) override;
- uint64_t consumer_state_bit() const { return consumer_state_bit_; }
+ uint32_t client_state_mask() const { return client_state_mask_; }
BufferInfo GetBufferInfo() const override;
- bool OnProducerPosted();
+ void OnProducerGained();
+ void OnProducerPosted();
void OnProducerClosed();
private:
@@ -38,12 +38,10 @@
pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
pdx::Status<void> OnConsumerRelease(Message& message,
LocalFence release_fence);
- pdx::Status<void> OnConsumerSetIgnore(Message& message, bool ignore);
- uint64_t consumer_state_bit_{0};
+ uint32_t client_state_mask_{0U};
bool acquired_{false};
bool released_{true};
- bool ignored_{false}; // True if we are ignoring events.
std::weak_ptr<Channel> producer_;
ConsumerChannel(const ConsumerChannel&) = delete;
diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
similarity index 69%
rename from services/vr/bufferhubd/consumer_queue_channel.h
rename to services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
index 8437c4c..3a81b03 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
@@ -1,14 +1,12 @@
#ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
#define ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_
-#include "buffer_hub.h"
-
-#include <private/dvr/bufferhub_rpc.h>
-
#include <queue>
-#include "consumer_channel.h"
-#include "producer_queue_channel.h"
+#include <private/dvr/buffer_hub.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_queue_channel.h>
namespace android {
namespace dvr {
@@ -30,7 +28,8 @@
// Called by ProdcuerQueueChannel to notify consumer queue that a new
// buffer has been allocated.
void RegisterNewBuffer(
- const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot);
+ const std::shared_ptr<ProducerChannel>& producer_channel,
+ size_t producer_slot);
// Called after clients been signaled by service that new buffer has been
// allocated. Clients uses kOpConsumerQueueImportBuffers to import new
@@ -42,14 +41,29 @@
void OnProducerClosed();
private:
+ // Data structure to store relavant info of a newly allocated producer buffer
+ // so that consumer channel and buffer can be created later.
+ struct PendingBuffer {
+ PendingBuffer(std::shared_ptr<ProducerChannel> channel, size_t slot,
+ uint64_t mask) {
+ producer_channel = channel;
+ producer_slot = slot;
+ consumer_state_mask = mask;
+ }
+ PendingBuffer() = delete;
+
+ std::weak_ptr<ProducerChannel> producer_channel;
+ size_t producer_slot;
+ uint64_t consumer_state_mask;
+ };
+
std::shared_ptr<ProducerQueueChannel> GetProducer() const;
- // Pointer to the prodcuer channel
+ // Pointer to the producer channel.
std::weak_ptr<Channel> producer_;
// Tracks newly allocated buffer producers along with it's slot number.
- std::queue<std::pair<std::weak_ptr<ProducerChannel>, size_t>>
- pending_buffer_slots_;
+ std::queue<PendingBuffer> pending_buffer_slots_;
// Tracks how many buffers have this queue imported.
size_t capacity_;
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
similarity index 77%
rename from services/vr/bufferhubd/producer_channel.h
rename to services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 67fdf15..45593ad 100644
--- a/services/vr/bufferhubd/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -1,8 +1,6 @@
#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
#define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
-#include "buffer_hub.h"
-
#include <functional>
#include <memory>
#include <vector>
@@ -10,6 +8,7 @@
#include <pdx/channel_handle.h>
#include <pdx/file_handle.h>
#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/buffer_hub.h>
#include <private/dvr/bufferhub_rpc.h>
#include <private/dvr/ion_buffer.h>
@@ -43,56 +42,55 @@
~ProducerChannel() override;
+ uint32_t buffer_state() const {
+ return buffer_state_->load(std::memory_order_acquire);
+ }
+
bool HandleMessage(Message& message) override;
void HandleImpulse(Message& message) override;
BufferInfo GetBufferInfo() const override;
- BufferDescription<BorrowedHandle> GetBuffer(uint64_t buffer_state_bit);
+ BufferDescription<BorrowedHandle> GetBuffer(uint32_t client_state_mask);
- pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message);
+ pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message,
+ uint32_t consumer_state_mask);
+ pdx::Status<uint32_t> CreateConsumerStateMask();
pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message);
pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
pdx::Status<void> OnConsumerRelease(Message& message,
LocalFence release_fence);
- void OnConsumerIgnored();
- void OnConsumerOrphaned(ConsumerChannel* channel);
+ void OnConsumerOrphaned(const uint32_t& consumer_state_mask);
void AddConsumer(ConsumerChannel* channel);
void RemoveConsumer(ConsumerChannel* channel);
bool CheckParameters(uint32_t width, uint32_t height, uint32_t layer_count,
uint32_t format, uint64_t usage,
- size_t user_metadata_size);
+ size_t user_metadata_size) const;
private:
std::vector<ConsumerChannel*> consumer_channels_;
- // This counts the number of consumers left to process this buffer. If this is
- // zero then the producer can re-acquire ownership.
- int pending_consumers_;
IonBuffer buffer_;
// IonBuffer that is shared between bufferhubd, producer, and consumers.
IonBuffer metadata_buffer_;
BufferHubDefs::MetadataHeader* metadata_header_ = nullptr;
- std::atomic<uint64_t>* buffer_state_ = nullptr;
- std::atomic<uint64_t>* fence_state_ = nullptr;
+ std::atomic<uint32_t>* buffer_state_ = nullptr;
+ std::atomic<uint32_t>* fence_state_ = nullptr;
+ std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr;
- // All active consumer bits. Valid bits are the lower 63 bits, while the
- // highest bit is reserved for the producer and should not be set.
- uint64_t active_consumer_bit_mask_{0ULL};
// All orphaned consumer bits. Valid bits are the lower 63 bits, while the
// highest bit is reserved for the producer and should not be set.
- uint64_t orphaned_consumer_bit_mask_{0ULL};
+ uint32_t orphaned_consumer_bit_mask_{0U};
- bool producer_owns_;
LocalFence post_fence_;
LocalFence returned_fence_;
size_t user_metadata_size_; // size of user requested buffer buffer size.
- size_t metadata_buf_size_; // size of the ion buffer that holds metadata.
+ size_t metadata_buf_size_; // size of the ion buffer that holds metadata.
pdx::LocalHandle acquire_fence_fd_;
pdx::LocalHandle release_fence_fd_;
@@ -109,7 +107,14 @@
pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message);
pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence);
pdx::Status<LocalFence> OnProducerGain(Message& message);
- pdx::Status<RemoteChannelHandle> OnProducerDetach(Message& message);
+
+ // Remove consumer from atomics in shared memory based on consumer_state_mask.
+ // This function is used for clean up for failures in CreateConsumer method.
+ void RemoveConsumerClientMask(uint32_t consumer_state_mask);
+
+ // Checks whether the buffer is released by all active clients, excluding
+ // orphaned consumers.
+ bool IsBufferReleasedByAllActiveClientsExceptForOrphans() const;
ProducerChannel(const ProducerChannel&) = delete;
void operator=(const ProducerChannel&) = delete;
diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h
similarity index 89%
rename from services/vr/bufferhubd/producer_queue_channel.h
rename to services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h
index e825f47..0456ad8 100644
--- a/services/vr/bufferhubd/producer_queue_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h
@@ -1,10 +1,9 @@
#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
#define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
-#include "buffer_hub.h"
-
#include <pdx/status.h>
#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/buffer_hub.h>
namespace android {
namespace dvr {
@@ -38,8 +37,12 @@
uint32_t format, uint64_t usage,
size_t buffer_count);
- // Detach a BufferHubProducer indicated by |slot|. Note that the buffer must
- // be in Gain'ed state for the producer queue to detach.
+ // Inserts a ProducerBuffer into the queue. Note that the buffer must be in
+ // Gain'ed state for the operation to succeed.
+ pdx::Status<size_t> OnProducerQueueInsertBuffer(pdx::Message& message, int buffer_cid);
+
+ // Removes a ProducerBuffer indicated by |slot|. Note that the buffer must be
+ // in Gain'ed state for the operation to succeed.
pdx::Status<void> OnProducerQueueRemoveBuffer(pdx::Message& message,
size_t slot);
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index b6977aa..a7fd912 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -1,19 +1,17 @@
-#include "producer_channel.h"
-
-#include <log/log.h>
-#include <sync/sync.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/poll.h>
-#include <utils/Trace.h>
#include <algorithm>
#include <atomic>
#include <thread>
+#include <log/log.h>
#include <private/dvr/bufferhub_rpc.h>
-#include "consumer_channel.h"
-#include "detached_buffer_channel.h"
+#include <private/dvr/consumer_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
using android::pdx::BorrowedHandle;
using android::pdx::ErrorStatus;
@@ -27,14 +25,6 @@
namespace android {
namespace dvr {
-namespace {
-
-static inline uint64_t FindNextClearedBit(uint64_t bits) {
- return ~bits - (~bits & (~bits - 1));
-}
-
-} // namespace
-
ProducerChannel::ProducerChannel(BufferHubService* service, int buffer_id,
int channel_id, IonBuffer buffer,
IonBuffer metadata_buffer,
@@ -65,8 +55,6 @@
uint64_t usage, size_t user_metadata_size,
int* error)
: BufferHubChannel(service, channel_id, channel_id, kProducerType),
- pending_consumers_(0),
- producer_owns_(true),
user_metadata_size_(user_metadata_size),
metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
user_metadata_size) {
@@ -103,10 +91,16 @@
// Using placement new here to reuse shared memory instead of new allocation
// and also initialize the value to zero.
- buffer_state_ =
- new (&metadata_header_->buffer_state) std::atomic<uint64_t>(0);
- fence_state_ =
- new (&metadata_header_->fence_state) std::atomic<uint64_t>(0);
+ buffer_state_ = new (&metadata_header_->bufferState) std::atomic<uint32_t>(0);
+ fence_state_ = new (&metadata_header_->fenceState) std::atomic<uint32_t>(0);
+ active_clients_bit_mask_ =
+ new (&metadata_header_->activeClientsBitMask) std::atomic<uint32_t>(0);
+
+ // Producer channel is never created after consumer channel, and one buffer
+ // only have one fixed producer for now. Thus, it is correct to assume
+ // producer state bit is kFirstClientBitMask for now.
+ active_clients_bit_mask_->store(BufferHubDefs::kFirstClientBitMask,
+ std::memory_order_release);
acquire_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
release_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
@@ -123,7 +117,7 @@
epoll_event event;
event.events = 0;
- event.data.u64 = 0ULL;
+ event.data.u32 = 0U;
if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_ADD, dummy_fence_fd_.Get(),
&event) < 0) {
ALOGE(
@@ -168,8 +162,9 @@
ProducerChannel::~ProducerChannel() {
ALOGD_IF(TRACE,
"ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d "
- "state=%" PRIx64 ".",
- channel_id(), buffer_id(), buffer_state_->load());
+ "state=%" PRIx32 ".",
+ channel_id(), buffer_id(),
+ buffer_state_->load(std::memory_order_acquire));
for (auto consumer : consumer_channels_) {
consumer->OnProducerClosed();
}
@@ -178,15 +173,16 @@
BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
// Derive the mask of signaled buffers in this producer / consumer set.
- uint64_t signaled_mask = signaled() ? BufferHubDefs::kProducerStateBit : 0;
+ uint32_t signaled_mask = signaled() ? BufferHubDefs::kFirstClientBitMask : 0;
for (const ConsumerChannel* consumer : consumer_channels_) {
- signaled_mask |= consumer->signaled() ? consumer->consumer_state_bit() : 0;
+ signaled_mask |= consumer->signaled() ? consumer->client_state_mask() : 0;
}
return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
buffer_.height(), buffer_.layer_count(), buffer_.format(),
- buffer_.usage(), pending_consumers_, buffer_state_->load(),
- signaled_mask, metadata_header_->queue_index);
+ buffer_.usage(),
+ buffer_state_->load(std::memory_order_acquire),
+ signaled_mask, metadata_header_->queueIndex);
}
void ProducerChannel::HandleImpulse(Message& message) {
@@ -224,107 +220,192 @@
*this, &ProducerChannel::OnProducerGain, message);
return true;
- case BufferHubRPC::ProducerBufferDetach::Opcode:
- DispatchRemoteMethod<BufferHubRPC::ProducerBufferDetach>(
- *this, &ProducerChannel::OnProducerDetach, message);
- return true;
-
default:
return false;
}
}
BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer(
- uint64_t buffer_state_bit) {
- return {
- buffer_, metadata_buffer_, buffer_id(),
- buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()};
+ uint32_t client_state_mask) {
+ return {buffer_,
+ metadata_buffer_,
+ buffer_id(),
+ channel_id(),
+ client_state_mask,
+ acquire_fence_fd_.Borrow(),
+ release_fence_fd_.Borrow()};
}
Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer(
Message& /*message*/) {
ATRACE_NAME("ProducerChannel::OnGetBuffer");
- ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx64 ".",
- buffer_id(), buffer_state_->load());
- return {GetBuffer(BufferHubDefs::kProducerStateBit)};
+ ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx32 ".",
+ buffer_id(), buffer_state_->load(std::memory_order_acquire));
+ return {GetBuffer(BufferHubDefs::kFirstClientBitMask)};
}
-Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) {
- ATRACE_NAME("ProducerChannel::CreateConsumer");
- ALOGD_IF(TRACE,
- "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
- buffer_id(), producer_owns_);
+Status<uint32_t> ProducerChannel::CreateConsumerStateMask() {
+ // Try find the next consumer state bit which has not been claimed by any
+ // consumer yet.
+ // memory_order_acquire is chosen here because all writes in other threads
+ // that release active_clients_bit_mask_ need to be visible here.
+ uint32_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ uint32_t consumer_state_mask =
+ BufferHubDefs::findNextAvailableClientStateMask(
+ current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+ if (consumer_state_mask == 0U) {
+ ALOGE("%s: reached the maximum mumber of consumers per producer: 63.",
+ __FUNCTION__);
+ return ErrorStatus(E2BIG);
+ }
+ uint32_t updated_active_clients_bit_mask =
+ current_active_clients_bit_mask | consumer_state_mask;
+ // Set the updated value only if the current value stays the same as what was
+ // read before. If the comparison succeeds, update the value without
+ // reordering anything before or after this read-modify-write in the current
+ // thread, and the modification will be visible in other threads that acquire
+ // active_clients_bit_mask_. If the comparison fails, load the result of
+ // all writes from all threads to updated_active_clients_bit_mask.
+ // Keep on finding the next available slient state mask until succeed or out
+ // of memory.
+ while (!active_clients_bit_mask_->compare_exchange_weak(
+ current_active_clients_bit_mask, updated_active_clients_bit_mask,
+ std::memory_order_acq_rel, std::memory_order_acquire)) {
+ ALOGE("%s: Current active clients bit mask is changed to %" PRIx32
+ ", which was expected to be %" PRIx32
+ ". Trying to generate a new client state mask to resolve race "
+ "condition.",
+ __FUNCTION__, updated_active_clients_bit_mask,
+ current_active_clients_bit_mask);
+ consumer_state_mask = BufferHubDefs::findNextAvailableClientStateMask(
+ current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+ if (consumer_state_mask == 0U) {
+ ALOGE("%s: reached the maximum mumber of consumers per producer: %d.",
+ __FUNCTION__, (BufferHubDefs::kMaxNumberOfClients - 1));
+ return ErrorStatus(E2BIG);
+ }
+ updated_active_clients_bit_mask =
+ current_active_clients_bit_mask | consumer_state_mask;
+ }
+
+ return {consumer_state_mask};
+}
+
+void ProducerChannel::RemoveConsumerClientMask(uint32_t consumer_state_mask) {
+ // Clear up the buffer state and fence state in case there is already
+ // something there due to possible race condition between producer post and
+ // consumer failed to create channel.
+ buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+ fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+ // Restore the consumer state bit and make it visible in other threads that
+ // acquire the active_clients_bit_mask_.
+ active_clients_bit_mask_->fetch_and(~consumer_state_mask,
+ std::memory_order_release);
+}
+
+Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(
+ Message& message, uint32_t consumer_state_mask) {
+ ATRACE_NAME(__FUNCTION__);
+ ALOGD("%s: buffer_id=%d", __FUNCTION__, buffer_id());
int channel_id;
auto status = message.PushChannel(0, nullptr, &channel_id);
if (!status) {
- ALOGE(
- "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
- status.GetErrorMessage().c_str());
+ ALOGE("%s: Failed to push consumer channel: %s", __FUNCTION__,
+ status.GetErrorMessage().c_str());
+ RemoveConsumerClientMask(consumer_state_mask);
return ErrorStatus(ENOMEM);
}
- // Try find the next consumer state bit which has not been claimed by any
- // consumer yet.
- uint64_t consumer_state_bit = FindNextClearedBit(
- active_consumer_bit_mask_ | orphaned_consumer_bit_mask_ |
- BufferHubDefs::kProducerStateBit);
- if (consumer_state_bit == 0ULL) {
- ALOGE(
- "ProducerChannel::CreateConsumer: reached the maximum mumber of "
- "consumers per producer: 63.");
- return ErrorStatus(E2BIG);
- }
-
- auto consumer =
- std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id,
- consumer_state_bit, shared_from_this());
+ auto consumer = std::make_shared<ConsumerChannel>(
+ service(), buffer_id(), channel_id, consumer_state_mask,
+ shared_from_this());
const auto channel_status = service()->SetChannel(channel_id, consumer);
if (!channel_status) {
- ALOGE(
- "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
- "%s",
- channel_status.GetErrorMessage().c_str());
+ ALOGE("%s: failed to set new consumer channel: %s.", __FUNCTION__,
+ channel_status.GetErrorMessage().c_str());
+ RemoveConsumerClientMask(consumer_state_mask);
return ErrorStatus(ENOMEM);
}
- if (!producer_owns_ &&
- !BufferHubDefs::IsBufferReleased(buffer_state_->load())) {
- // Signal the new consumer when adding it to a posted producer.
- if (consumer->OnProducerPosted())
- pending_consumers_++;
+ uint32_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ // Return the consumer channel handle without signal when adding the new
+ // consumer to a buffer that is available to producer (a.k.a a fully-released
+ // buffer) or a gained buffer.
+ if (current_buffer_state == 0U ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state)) {
+ return {status.take()};
}
- active_consumer_bit_mask_ |= consumer_state_bit;
+ // Signal the new consumer when adding it to a posted producer.
+ bool update_buffer_state = true;
+ if (!BufferHubDefs::isClientPosted(current_buffer_state,
+ consumer_state_mask)) {
+ uint32_t updated_buffer_state =
+ current_buffer_state ^
+ (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGI(
+ "%s: Failed to post to the new consumer. "
+ "Current buffer state was changed to %" PRIx32
+ " when trying to acquire the buffer and modify the buffer state to "
+ "%" PRIx32
+ ". About to try again if the buffer is still not gained nor fully "
+ "released.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (current_buffer_state == 0U ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state)) {
+ ALOGI("%s: buffer is gained or fully released, state=%" PRIx32 ".",
+ __FUNCTION__, current_buffer_state);
+ update_buffer_state = false;
+ break;
+ }
+ updated_buffer_state =
+ current_buffer_state ^
+ (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+ }
+ }
+ if (update_buffer_state || BufferHubDefs::isClientPosted(
+ buffer_state_->load(std::memory_order_acquire),
+ consumer_state_mask)) {
+ consumer->OnProducerPosted();
+ }
+
return {status.take()};
}
Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) {
ATRACE_NAME("ProducerChannel::OnNewConsumer");
ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
- return CreateConsumer(message);
+ auto status = CreateConsumerStateMask();
+ if (!status.ok()) {
+ return status.error_status();
+ }
+ return CreateConsumer(message, /*consumer_state_mask=*/status.get());
}
-Status<void> ProducerChannel::OnProducerPost(
- Message&, LocalFence acquire_fence) {
+Status<void> ProducerChannel::OnProducerPost(Message&,
+ LocalFence acquire_fence) {
ATRACE_NAME("ProducerChannel::OnProducerPost");
- ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
- if (!producer_owns_) {
- ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
- return ErrorStatus(EBUSY);
- }
+ ALOGD_IF(TRACE, "%s: buffer_id=%d, state=0x%x", __FUNCTION__, buffer_id(),
+ buffer_state_->load(std::memory_order_acquire));
epoll_event event;
event.events = 0;
- event.data.u64 = 0ULL;
+ event.data.u32 = 0U;
int ret = epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
dummy_fence_fd_.Get(), &event);
ALOGE_IF(ret < 0,
- "ProducerChannel::OnProducerPost: Failed to modify the shared "
- "release fence to include the dummy fence: %s",
- strerror(errno));
+ "ProducerChannel::OnProducerPost: Failed to modify the shared "
+ "release fence to include the dummy fence: %s",
+ strerror(errno));
- eventfd_t dummy_fence_count = 0ULL;
+ eventfd_t dummy_fence_count = 0U;
if (eventfd_read(dummy_fence_fd_.Get(), &dummy_fence_count) < 0) {
const int error = errno;
if (error != EAGAIN) {
@@ -343,57 +424,44 @@
dummy_fence_count, buffer_id());
post_fence_ = std::move(acquire_fence);
- producer_owns_ = false;
// Signal any interested consumers. If there are none, the buffer will stay
// in posted state until a consumer comes online. This behavior guarantees
// that no frame is silently dropped.
- pending_consumers_ = 0;
- for (auto consumer : consumer_channels_) {
- if (consumer->OnProducerPosted())
- pending_consumers_++;
+ for (auto& consumer : consumer_channels_) {
+ consumer->OnProducerPosted();
}
- ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
- pending_consumers_);
return {};
}
Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) {
ATRACE_NAME("ProducerChannel::OnGain");
- ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
- channel_id());
- return ErrorStatus(EALREADY);
- }
-
- // There are still pending consumers, return busy.
- if (pending_consumers_ > 0) {
- ALOGE(
- "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
- "still has %d pending consumer(s).",
- buffer_id(), pending_consumers_);
- return ErrorStatus(EBUSY);
- }
+ ALOGD_IF(TRACE, "%s: buffer_id=%d", __FUNCTION__, buffer_id());
ClearAvailable();
- producer_owns_ = true;
post_fence_.close();
+ for (auto& consumer : consumer_channels_) {
+ consumer->OnProducerGained();
+ }
return {std::move(returned_fence_)};
}
-Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach(
+// TODO(b/112338294) Keep here for reference. Remove it after new logic is
+// written.
+/* Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach(
Message& message) {
ATRACE_NAME("ProducerChannel::OnProducerDetach");
ALOGD_IF(TRACE, "ProducerChannel::OnProducerDetach: buffer_id=%d",
buffer_id());
- uint64_t buffer_state = buffer_state_->load();
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
- // Can only detach a BufferProducer when it's in gained state.
+ uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::isClientGained(
+ buffer_state, BufferHubDefs::kFirstClientStateMask)) {
+ // Can only detach a ProducerBuffer when it's in gained state.
ALOGW(
- "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+ "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=%"
+ PRIx32
") is not in gained state.",
buffer_id(), buffer_state);
return {};
@@ -415,10 +483,9 @@
return ErrorStatus(-ret);
};
- std::unique_ptr<DetachedBufferChannel> channel =
- DetachedBufferChannel::Create(
- service(), buffer_id(), channel_id, std::move(buffer_),
- std::move(metadata_buffer_), user_metadata_size_);
+ std::unique_ptr<BufferChannel> channel =
+ BufferChannel::Create(service(), buffer_id(), channel_id,
+ std::move(buffer_), user_metadata_size_);
if (!channel) {
ALOGE("ProducerChannel::OnProducerDetach: Invalid buffer.");
return ErrorStatus(EINVAL);
@@ -427,28 +494,23 @@
const auto channel_status =
service()->SetChannel(channel_id, std::move(channel));
if (!channel_status) {
- // Technically, this should never fail, as we just pushed the channel. Note
- // that LOG_FATAL will be stripped out in non-debug build.
+ // Technically, this should never fail, as we just pushed the channel.
+ // Note that LOG_FATAL will be stripped out in non-debug build.
LOG_FATAL(
- "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
- "channel: %s.",
- channel_status.GetErrorMessage().c_str());
+ "ProducerChannel::OnProducerDetach: Failed to set new detached "
+ "buffer channel: %s.", channel_status.GetErrorMessage().c_str());
}
return status;
-}
+} */
Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) {
ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
- return ErrorStatus(EBUSY);
- }
- // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
- // Serialization just needs to read the handle.
+ // Return a borrowed fd to avoid unnecessary duplication of the underlying
+ // fd. Serialization just needs to read the handle.
return {std::move(post_fence_)};
}
@@ -457,10 +519,6 @@
ATRACE_NAME("ProducerChannel::OnConsumerRelease");
ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
- return ErrorStatus(EBUSY);
- }
// Attempt to merge the fences if necessary.
if (release_fence) {
@@ -480,74 +538,51 @@
}
}
- OnConsumerIgnored();
- if (pending_consumers_ == 0) {
- // Clear the producer bit atomically to transit into released state. This
- // has to done by BufferHub as it requries synchronization among all
- // consumers.
- BufferHubDefs::ModifyBufferState(buffer_state_,
- BufferHubDefs::kProducerStateBit, 0ULL);
- ALOGD_IF(TRACE,
- "ProducerChannel::OnConsumerRelease: releasing last consumer: "
- "buffer_id=%d state=%" PRIx64 ".",
- buffer_id(), buffer_state_->load());
-
+ if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
+ buffer_state_->store(0U);
+ SignalAvailable();
if (orphaned_consumer_bit_mask_) {
ALOGW(
- "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
- "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
- " queue_index=%" PRIu64 ".",
- buffer_id(), orphaned_consumer_bit_mask_,
- metadata_header_->queue_index);
+ "%s: orphaned buffer detected during the this acquire/release cycle: "
+ "id=%d orphaned=0x%" PRIx32 " queue_index=%" PRId64 ".",
+ __FUNCTION__, buffer_id(), orphaned_consumer_bit_mask_,
+ metadata_header_->queueIndex);
orphaned_consumer_bit_mask_ = 0;
}
-
- SignalAvailable();
}
- ALOGE_IF(pending_consumers_ &&
- BufferHubDefs::IsBufferReleased(buffer_state_->load()),
- "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
- "pending_consumers=%d, buffer buffer is in releaed state.",
- pending_consumers_);
return {};
}
-void ProducerChannel::OnConsumerIgnored() {
- if (pending_consumers_ == 0) {
- ALOGE("ProducerChannel::OnConsumerIgnored: no pending consumer.");
- return;
+void ProducerChannel::OnConsumerOrphaned(const uint32_t& consumer_state_mask) {
+ // Remember the ignored consumer so that newly added consumer won't be
+ // taking the same state mask as this orphaned consumer.
+ ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_mask,
+ "%s: Consumer (consumer_state_mask=%" PRIx32
+ ") is already orphaned.",
+ __FUNCTION__, consumer_state_mask);
+ orphaned_consumer_bit_mask_ |= consumer_state_mask;
+
+ if (IsBufferReleasedByAllActiveClientsExceptForOrphans()) {
+ buffer_state_->store(0U);
+ SignalAvailable();
}
- --pending_consumers_;
- ALOGD_IF(TRACE,
- "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
- buffer_id(), pending_consumers_);
-}
-
-void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
- // Ignore the orphaned consumer.
- OnConsumerIgnored();
-
- const uint64_t consumer_state_bit = channel->consumer_state_bit();
- ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_bit,
- "ProducerChannel::OnConsumerOrphaned: Consumer "
- "(consumer_state_bit=%" PRIx64 ") is already orphaned.",
- consumer_state_bit);
- orphaned_consumer_bit_mask_ |= consumer_state_bit;
-
// Atomically clear the fence state bit as an orphaned consumer will never
- // signal a release fence. Also clear the buffer state as it won't be released
- // as well.
- fence_state_->fetch_and(~consumer_state_bit);
- BufferHubDefs::ModifyBufferState(buffer_state_, consumer_state_bit, 0ULL);
+ // signal a release fence.
+ fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+ // Atomically set the buffer state of this consumer to released state.
+ buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
ALOGW(
- "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
- "buffer_id=%d consumer_state_bit=%" PRIx64 " queue_index=%" PRIu64
- " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
- buffer_id(), consumer_state_bit, metadata_header_->queue_index,
- buffer_state_->load(), fence_state_->load());
+ "%s: detected new orphaned consumer buffer_id=%d "
+ "consumer_state_mask=%" PRIx32 " queue_index=%" PRId64
+ " buffer_state=%" PRIx32 " fence_state=%" PRIx32 ".",
+ __FUNCTION__, buffer_id(), consumer_state_mask,
+ metadata_header_->queueIndex,
+ buffer_state_->load(std::memory_order_acquire),
+ fence_state_->load(std::memory_order_acquire));
}
void ProducerChannel::AddConsumer(ConsumerChannel* channel) {
@@ -557,52 +592,82 @@
void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
consumer_channels_.erase(
std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
- active_consumer_bit_mask_ &= ~channel->consumer_state_bit();
+ // Restore the consumer state bit and make it visible in other threads that
+ // acquire the active_clients_bit_mask_.
+ uint32_t consumer_state_mask = channel->client_state_mask();
+ uint32_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ uint32_t updated_active_clients_bit_mask =
+ current_active_clients_bit_mask & (~consumer_state_mask);
+ while (!active_clients_bit_mask_->compare_exchange_weak(
+ current_active_clients_bit_mask, updated_active_clients_bit_mask,
+ std::memory_order_acq_rel, std::memory_order_acquire)) {
+ ALOGI(
+ "%s: Failed to remove consumer state mask. Current active clients bit "
+ "mask is changed to %" PRIx32
+ " when trying to acquire and modify it to %" PRIx32
+ ". About to try again.",
+ __FUNCTION__, current_active_clients_bit_mask,
+ updated_active_clients_bit_mask);
+ updated_active_clients_bit_mask =
+ current_active_clients_bit_mask & (~consumer_state_mask);
+ }
- const uint64_t buffer_state = buffer_state_->load();
- if (BufferHubDefs::IsBufferPosted(buffer_state) ||
- BufferHubDefs::IsBufferAcquired(buffer_state)) {
+ const uint32_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::isClientPosted(current_buffer_state,
+ consumer_state_mask) ||
+ BufferHubDefs::isClientAcquired(current_buffer_state,
+ consumer_state_mask)) {
// The consumer client is being destoryed without releasing. This could
// happen in corner cases when the consumer crashes. Here we mark it
// orphaned before remove it from producer.
- OnConsumerOrphaned(channel);
+ OnConsumerOrphaned(consumer_state_mask);
+ return;
}
- if (BufferHubDefs::IsBufferReleased(buffer_state) ||
- BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (BufferHubDefs::isClientReleased(current_buffer_state,
+ consumer_state_mask) ||
+ BufferHubDefs::isAnyClientGained(current_buffer_state)) {
// The consumer is being close while it is suppose to signal a release
// fence. Signal the dummy fence here.
- if (fence_state_->load() & channel->consumer_state_bit()) {
+ if (fence_state_->load(std::memory_order_acquire) & consumer_state_mask) {
epoll_event event;
event.events = EPOLLIN;
- event.data.u64 = channel->consumer_state_bit();
+ event.data.u32 = consumer_state_mask;
if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
dummy_fence_fd_.Get(), &event) < 0) {
ALOGE(
- "ProducerChannel::RemoveConsumer: Failed to modify the shared "
- "release fence to include the dummy fence: %s",
- strerror(errno));
+ "%s: Failed to modify the shared release fence to include the "
+ "dummy fence: %s",
+ __FUNCTION__, strerror(errno));
return;
}
- ALOGW(
- "ProducerChannel::RemoveConsumer: signal dummy release fence "
- "buffer_id=%d",
- buffer_id());
+ ALOGW("%s: signal dummy release fence buffer_id=%d", __FUNCTION__,
+ buffer_id());
eventfd_write(dummy_fence_fd_.Get(), 1);
}
}
}
-// Returns true if the given parameters match the underlying buffer parameters.
+// Returns true if the given parameters match the underlying buffer
+// parameters.
bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format,
uint64_t usage,
- size_t user_metadata_size) {
+ size_t user_metadata_size) const {
return user_metadata_size == user_metadata_size_ &&
buffer_.width() == width && buffer_.height() == height &&
buffer_.layer_count() == layer_count && buffer_.format() == format &&
buffer_.usage() == usage;
}
+bool ProducerChannel::IsBufferReleasedByAllActiveClientsExceptForOrphans()
+ const {
+ return (buffer_state_->load(std::memory_order_acquire) &
+ ~orphaned_consumer_bit_mask_ &
+ active_clients_bit_mask_->load(std::memory_order_acquire)) == 0U;
+}
+
} // namespace dvr
} // namespace android
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index c0c48c2..004dc7c 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -1,9 +1,8 @@
-#include "producer_queue_channel.h"
-
#include <inttypes.h>
-#include "consumer_queue_channel.h"
-#include "producer_channel.h"
+#include <private/dvr/consumer_queue_channel.h>
+#include <private/dvr/producer_channel.h>
+#include <private/dvr/producer_queue_channel.h>
using android::pdx::ErrorStatus;
using android::pdx::Message;
@@ -76,6 +75,11 @@
message);
return true;
+ case BufferHubRPC::ProducerQueueInsertBuffer::Opcode:
+ DispatchRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
+ *this, &ProducerQueueChannel::OnProducerQueueInsertBuffer, message);
+ return true;
+
case BufferHubRPC::ProducerQueueRemoveBuffer::Opcode:
DispatchRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(
*this, &ProducerQueueChannel::OnProducerQueueRemoveBuffer, message);
@@ -278,6 +282,86 @@
return {{std::move(buffer_handle), slot}};
}
+Status<size_t> ProducerQueueChannel::OnProducerQueueInsertBuffer(
+ pdx::Message& message, int buffer_cid) {
+ ATRACE_NAME("ProducerQueueChannel::InsertBuffer");
+ ALOGD_IF(TRACE,
+ "ProducerQueueChannel::InsertBuffer: channel_id=%d, buffer_cid=%d",
+ channel_id(), buffer_cid);
+
+ if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) {
+ ALOGE("ProducerQueueChannel::InsertBuffer: reaches kMaxQueueCapacity.");
+ return ErrorStatus(E2BIG);
+ }
+ auto producer_channel = std::static_pointer_cast<ProducerChannel>(
+ service()->GetChannel(buffer_cid));
+ if (producer_channel == nullptr ||
+ producer_channel->channel_type() != BufferHubChannel::kProducerType) {
+ // Rejects the request if the requested buffer channel is invalid and/or
+ // it's not a ProducerChannel.
+ ALOGE(
+ "ProducerQueueChannel::InsertBuffer: Invalid buffer_cid=%d, "
+ "producer_buffer=0x%p, channel_type=%d.",
+ buffer_cid, producer_channel.get(),
+ producer_channel == nullptr ? -1 : producer_channel->channel_type());
+ return ErrorStatus(EINVAL);
+ }
+ if (producer_channel->GetActiveProcessId() != message.GetProcessId()) {
+ // Rejects the request if the requested buffer channel is not currently
+ // connected to the caller this is IPC request. This effectively prevents
+ // fake buffer_cid from being injected.
+ ALOGE(
+ "ProducerQueueChannel::InsertBuffer: Requested buffer channel "
+ "(buffer_cid=%d) is not connected to the calling process (pid=%d). "
+ "It's connected to a different process (pid=%d).",
+ buffer_cid, message.GetProcessId(),
+ producer_channel->GetActiveProcessId());
+ return ErrorStatus(EINVAL);
+ }
+ uint64_t buffer_state = producer_channel->buffer_state();
+ // TODO(b/112007999) add an atomic variable in metadata header in shared
+ // memory to indicate which client is the last producer of the buffer.
+ // Currently, the first client is the only producer to the buffer.
+ // Thus, it checks whether the first client gains the buffer below.
+ if (!BufferHubDefs::isClientGained(buffer_state,
+ BufferHubDefs::kFirstClientBitMask)) {
+ // Rejects the request if the requested buffer is not in Gained state.
+ ALOGE(
+ "ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, "
+ "state=0x%" PRIx64 ") is not in gained state.",
+ buffer_cid, buffer_state);
+ return ErrorStatus(EINVAL);
+ }
+
+ // Register the to-be-inserted buffer's channel_id into the first empty
+ // buffer slot.
+ size_t slot = 0;
+ for (; slot < BufferHubRPC::kMaxQueueCapacity; slot++) {
+ if (buffers_[slot].expired())
+ break;
+ }
+ if (slot == BufferHubRPC::kMaxQueueCapacity) {
+ ALOGE(
+ "ProducerQueueChannel::AllocateBuffer: Cannot find empty slot for new "
+ "buffer allocation.");
+ return ErrorStatus(E2BIG);
+ }
+
+ buffers_[slot] = producer_channel;
+ capacity_++;
+
+ // Notify each consumer channel about the new buffer.
+ for (auto* consumer_channel : consumer_channels_) {
+ ALOGD(
+ "ProducerQueueChannel::AllocateBuffer: Notified consumer with new "
+ "buffer, buffer_cid=%d",
+ buffer_cid);
+ consumer_channel->RegisterNewBuffer(producer_channel, slot);
+ }
+
+ return {slot};
+}
+
Status<void> ProducerQueueChannel::OnProducerQueueRemoveBuffer(
Message& /*message*/, size_t slot) {
if (buffers_[slot].expired()) {
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 90edf69..d9df204 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -15,6 +15,7 @@
"android.frameworks.vr.composer@1.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
"libbase",
"libbufferhubqueue",
"libbinder",
@@ -39,6 +40,10 @@
"android.hardware.graphics.composer@2.1-hal",
],
+ export_static_lib_headers: [
+ "libdisplay",
+ ],
+
export_shared_lib_headers: [
"android.frameworks.vr.composer@1.0",
"android.hardware.graphics.composer@2.1",
@@ -48,9 +53,9 @@
cflags: [
"-DLOG_TAG=\"vr_hwc\"",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
"-Wall",
"-Werror",
- // mVrClient unused in vr_composer_client.cpp
"-Wno-error=unused-private-field",
// Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
"-Wno-sign-compare",
@@ -115,6 +120,7 @@
cc_binary {
name: "vr_hwc",
+ vintf_fragments: ["manifest_vr_hwc.xml"],
srcs: [
"vr_hardware_composer_service.cpp"
],
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
index d775711..786d5fa 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -45,7 +45,7 @@
}
VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
- : ComposerCommandEngine(client.mHal, client.mResources.get()), mVrClient(client),
+ : ComposerCommandEngine(client.mHal, client.mResources.get()),
mVrHal(client.mVrHal) {}
VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
index 2ad95fc..0b7ce5e 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -54,7 +54,6 @@
IVrComposerClient::BufferMetadata readBufferMetadata();
- VrComposerClient& mVrClient;
android::dvr::VrHwc& mVrHal;
VrCommandEngine(const VrCommandEngine&) = delete;
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 4af47d2..fb7932d 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -16,9 +16,11 @@
#include "impl/vr_hwc.h"
#include "android-base/stringprintf.h"
+#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <private/dvr/display_client.h>
#include <ui/Fence.h>
+#include <utils/Trace.h>
#include <mutex>
@@ -244,29 +246,38 @@
////////////////////////////////////////////////////////////////////////////////
// VrHwcClient
-VrHwc::VrHwc() {}
+VrHwc::VrHwc() {
+ vsync_callback_ = new VsyncCallback;
+}
-VrHwc::~VrHwc() {}
+VrHwc::~VrHwc() {
+ vsync_callback_->SetEventCallback(nullptr);
+}
bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
void VrHwc::registerEventCallback(EventCallback* callback) {
- {
- std::lock_guard<std::mutex> guard(mutex_);
- event_callback_ = callback;
- int32_t width, height;
- GetPrimaryDisplaySize(&width, &height);
- // Create the primary display late to avoid initialization issues between
- // VR HWC and SurfaceFlinger.
- displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
- }
+ std::unique_lock<std::mutex> lock(mutex_);
+ event_callback_ = callback;
+ int32_t width, height;
+ GetPrimaryDisplaySize(&width, &height);
+ // Create the primary display late to avoid initialization issues between
+ // VR HWC and SurfaceFlinger.
+ displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
+
+ // Surface flinger will make calls back into vr_hwc when it receives the
+ // onHotplug() call, so it's important to release mutex_ here.
+ lock.unlock();
event_callback_->onHotplug(kDefaultDisplayId,
IComposerCallback::Connection::CONNECTED);
+ lock.lock();
+ UpdateVsyncCallbackEnabledLocked();
}
void VrHwc::unregisterEventCallback() {
std::lock_guard<std::mutex> guard(mutex_);
event_callback_ = nullptr;
+ UpdateVsyncCallbackEnabledLocked();
}
uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
@@ -321,10 +332,14 @@
return Error::NONE;
}
-Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */,
+Error VrHwc::getClientTargetSupport(Display display, uint32_t /* width */,
uint32_t /* height */,
PixelFormat /* format */,
Dataspace /* dataspace */) {
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (!FindDisplay(display))
+ return Error::BAD_DISPLAY;
+
return Error::NONE;
}
@@ -455,16 +470,37 @@
if (!display_ptr)
return Error::BAD_DISPLAY;
+ if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
+ return Error::BAD_PARAMETER;
+
display_ptr->set_color_mode(mode);
return Error::NONE;
}
Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+ bool dozeSupported = false;
+
+ Error dozeSupportError = getDozeSupport(display, &dozeSupported);
+
+ if (dozeSupportError != Error::NONE)
+ return dozeSupportError;
+
std::lock_guard<std::mutex> guard(mutex_);
auto display_ptr = FindDisplay(display);
if (!display_ptr)
return Error::BAD_DISPLAY;
+ if (mode < IComposerClient::PowerMode::OFF ||
+ mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
+ return Error::BAD_PARAMETER;
+ }
+
+ if (!dozeSupported &&
+ (mode == IComposerClient::PowerMode::DOZE ||
+ mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
+ return Error::UNSUPPORTED;
+ }
+
display_ptr->set_power_mode(mode);
return Error::NONE;
}
@@ -475,8 +511,45 @@
if (!display_ptr)
return Error::BAD_DISPLAY;
- display_ptr->set_vsync_enabled(enabled);
- return Error::NONE;
+ if (enabled != IComposerClient::Vsync::ENABLE &&
+ enabled != IComposerClient::Vsync::DISABLE) {
+ return Error::BAD_PARAMETER;
+ }
+
+ Error set_vsync_result = Error::NONE;
+ if (display == kDefaultDisplayId) {
+ sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
+ defaultServiceManager()->getService(
+ String16(IVsyncService::GetServiceName())));
+ if (vsync_service == nullptr) {
+ ALOGE("Failed to get vsync service");
+ return Error::NO_RESOURCES;
+ }
+
+ if (enabled == IComposerClient::Vsync::ENABLE) {
+ ALOGI("Enable vsync");
+ display_ptr->set_vsync_enabled(true);
+ status_t result = vsync_service->registerCallback(vsync_callback_);
+ if (result != OK) {
+ ALOGE("%s service registerCallback() failed: %s (%d)",
+ IVsyncService::GetServiceName(), strerror(-result), result);
+ set_vsync_result = Error::NO_RESOURCES;
+ }
+ } else if (enabled == IComposerClient::Vsync::DISABLE) {
+ ALOGI("Disable vsync");
+ display_ptr->set_vsync_enabled(false);
+ status_t result = vsync_service->unregisterCallback(vsync_callback_);
+ if (result != OK) {
+ ALOGE("%s service unregisterCallback() failed: %s (%d)",
+ IVsyncService::GetServiceName(), strerror(-result), result);
+ set_vsync_result = Error::NO_RESOURCES;
+ }
+ }
+
+ UpdateVsyncCallbackEnabledLocked();
+ }
+
+ return set_vsync_result;
}
Error VrHwc::setColorTransform(Display display, const float* matrix,
@@ -559,7 +632,8 @@
frame.display_height = display_ptr->height();
frame.active_config = display_ptr->active_config();
frame.power_mode = display_ptr->power_mode();
- frame.vsync_enabled = display_ptr->vsync_enabled();
+ frame.vsync_enabled = display_ptr->vsync_enabled() ?
+ IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
frame.color_transform_hint = display_ptr->color_transform_hint();
frame.color_mode = display_ptr->color_mode();
memcpy(frame.color_transform, display_ptr->color_transform(),
@@ -911,6 +985,15 @@
return iter == displays_.end() ? nullptr : iter->second.get();
}
+void VrHwc::UpdateVsyncCallbackEnabledLocked() {
+ auto primary_display = FindDisplay(kDefaultDisplayId);
+ LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
+ "Should have created the primary display by now");
+ bool send_vsync =
+ event_callback_ != nullptr && primary_display->vsync_enabled();
+ vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
+}
+
void HwcLayer::dumpDebugInfo(std::string* result) const {
if (!result) {
return;
@@ -928,5 +1011,18 @@
buffer_metadata.layerCount, buffer_metadata.format);
}
+status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
+ ATRACE_NAME("vr_hwc onVsync");
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (callback_ != nullptr)
+ callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
+ return OK;
+}
+
+void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
+ std::lock_guard<std::mutex> guard(mutex_);
+ callback_ = callback;
+}
+
} // namespace dvr
} // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index 74c1699..e8c0212 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -20,6 +20,7 @@
#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
#include <android/hardware/graphics/composer/2.1/IComposer.h>
#include <composer-hal/2.1/ComposerHal.h>
+#include <private/dvr/vsync_service.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
@@ -154,10 +155,8 @@
IComposerClient::PowerMode power_mode() const { return power_mode_; }
void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
- IComposerClient::Vsync vsync_enabled() const { return vsync_enabled_; }
- void set_vsync_enabled(IComposerClient::Vsync vsync) {
- vsync_enabled_ = vsync;
- }
+ bool vsync_enabled() const { return vsync_enabled_; }
+ void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
const float* color_transform() const { return color_transform_; }
int32_t color_transform_hint() const { return color_transform_hint_; }
@@ -185,7 +184,7 @@
Config active_config_;
ColorMode color_mode_;
IComposerClient::PowerMode power_mode_;
- IComposerClient::Vsync vsync_enabled_;
+ bool vsync_enabled_ = false;
float color_transform_[16];
int32_t color_transform_hint_;
@@ -297,8 +296,23 @@
void UnregisterObserver(Observer* observer) override;
private:
+ class VsyncCallback : public BnVsyncCallback {
+ public:
+ status_t onVsync(int64_t vsync_timestamp) override;
+ void SetEventCallback(EventCallback* callback);
+ private:
+ std::mutex mutex_;
+ EventCallback* callback_;
+ };
+
HwcDisplay* FindDisplay(Display display);
+ // Re-evaluate whether or not we should start making onVsync() callbacks to
+ // the client. We need enableCallback(true) to have been called, and
+ // setVsyncEnabled() to have been called for the primary display. The caller
+ // must have mutex_ locked already.
+ void UpdateVsyncCallbackEnabledLocked();
+
wp<VrComposerClient> client_;
// Guard access to internal state from binder threads.
@@ -310,6 +324,8 @@
EventCallback* event_callback_ = nullptr;
Observer* observer_ = nullptr;
+ sp<VsyncCallback> vsync_callback_;
+
VrHwc(const VrHwc&) = delete;
void operator=(const VrHwc&) = delete;
};
diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml
new file mode 100644
index 0000000..1068cac
--- /dev/null
+++ b/services/vr/hardware_composer/manifest_vr_hwc.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.hardware.graphics.composer</name>
+ <transport>hwbinder</transport>
+ <version>2.1</version>
+ <interface>
+ <name>IComposer</name>
+ <instance>vr</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
index 1a7264c..d940b79 100644
--- a/services/vr/performanced/cpu_set.cpp
+++ b/services/vr/performanced/cpu_set.cpp
@@ -106,7 +106,7 @@
return sets;
}
-std::string CpuSetManager::DumpState() const {
+void CpuSetManager::DumpState(std::ostringstream& stream) const {
size_t max_path = 0;
std::vector<CpuSet*> sets;
@@ -119,8 +119,6 @@
return a->path() < b->path();
});
- std::ostringstream stream;
-
stream << std::left;
stream << std::setw(max_path) << "Path";
stream << " ";
@@ -146,8 +144,6 @@
stream << std::setw(6) << set->GetTasks().size();
stream << std::endl;
}
-
- return stream.str();
}
void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
index 6879272..4c25e9e 100644
--- a/services/vr/performanced/cpu_set.h
+++ b/services/vr/performanced/cpu_set.h
@@ -5,6 +5,7 @@
#include <memory>
#include <mutex>
+#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -83,7 +84,7 @@
// to shield the system from interference from unbound kernel threads.
void MoveUnboundTasks(const std::string& target_set);
- std::string DumpState() const;
+ void DumpState(std::ostringstream& stream) const;
operator bool() const { return root_set_ != nullptr; }
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
index 4c26671..73dcf76 100644
--- a/services/vr/performanced/performance_service.cpp
+++ b/services/vr/performanced/performance_service.cpp
@@ -1,5 +1,7 @@
#include "performance_service.h"
+#include <sstream>
+
#include <sched.h>
#include <sys/prctl.h>
#include <unistd.h>
@@ -31,6 +33,10 @@
const char kRootCpuSet[] = "/";
+const char kVrAppRenderPolicy[] = "vr:app:render";
+
+const bool kAllowAppsToRequestVrAppRenderPolicy = false;
+
constexpr unsigned long kTimerSlackForegroundNs = 50000;
constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
@@ -124,9 +130,6 @@
// TODO(eieio): Replace this witha device-specific config file. This is just a
// hack for now to put some form of permission logic in place while a longer
// term solution is developed.
- using AllowRootSystem =
- CheckAnd<SameProcess,
- CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>;
using AllowRootSystemGraphics =
CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
@@ -136,6 +139,17 @@
using AllowRootSystemTrusted =
CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>;
+ auto vr_app_render_permission_check = [](
+ const pdx::Message& sender, const Task& task) {
+ // For vr:app:render, in addition to system/root apps and VrCore, we
+ // also allow apps to request vr:app:render if
+ // kAllowAppsToRequestVrAppRenderPolicy == true, but not for other
+ // apps, only for themselves.
+ return (task && task.thread_group_id() == sender.GetProcessId() &&
+ kAllowAppsToRequestVrAppRenderPolicy)
+ || AllowRootSystemTrusted::Check(sender, task);
+ };
+
partition_permission_check_ = AllowRootSystemTrusted::Check;
// Setup the scheduler classes.
@@ -170,28 +184,28 @@
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
.priority = fifo_low,
- .permission_check = AllowRootSystem::Check}},
+ .permission_check = AllowRootSystemTrusted::Check}},
{"sensors:low",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
.priority = fifo_low,
- .permission_check = AllowRootSystem::Check}},
+ .permission_check = AllowRootSystemTrusted::Check}},
{"sensors:high",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
.priority = fifo_low + 1,
- .permission_check = AllowRootSystem::Check}},
+ .permission_check = AllowRootSystemTrusted::Check}},
{"vr:system:arp",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
.priority = fifo_medium + 2,
.permission_check = AllowRootSystemTrusted::Check,
"/system/performance"}},
- {"vr:app:render",
+ {kVrAppRenderPolicy,
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
.priority = fifo_medium + 1,
- .permission_check = AllowRootSystemTrusted::Check,
+ .permission_check = vr_app_render_permission_check,
"/application/performance"}},
{"normal",
{.timer_slack = kTimerSlackForegroundNs,
@@ -218,7 +232,10 @@
}
std::string PerformanceService::DumpState(size_t /*max_length*/) {
- return cpuset_.DumpState();
+ std::ostringstream stream;
+ stream << "vr_app_render_thread: " << vr_app_render_thread_ << std::endl;
+ cpuset_.DumpState(stream);
+ return stream.str();
}
Status<void> PerformanceService::OnSetSchedulerPolicy(
@@ -244,7 +261,12 @@
// Make sure the sending process is allowed to make the requested change to
// this task.
if (!config.IsAllowed(message, task))
- return ErrorStatus(EINVAL);
+ return ErrorStatus(EPERM);
+
+ if (scheduler_policy == kVrAppRenderPolicy) {
+ // We only allow one vr:app:render thread at a time
+ SetVrAppRenderThread(task_id);
+ }
// Get the thread group's cpu set. Policies that do not specify a cpuset
// should default to this cpuset.
@@ -302,14 +324,16 @@
Status<void> PerformanceService::OnSetCpuPartition(
Message& message, pid_t task_id, const std::string& partition) {
Task task(task_id);
- if (!task || task.thread_group_id() != message.GetProcessId())
+ if (!task)
return ErrorStatus(EINVAL);
+ if (task.thread_group_id() != message.GetProcessId())
+ return ErrorStatus(EPERM);
// Temporary permission check.
// TODO(eieio): Replace this with a configuration file.
if (partition_permission_check_ &&
!partition_permission_check_(message, task)) {
- return ErrorStatus(EINVAL);
+ return ErrorStatus(EPERM);
}
auto target_set = cpuset_.Lookup(partition);
@@ -336,7 +360,12 @@
// Make sure the sending process is allowed to make the requested change to
// this task.
if (!config.IsAllowed(message, task))
- return ErrorStatus(EINVAL);
+ return ErrorStatus(EPERM);
+
+ if (scheduler_class == kVrAppRenderPolicy) {
+ // We only allow one vr:app:render thread at a time
+ SetVrAppRenderThread(task_id);
+ }
struct sched_param param;
param.sched_priority = config.priority;
@@ -359,8 +388,10 @@
pid_t task_id) {
// Make sure the task id is valid and belongs to the sending process.
Task task(task_id);
- if (!task || task.thread_group_id() != message.GetProcessId())
+ if (!task)
return ErrorStatus(EINVAL);
+ if (task.thread_group_id() != message.GetProcessId())
+ return ErrorStatus(EPERM);
return task.GetCpuSetPath();
}
@@ -393,5 +424,38 @@
}
}
+void PerformanceService::SetVrAppRenderThread(pid_t new_vr_app_render_thread) {
+ ALOGI("SetVrAppRenderThread old=%d new=%d",
+ vr_app_render_thread_, new_vr_app_render_thread);
+
+ if (vr_app_render_thread_ >= 0 &&
+ vr_app_render_thread_ != new_vr_app_render_thread) {
+ // Restore the default scheduler policy and priority on the previous
+ // vr:app:render thread.
+ struct sched_param param;
+ param.sched_priority = 0;
+ if (sched_setscheduler(vr_app_render_thread_, SCHED_NORMAL, ¶m) < 0) {
+ if (errno == ESRCH) {
+ ALOGI("Failed to revert %s scheduler policy. Couldn't find thread %d."
+ " Was the app killed?", kVrAppRenderPolicy, vr_app_render_thread_);
+ } else {
+ ALOGE("Failed to revert %s scheduler policy: %s",
+ kVrAppRenderPolicy, strerror(errno));
+ }
+ }
+
+ // Restore the default timer slack on the previous vr:app:render thread.
+ prctl(PR_SET_TIMERSLACK_PID, kTimerSlackForegroundNs,
+ vr_app_render_thread_);
+ }
+
+ // We could also reset the thread's cpuset here, but the cpuset is already
+ // managed by Android. Better to let Android adjust the cpuset as the app
+ // moves to the background, rather than adjust it ourselves here, and risk
+ // stomping on the value set by Android.
+
+ vr_app_render_thread_ = new_vr_app_render_thread;
+}
+
} // namespace dvr
} // namespace android
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
index 6b519ab..fe63756 100644
--- a/services/vr/performanced/performance_service.h
+++ b/services/vr/performanced/performance_service.h
@@ -39,6 +39,14 @@
pdx::Status<std::string> OnGetCpuPartition(pdx::Message& message,
pid_t task_id);
+ // Set which thread gets the vr:app:render policy. Only one thread at a time
+ // is allowed to have vr:app:render. If multiple threads are allowed
+ // vr:app:render, and those threads busy loop, the system can freeze. When
+ // SetVrAppRenderThread() is called, the thread which we had previously
+ // assigned vr:app:render will have its scheduling policy reset to default
+ // values.
+ void SetVrAppRenderThread(pid_t new_vr_app_render_thread);
+
CpuSetManager cpuset_;
int sched_fifo_min_priority_;
@@ -70,6 +78,8 @@
std::function<bool(const pdx::Message& message, const Task& task)>
partition_permission_check_;
+ pid_t vr_app_render_thread_ = -1;
+
PerformanceService(const PerformanceService&) = delete;
void operator=(const PerformanceService&) = delete;
};
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 673a066..71048db 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -21,6 +21,8 @@
// There are a few of them requiring manual code for things such as layer
// discovery or chaining. They call into functions defined in this file.
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <stdlib.h>
#include <string.h>
@@ -32,6 +34,7 @@
#include <android-base/strings.h>
#include <cutils/properties.h>
#include <log/log.h>
+#include <utils/Trace.h>
#include <vulkan/vk_layer_interface.h>
#include <graphicsenv/GraphicsEnv.h>
@@ -1176,6 +1179,8 @@
VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance) {
+ ATRACE_CALL();
+
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
@@ -1184,6 +1189,8 @@
void DestroyInstance(VkInstance instance,
const VkAllocationCallbacks* pAllocator) {
+ ATRACE_CALL();
+
if (instance != VK_NULL_HANDLE)
LayerChain::DestroyInstance(instance, pAllocator);
}
@@ -1192,17 +1199,23 @@
const VkDeviceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDevice* pDevice) {
+ ATRACE_CALL();
+
return LayerChain::CreateDevice(physicalDevice, pCreateInfo, pAllocator,
pDevice);
}
void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
+ ATRACE_CALL();
+
if (device != VK_NULL_HANDLE)
LayerChain::DestroyDevice(device, pAllocator);
}
VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
VkLayerProperties* pProperties) {
+ ATRACE_CALL();
+
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
@@ -1225,6 +1238,8 @@
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
+ ATRACE_CALL();
+
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
@@ -1253,6 +1268,8 @@
VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
uint32_t* pPropertyCount,
VkLayerProperties* pProperties) {
+ ATRACE_CALL();
+
uint32_t count;
const LayerChain::ActiveLayer* layers =
LayerChain::GetActiveLayers(physicalDevice, count);
@@ -1275,6 +1292,8 @@
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
+ ATRACE_CALL();
+
if (pLayerName) {
// EnumerateDeviceLayerProperties enumerates active layers for
// backward compatibility. The extension query here should work for
@@ -1302,6 +1321,8 @@
}
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
+ ATRACE_CALL();
+
*pApiVersion = VK_API_VERSION_1_1;
return VK_SUCCESS;
}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index fdbd969..b3259de 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
@@ -31,6 +33,8 @@
#include <configstore/Utils.h>
#include <cutils/properties.h>
#include <graphicsenv/GraphicsEnv.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
#include <utils/Vector.h>
#include "android-base/properties.h"
@@ -150,6 +154,8 @@
void* LoadLibrary(const android_dlextinfo& dlextinfo,
const char* subname,
int subname_len) {
+ ATRACE_CALL();
+
const char kLibFormat[] = "vulkan.%*s.so";
char* name = static_cast<char*>(
alloca(sizeof(kLibFormat) + static_cast<size_t>(subname_len)));
@@ -164,6 +170,8 @@
int LoadDriver(android_namespace_t* library_namespace,
const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = library_namespace,
@@ -198,20 +206,32 @@
}
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
auto ns = android_get_exported_namespace("sphal");
if (!ns)
return -ENOENT;
+ android::GraphicsEnv::getInstance().setDriverToLoad(
+ android::GraphicsEnv::Driver::VULKAN);
return LoadDriver(ns, module);
}
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
auto ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns)
return -ENOENT;
+ android::GraphicsEnv::getInstance().setDriverToLoad(
+ android::GraphicsEnv::Driver::VULKAN_UPDATED);
return LoadDriver(ns, module);
}
bool Hal::Open() {
+ ATRACE_CALL();
+
+ const nsecs_t openTime = systemTime();
+
ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
// Use a stub device unless we successfully open a real HAL device.
@@ -237,15 +257,22 @@
}
}
if (result != 0) {
+ android::GraphicsEnv::getInstance().setDriverLoaded(
+ android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime);
ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result);
return true;
}
+
hwvulkan_device_t* device;
+ ATRACE_BEGIN("hwvulkan module open");
result =
module->common.methods->open(&module->common, HWVULKAN_DEVICE_0,
reinterpret_cast<hw_device_t**>(&device));
+ ATRACE_END();
if (result != 0) {
+ android::GraphicsEnv::getInstance().setDriverLoaded(
+ android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime);
// Any device with a Vulkan HAL should be able to open the device.
ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result),
result);
@@ -256,10 +283,15 @@
hal_.InitDebugReportIndex();
+ android::GraphicsEnv::getInstance().setDriverLoaded(
+ android::GraphicsEnv::Api::API_VK, true, systemTime() - openTime);
+
return true;
}
bool Hal::InitDebugReportIndex() {
+ ATRACE_CALL();
+
uint32_t count;
if (dev_->EnumerateInstanceExtensionProperties(nullptr, &count, nullptr) !=
VK_SUCCESS) {
@@ -821,8 +853,10 @@
}
}
+ ATRACE_BEGIN("driver.EnumerateInstanceExtensionProperties");
VkResult result = Hal::Device().EnumerateInstanceExtensionProperties(
pLayerName, pPropertyCount, pProperties);
+ ATRACE_END();
if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
int idx = Hal::Get().GetDebugReportIndex();
@@ -931,8 +965,10 @@
*pPropertyCount -= count;
}
+ ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
VkResult result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
+ ATRACE_END();
if (pProperties) {
// map VK_ANDROID_native_buffer to VK_KHR_swapchain
@@ -968,12 +1004,15 @@
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)
@@ -984,12 +1023,14 @@
uint32_t icd_api_version;
PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version =
reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
- Hal::Device().GetInstanceProcAddr(NULL,
+ Hal::Device().GetInstanceProcAddr(nullptr,
"vkEnumerateInstanceVersion"));
if (!pfn_enumerate_instance_version) {
icd_api_version = VK_API_VERSION_1_0;
} else {
+ 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);
@@ -1000,12 +1041,15 @@
wrapper.DowngradeApiVersion();
}
#pragma clang diagnostic pop
+ ATRACE_END();
// call into the driver
VkInstance instance;
+ ATRACE_BEGIN("driver.CreateInstance");
result = Hal::Device().CreateInstance(
static_cast<const VkInstanceCreateInfo*>(wrapper), pAllocator,
&instance);
+ ATRACE_END();
if (result != VK_SUCCESS) {
FreeInstanceData(data, data_allocator);
return result;
@@ -1066,8 +1110,10 @@
if (result != VK_SUCCESS)
return result;
+ ATRACE_BEGIN("AllocateDeviceData");
DeviceData* data = AllocateDeviceData(data_allocator,
instance_data.debug_report_callbacks);
+ ATRACE_END();
if (!data)
return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -1075,9 +1121,11 @@
// call into the driver
VkDevice dev;
+ ATRACE_BEGIN("driver.CreateDevice");
result = instance_data.driver.CreateDevice(
physicalDevice, static_cast<const VkDeviceCreateInfo*>(wrapper),
pAllocator, &dev);
+ ATRACE_END();
if (result != VK_SUCCESS) {
FreeDeviceData(data, data_allocator);
return result;
@@ -1114,8 +1162,10 @@
}
VkPhysicalDeviceProperties properties;
+ ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
&properties);
+ ATRACE_END();
data->driver_device = dev;
data->driver_version = properties.driverVersion;
@@ -1141,6 +1191,8 @@
VkResult EnumeratePhysicalDevices(VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices) {
+ ATRACE_CALL();
+
const auto& data = GetData(instance);
VkResult result = data.driver.EnumeratePhysicalDevices(
@@ -1157,6 +1209,8 @@
VkInstance instance,
uint32_t* pPhysicalDeviceGroupCount,
VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+ ATRACE_CALL();
+
VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
@@ -1217,6 +1271,8 @@
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue* pQueue) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
data.driver.GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
@@ -1226,6 +1282,8 @@
void GetDeviceQueue2(VkDevice device,
const VkDeviceQueueInfo2* pQueueInfo,
VkQueue* pQueue) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
data.driver.GetDeviceQueue2(device, pQueueInfo, pQueue);
@@ -1236,6 +1294,8 @@
AllocateCommandBuffers(VkDevice device,
const VkCommandBufferAllocateInfo* pAllocateInfo,
VkCommandBuffer* pCommandBuffers) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
VkResult result = data.driver.AllocateCommandBuffers(device, pAllocateInfo,
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index ba4cf00..af1adcf 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "layers_extensions.h"
#include <alloca.h>
@@ -33,6 +35,7 @@
#include <log/log.h>
#include <nativebridge/native_bridge.h>
#include <nativeloader/native_loader.h>
+#include <utils/Trace.h>
#include <ziparchive/zip_archive.h>
// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
@@ -428,6 +431,8 @@
}
void DiscoverLayersInPathList(const std::string& pathstr) {
+ ATRACE_CALL();
+
std::vector<std::string> paths = android::base::Split(pathstr, ":");
for (const auto& path : paths) {
ForEachFileInPath(path, [&](const std::string& filename) {
@@ -477,6 +482,8 @@
} // anonymous namespace
void DiscoverLayers() {
+ ATRACE_CALL();
+
if (property_get_bool("ro.debuggable", false) &&
prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
DiscoverLayersInPathList(kSystemLayerLibraryDir);
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 3db8a39..73fc7b2 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <algorithm>
#include <grallocusage/GrallocUsageConversion.h>
@@ -21,6 +23,7 @@
#include <ui/BufferQueueDefs.h>
#include <sync/sync.h>
#include <utils/StrongPointer.h>
+#include <utils/Trace.h>
#include <utils/Vector.h>
#include <system/window.h>
#include <android/hardware/graphics/common/1.0/types.h>
@@ -335,15 +338,15 @@
swapchain.surface.window.get(), ti.native_frame_id_,
&desired_present_time, &render_complete_time,
&composition_latch_time,
- NULL, //&first_composition_start_time,
- NULL, //&last_composition_start_time,
- NULL, //&composition_finish_time,
+ nullptr, //&first_composition_start_time,
+ nullptr, //&last_composition_start_time,
+ nullptr, //&composition_finish_time,
// TODO(ianelliott): Maybe ask if this one is
// supported, at startup time (since it may not be
// supported):
&actual_present_time,
- NULL, //&dequeue_ready_time,
- NULL /*&reads_done_time*/);
+ nullptr, //&dequeue_ready_time,
+ nullptr /*&reads_done_time*/);
if (ret != android::NO_ERROR) {
continue;
@@ -416,7 +419,7 @@
case VK_FORMAT_R16G16B16A16_SFLOAT:
native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
break;
- case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+ case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
break;
default:
@@ -486,6 +489,8 @@
const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* allocator,
VkSurfaceKHR* out_surface) {
+ ATRACE_CALL();
+
if (!allocator)
allocator = &GetData(instance).allocator;
void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Surface),
@@ -528,6 +533,8 @@
void DestroySurfaceKHR(VkInstance instance,
VkSurfaceKHR surface_handle,
const VkAllocationCallbacks* allocator) {
+ ATRACE_CALL();
+
Surface* surface = SurfaceFromHandle(surface_handle);
if (!surface)
return;
@@ -548,6 +555,8 @@
uint32_t /*queue_family*/,
VkSurfaceKHR surface_handle,
VkBool32* supported) {
+ ATRACE_CALL();
+
const Surface* surface = SurfaceFromHandle(surface_handle);
if (!surface) {
return VK_ERROR_SURFACE_LOST_KHR;
@@ -569,21 +578,18 @@
switch (native_format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
format_supported = true;
break;
default:
break;
}
- // USAGE_CPU_READ_MASK 0xFUL
- // USAGE_CPU_WRITE_MASK (0xFUL << 4)
- // The currently used bits are as below:
- // USAGE_CPU_READ_RARELY = 2UL
- // USAGE_CPU_READ_OFTEN = 3UL
- // USAGE_CPU_WRITE_RARELY = (2UL << 4)
- // USAGE_CPU_WRITE_OFTEN = (3UL << 4)
- *supported = static_cast<VkBool32>(format_supported ||
- (surface->consumer_usage & 0xFFUL) == 0);
+ *supported = static_cast<VkBool32>(
+ format_supported || (surface->consumer_usage &
+ (AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) == 0);
return VK_SUCCESS;
}
@@ -593,6 +599,8 @@
VkPhysicalDevice /*pdev*/,
VkSurfaceKHR surface,
VkSurfaceCapabilitiesKHR* capabilities) {
+ ATRACE_CALL();
+
int err;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -666,6 +674,8 @@
VkSurfaceKHR surface_handle,
uint32_t* count,
VkSurfaceFormatKHR* formats) {
+ ATRACE_CALL();
+
const InstanceData& instance_data = GetData(pdev);
// TODO(jessehall): Fill out the set of supported formats. Longer term, add
@@ -678,6 +688,8 @@
{VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
{VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+ {VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+ {VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
};
const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
uint32_t total_num_formats = kNumFormats;
@@ -700,6 +712,12 @@
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
{VK_FORMAT_R8G8B8A8_SRGB,
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
+ {VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
+ {VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
+ {VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
};
const uint32_t kNumWideColorFormats =
sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
@@ -734,6 +752,8 @@
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
+ ATRACE_CALL();
+
VkResult result = GetPhysicalDeviceSurfaceCapabilitiesKHR(
physicalDevice, pSurfaceInfo->surface,
&pSurfaceCapabilities->surfaceCapabilities);
@@ -769,6 +789,8 @@
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormat2KHR* pSurfaceFormats) {
+ ATRACE_CALL();
+
if (!pSurfaceFormats) {
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
@@ -800,6 +822,8 @@
VkSurfaceKHR surface,
uint32_t* count,
VkPresentModeKHR* modes) {
+ ATRACE_CALL();
+
int err;
int query_value;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -851,6 +875,8 @@
VkResult GetDeviceGroupPresentCapabilitiesKHR(
VkDevice,
VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
+ ATRACE_CALL();
+
ALOGV_IF(pDeviceGroupPresentCapabilities->sType !=
VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR,
"vkGetDeviceGroupPresentCapabilitiesKHR: invalid "
@@ -873,6 +899,8 @@
VkDevice,
VkSurfaceKHR,
VkDeviceGroupPresentModeFlagsKHR* pModes) {
+ ATRACE_CALL();
+
*pModes = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR;
return VK_SUCCESS;
}
@@ -882,6 +910,8 @@
VkSurfaceKHR surface,
uint32_t* pRectCount,
VkRect2D* pRects) {
+ ATRACE_CALL();
+
if (!pRects) {
*pRectCount = 1;
} else {
@@ -923,6 +953,8 @@
const VkSwapchainCreateInfoKHR* create_info,
const VkAllocationCallbacks* allocator,
VkSwapchainKHR* swapchain_handle) {
+ ATRACE_CALL();
+
int err;
VkResult result = VK_SUCCESS;
@@ -1147,9 +1179,11 @@
int32_t legacy_usage = 0;
if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
uint64_t consumer_usage, producer_usage;
+ ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsage2ANDROID");
result = dispatch.GetSwapchainGrallocUsage2ANDROID(
device, create_info->imageFormat, create_info->imageUsage,
swapchain_image_usage, &consumer_usage, &producer_usage);
+ ATRACE_END();
if (result != VK_SUCCESS) {
ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1157,9 +1191,11 @@
legacy_usage =
android_convertGralloc1To0Usage(producer_usage, consumer_usage);
} else if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsageANDROID");
result = dispatch.GetSwapchainGrallocUsageANDROID(
device, create_info->imageFormat, create_info->imageUsage,
&legacy_usage);
+ ATRACE_END();
if (result != VK_SUCCESS) {
ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1254,8 +1290,10 @@
&image_native_buffer.usage2.producer,
&image_native_buffer.usage2.consumer);
+ ATRACE_BEGIN("dispatch.CreateImage");
result =
dispatch.CreateImage(device, &image_create, nullptr, &img.image);
+ ATRACE_END();
if (result != VK_SUCCESS) {
ALOGD("vkCreateImage w/ native buffer failed: %u", result);
break;
@@ -1279,8 +1317,11 @@
}
}
if (result != VK_SUCCESS) {
- if (img.image)
+ if (img.image) {
+ ATRACE_BEGIN("dispatch.DestroyImage");
dispatch.DestroyImage(device, img.image, nullptr);
+ ATRACE_END();
+ }
}
}
@@ -1299,6 +1340,8 @@
void DestroySwapchainKHR(VkDevice device,
VkSwapchainKHR swapchain_handle,
const VkAllocationCallbacks* allocator) {
+ ATRACE_CALL();
+
const auto& dispatch = GetData(device).driver;
Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
if (!swapchain)
@@ -1324,6 +1367,8 @@
VkSwapchainKHR swapchain_handle,
uint32_t* count,
VkImage* images) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
"getting images for non-active swapchain 0x%" PRIx64
@@ -1352,6 +1397,8 @@
VkSemaphore semaphore,
VkFence vk_fence,
uint32_t* image_index) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
ANativeWindow* window = swapchain.surface.window.get();
VkResult result;
@@ -1432,6 +1479,8 @@
VkResult AcquireNextImage2KHR(VkDevice device,
const VkAcquireNextImageInfoKHR* pAcquireInfo,
uint32_t* pImageIndex) {
+ ATRACE_CALL();
+
// TODO: this should actually be the other way around and this function
// should handle any additional structures that get passed in
return AcquireNextImageKHR(device, pAcquireInfo->swapchain,
@@ -1461,6 +1510,8 @@
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
+ ATRACE_CALL();
+
ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
@@ -1672,6 +1723,8 @@
VkDevice,
VkSwapchainKHR swapchain_handle,
VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
VkResult result = VK_SUCCESS;
@@ -1687,6 +1740,8 @@
VkSwapchainKHR swapchain_handle,
uint32_t* count,
VkPastPresentationTimingGOOGLE* timings) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
ANativeWindow* window = swapchain.surface.window.get();
VkResult result = VK_SUCCESS;
@@ -1711,6 +1766,8 @@
VkResult GetSwapchainStatusKHR(
VkDevice,
VkSwapchainKHR swapchain_handle) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
VkResult result = VK_SUCCESS;
@@ -1728,6 +1785,7 @@
uint32_t swapchainCount,
const VkSwapchainKHR* pSwapchains,
const VkHdrMetadataEXT* pHdrMetadataEXTs) {
+ ATRACE_CALL();
for (uint32_t idx = 0; idx < swapchainCount; idx++) {
Swapchain* swapchain = SwapchainFromHandle(pSwapchains[idx]);
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index 93620f4..78d6694 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -10,7 +10,6 @@
"-Wimplicit-fallthrough",
],
cppflags: [
- "-std=c++11",
"-Wno-sign-compare",
],
export_include_dirs: [
@@ -37,7 +36,6 @@
"-Wimplicit-fallthrough",
],
cppflags: [
- "-std=c++11",
"-Wno-sign-compare",
],
export_include_dirs: [