Merge "Do not read the EMMC ext_csd register from dumpstate"
am: 267b36b7d0
Change-Id: Icceab0bb9f950b9a85e343a3f6d8195456cea8a6
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 eaa84f5..5186ad3 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, {
@@ -890,6 +891,7 @@
setTagsProperty(0);
clearAppProperties();
pokeBinderServices();
+ pokeHalServices();
if (g_tracePdx) {
ServiceUtility::PokeServices();
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 945eae5..40346be 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 e296eaa..c98784e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <libgen.h>
#include <limits.h>
#include <math.h>
@@ -61,9 +62,11 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <android/content/pm/IPackageManagerNative.h>
#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <android/os/IIncidentCompanion.h>
+#include <binder/IServiceManager.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
@@ -237,6 +240,30 @@
return file.tellg() <= 0;
}
+int64_t GetModuleMetadataVersion() {
+ auto binder = defaultServiceManager()->getService(android::String16("package_native"));
+ if (binder == nullptr) {
+ MYLOGE("Failed to retrieve package_native service");
+ return 0L;
+ }
+ auto package_service = android::interface_cast<content::pm::IPackageManagerNative>(binder);
+ std::string package_name;
+ auto status = package_service->getModuleMetadataPackageName(&package_name);
+ if (!status.isOk()) {
+ MYLOGE("Failed to retrieve module metadata package name: %s", status.toString8().c_str());
+ return 0L;
+ }
+ MYLOGD("Module metadata package name: %s", package_name.c_str());
+ int64_t version_code;
+ status = package_service->getVersionCodeForPackage(android::String16(package_name.c_str()),
+ &version_code);
+ if (!status.isOk()) {
+ MYLOGE("Failed to retrieve module metadata version: %s", status.toString8().c_str());
+ return 0L;
+ }
+ return version_code;
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -668,6 +695,10 @@
printf("Bootloader: %s\n", bootloader.c_str());
printf("Radio: %s\n", radio.c_str());
printf("Network: %s\n", network.c_str());
+ int64_t module_metadata_version = android::os::GetModuleMetadataVersion();
+ if (module_metadata_version != 0) {
+ printf("Module Metadata version: %" PRId64 "\n", module_metadata_version);
+ }
printf("Kernel: ");
DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
@@ -1445,6 +1476,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;
}
@@ -2700,8 +2737,8 @@
if (ics != nullptr) {
MYLOGD("Checking user consent via incidentcompanion service\n");
android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
- consent_callback_.get());
+ calling_uid, calling_package, String16(), String16(),
+ 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
} else {
MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
}
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 1016832..75dec37 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",
],
@@ -127,6 +128,10 @@
],
},
},
+
+ // Needs to be wherever installd is as it's execed by
+ // installd.
+ required: [ "migrate_legacy_obb_data.sh" ],
}
// OTA chroot tool
@@ -202,6 +207,7 @@
"otapreopt.cpp",
"otapreopt_utils.cpp",
"utils.cpp",
+ "utils_default.cpp",
"view_compiler.cpp",
],
@@ -245,3 +251,9 @@
"otapreopt_slot",
],
}
+
+// Script to migrate legacy obb data.
+sh_binary {
+ name: "migrate_legacy_obb_data.sh",
+ src: "migrate_legacy_obb_data.sh"
+}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 47fdcc4..dd51898 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";
@@ -597,6 +588,41 @@
}
}
}
+ if (flags & FLAG_STORAGE_EXTERNAL) {
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+ for (const auto& n : mStorageMounts) {
+ auto extPath = n.second;
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ // Clear only cached data from shared storage
+ auto 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
+ auto 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);
+ }
+ }
+ }
+ }
return res;
}
@@ -658,6 +684,30 @@
// Verify if it's ok to do that.
destroy_app_reference_profile(packageName);
}
+ if (flags & FLAG_STORAGE_EXTERNAL) {
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+ for (const auto& n : mStorageMounts) {
+ auto extPath = n.second;
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
+ auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
+ if (delete_dir_contents_and_dir(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_and_dir(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_and_dir(path, true) != 0) {
+ res = error("Failed to delete contents of " + path);
+ }
+ }
+ }
return res;
}
@@ -1610,7 +1660,8 @@
ATRACE_BEGIN("obb");
for (const auto& packageName : packageNames) {
- auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
+ auto obbCodePath = create_data_media_package_path(uuid_, userId,
+ "obb", packageName.c_str());
calculate_tree_size(obbCodePath, &extStats.codeSize);
}
ATRACE_END();
@@ -1942,7 +1993,8 @@
ATRACE_END();
ATRACE_BEGIN("obb");
- auto obbPath = create_data_media_obb_path(uuid_, "");
+ auto obbPath = StringPrintf("%s/Android/obb",
+ create_data_media_path(uuid_, userId).c_str());
calculate_tree_size(obbPath, &obbSize);
ATRACE_END();
}
@@ -2467,7 +2519,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();
@@ -2769,5 +2821,16 @@
return ok();
}
+binder::Status InstalldNativeService::migrateLegacyObbData() {
+ ENFORCE_UID(AID_SYSTEM);
+ // NOTE: The lint warning doesn't apply to the use of system(3) with
+ // absolute parse and no command line arguments.
+ if (system("/system/bin/migrate_legacy_obb_data.sh") != 0) { // NOLINT(cert-env33-c)
+ LOG(ERROR) << "Unable to migrate legacy obb data";
+ }
+
+ return ok();
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 0e91cb2..2b7bf33 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -155,6 +155,8 @@
const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata,
bool* _aidl_return);
+ binder::Status migrateLegacyObbData();
+
private:
std::recursive_mutex mLock;
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 51fe66b..26e9984 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -112,6 +112,19 @@
void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+ void migrateLegacyObbData();
+
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
+ const int FLAG_STORAGE_EXTERNAL = 0x4;
+
+ 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/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh
new file mode 100644
index 0000000..4f8a1ec
--- /dev/null
+++ b/cmds/installd/migrate_legacy_obb_data.sh
@@ -0,0 +1,27 @@
+#!/system/bin/sh
+
+#
+# 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.
+
+if ! test -d /data/media/obb ; then
+ log -p i -t migrate_legacy_obb_data "No legacy obb data to migrate."
+ exit 0
+fi
+
+log -p i -t migrate_legacy_obb_data "Migrating legacy obb data."
+rm -rf /data/media/0/Android/obb
+cp -F -p -R -P -d /data/media/obb /data/media/0/Android
+rm -rf /data/media/obb
+log -p i -t migrate_legacy_obb_data "Done."
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.cpp b/cmds/installd/utils.cpp
index da097db..4eb1df0 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -244,10 +244,6 @@
return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
}
-std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name) {
- return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name);
-}
-
std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
const char* data_type, const char* package_name) {
return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(),
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 91d9e13..6a42026 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -75,7 +75,6 @@
userid_t user, int32_t snapshot_id, const char* package_name);
std::string create_data_media_path(const char* volume_uuid, userid_t userid);
-std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
const char* data_type, const char* package_name);
@@ -125,6 +124,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/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp
new file mode 100644
index 0000000..606477f
--- /dev/null
+++ b/cmds/servicemanager/Access.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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 "Access.h"
+
+#include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
+#include <log/log_safetynet.h>
+#include <selinux/android.h>
+#include <selinux/avc.h>
+
+namespace android {
+
+#ifdef VENDORSERVICEMANAGER
+constexpr bool kIsVendor = true;
+#else
+constexpr bool kIsVendor = false;
+#endif
+
+static std::string getPidcon(pid_t pid) {
+ android_errorWriteLog(0x534e4554, "121035042");
+
+ char* lookup = nullptr;
+ if (getpidcon(pid, &lookup) < 0) {
+ LOG(ERROR) << "SELinux: getpidcon(pid=" << pid << ") failed to retrieve pid context";
+ return "";
+ }
+ std::string result = lookup;
+ freecon(lookup);
+ return result;
+}
+
+static struct selabel_handle* getSehandle() {
+ static struct selabel_handle* gSehandle = nullptr;
+
+ if (gSehandle != nullptr && selinux_status_updated()) {
+ selabel_close(gSehandle);
+ gSehandle = nullptr;
+ }
+
+ if (gSehandle == nullptr) {
+ gSehandle = kIsVendor
+ ? selinux_android_vendor_service_context_handle()
+ : selinux_android_service_context_handle();
+ }
+
+ CHECK(gSehandle != nullptr);
+ return gSehandle;
+}
+
+struct AuditCallbackData {
+ const Access::CallingContext* context;
+ const std::string* tname;
+};
+
+static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
+ const AuditCallbackData* ad = reinterpret_cast<AuditCallbackData*>(data);
+
+ if (!ad) {
+ LOG(ERROR) << "No service manager audit data";
+ return 0;
+ }
+
+ snprintf(buf, len, "pid=%d uid=%d name=%s", ad->context->debugPid, ad->context->uid,
+ ad->tname->c_str());
+ return 0;
+}
+
+Access::Access() {
+ union selinux_callback cb;
+
+ cb.func_audit = auditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
+ cb.func_log = kIsVendor ? selinux_vendor_log_callback : selinux_log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+
+ CHECK(selinux_status_open(true /*fallback*/) >= 0);
+
+ CHECK(getcon(&mThisProcessContext) == 0);
+}
+
+Access::~Access() {
+ freecon(mThisProcessContext);
+}
+
+Access::CallingContext Access::getCallingContext() {
+ IPCThreadState* ipc = IPCThreadState::self();
+
+ const char* callingSid = ipc->getCallingSid();
+ pid_t callingPid = ipc->getCallingPid();
+
+ return CallingContext {
+ .debugPid = callingPid,
+ .uid = ipc->getCallingUid(),
+ .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid),
+ };
+}
+
+bool Access::canFind(const CallingContext& ctx,const std::string& name) {
+ return actionAllowedFromLookup(ctx, name, "find");
+}
+
+bool Access::canAdd(const CallingContext& ctx, const std::string& name) {
+ return actionAllowedFromLookup(ctx, name, "add");
+}
+
+bool Access::canList(const CallingContext& ctx) {
+ return actionAllowed(ctx, mThisProcessContext, "list", "service_manager");
+}
+
+bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm,
+ const std::string& tname) {
+ const char* tclass = "service_manager";
+
+ AuditCallbackData data = {
+ .context = &sctx,
+ .tname = &tname,
+ };
+
+ return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm,
+ reinterpret_cast<void*>(&data));
+}
+
+bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) {
+ char *tctx = nullptr;
+ if (selabel_lookup(getSehandle(), &tctx, name.c_str(), 0) != 0) {
+ LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n";
+ return false;
+ }
+
+ bool allowed = actionAllowed(sctx, tctx, perm, name);
+ freecon(tctx);
+ return allowed;
+}
+
+} // android
diff --git a/cmds/servicemanager/Access.h b/cmds/servicemanager/Access.h
new file mode 100644
index 0000000..77c2cd4
--- /dev/null
+++ b/cmds/servicemanager/Access.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <string>
+#include <sys/types.h>
+
+namespace android {
+
+// singleton
+class Access {
+public:
+ Access();
+ virtual ~Access();
+
+ Access(const Access&) = delete;
+ Access& operator=(const Access&) = delete;
+ Access(Access&&) = delete;
+ Access& operator=(Access&&) = delete;
+
+ struct CallingContext {
+ pid_t debugPid;
+ uid_t uid;
+ std::string sid;
+ };
+
+ virtual CallingContext getCallingContext();
+
+ virtual bool canFind(const CallingContext& ctx, const std::string& name);
+ virtual bool canAdd(const CallingContext& ctx, const std::string& name);
+ virtual bool canList(const CallingContext& ctx);
+
+private:
+ bool actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm,
+ const std::string& tname);
+ bool actionAllowedFromLookup(const CallingContext& sctx, const std::string& name,
+ const char *perm);
+
+ char* mThisProcessContext = nullptr;
+};
+
+};
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 428561b..9cf3c5c 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -1,51 +1,51 @@
cc_defaults {
- name: "servicemanager_flags",
+ name: "servicemanager_defaults",
cflags: [
"-Wall",
"-Wextra",
"-Werror",
],
- product_variables: {
- binder32bit: {
- cflags: ["-DBINDER_IPC_32BIT=1"],
- },
- },
- shared_libs: ["liblog"],
-}
-
-cc_binary {
- name: "bctest",
- defaults: ["servicemanager_flags"],
srcs: [
- "bctest.c",
- "binder.c",
+ "Access.cpp",
+ "ServiceManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder", // also contains servicemanager_interface
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libselinux",
],
}
cc_binary {
name: "servicemanager",
- defaults: ["servicemanager_flags"],
- srcs: [
- "service_manager.c",
- "binder.c",
- ],
- shared_libs: ["libcutils", "libselinux"],
+ defaults: ["servicemanager_defaults"],
init_rc: ["servicemanager.rc"],
+ srcs: ["main.cpp"],
}
cc_binary {
name: "vndservicemanager",
- defaults: ["servicemanager_flags"],
+ defaults: ["servicemanager_defaults"],
+ init_rc: ["vndservicemanager.rc"],
vendor: true,
- srcs: [
- "service_manager.c",
- "binder.c",
- ],
cflags: [
"-DVENDORSERVICEMANAGER=1",
],
- shared_libs: ["libcutils", "libselinux"],
- init_rc: ["vndservicemanager.rc"],
+ srcs: ["main.cpp"],
+}
+
+cc_test {
+ name: "servicemanager_test",
+ test_suites: ["device-tests"],
+ defaults: ["servicemanager_defaults"],
+ srcs: [
+ "test_sm.cpp",
+ ],
+ static_libs: ["libgmock"],
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
new file mode 100644
index 0000000..f35f360
--- /dev/null
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "ServiceManager.h"
+
+#include <android-base/logging.h>
+#include <cutils/android_filesystem_config.h>
+#include <cutils/multiuser.h>
+
+using ::android::binder::Status;
+
+namespace android {
+
+ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {}
+
+Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
+ // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
+ return checkService(name, outBinder);
+}
+
+Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
+ auto ctx = mAccess->getCallingContext();
+
+ auto it = mNameToService.find(name);
+ if (it == mNameToService.end()) {
+ *outBinder = nullptr;
+ return Status::ok();
+ }
+
+ const Service& service = it->second;
+
+ if (!service.allowIsolated) {
+ uid_t appid = multiuser_get_app_id(ctx.uid);
+ bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
+
+ if (isIsolated) {
+ *outBinder = nullptr;
+ return Status::ok();
+ }
+ }
+
+ if (!mAccess->canFind(ctx, name)) {
+ // returns ok and null for legacy reasons
+ *outBinder = nullptr;
+ return Status::ok();
+ }
+
+ *outBinder = service.binder;
+ return Status::ok();
+}
+
+bool isValidServiceName(const std::string& name) {
+ if (name.size() == 0) return false;
+ if (name.size() > 127) return false;
+
+ for (char c : name) {
+ if (c == '_' || c == '-' || c == '.') continue;
+ if (c >= 'a' && c <= 'z') continue;
+ if (c >= 'A' && c <= 'Z') continue;
+ if (c >= '0' && c <= '9') continue;
+ return false;
+ }
+
+ return true;
+}
+
+Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
+ auto ctx = mAccess->getCallingContext();
+
+ // apps cannot add services
+ if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ if (binder == nullptr) {
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (!isValidServiceName(name)) {
+ LOG(ERROR) << "Invalid service name: " << name;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+
+ // implicitly unlinked when the binder is removed
+ if (OK != binder->linkToDeath(this)) {
+ LOG(ERROR) << "Could not linkToDeath when adding " << name;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ mNameToService[name] = Service {
+ .binder = binder,
+ .allowIsolated = allowIsolated,
+ .dumpPriority = dumpPriority,
+ };
+
+ return Status::ok();
+}
+
+Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) {
+ if (!mAccess->canList(mAccess->getCallingContext())) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ size_t toReserve = 0;
+ for (auto const& [name, service] : mNameToService) {
+ (void) name;
+
+ if (service.dumpPriority & dumpPriority) ++toReserve;
+ }
+
+ CHECK(outList->empty());
+
+ outList->reserve(toReserve);
+ for (auto const& [name, service] : mNameToService) {
+ (void) service;
+
+ if (service.dumpPriority & dumpPriority) {
+ outList->push_back(name);
+ }
+ }
+
+ return Status::ok();
+}
+
+void ServiceManager::binderDied(const wp<IBinder>& who) {
+ for (auto it = mNameToService.begin(); it != mNameToService.end();) {
+ if (who == it->second.binder) {
+ it = mNameToService.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace android
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
new file mode 100644
index 0000000..78e4805
--- /dev/null
+++ b/cmds/servicemanager/ServiceManager.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <android/os/BnServiceManager.h>
+
+#include "Access.h"
+
+namespace android {
+
+class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
+public:
+ ServiceManager(std::unique_ptr<Access>&& access);
+
+ binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;
+ binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override;
+ binder::Status addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) override;
+ binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override;
+
+ void binderDied(const wp<IBinder>& who) override;
+
+private:
+ struct Service {
+ sp<IBinder> binder;
+ bool allowIsolated;
+ int32_t dumpPriority;
+ };
+
+ std::map<std::string, Service> mNameToService;
+ std::unique_ptr<Access> mAccess;
+};
+
+} // namespace android
diff --git a/cmds/servicemanager/TEST_MAPPING b/cmds/servicemanager/TEST_MAPPING
new file mode 100644
index 0000000..739740a
--- /dev/null
+++ b/cmds/servicemanager/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "servicemanager_test"
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/native/libs/binder"
+ }
+ ]
+}
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
deleted file mode 100644
index 354df67..0000000
--- a/cmds/servicemanager/bctest.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* Copyright 2008 The Android Open Source Project
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include "binder.h"
-
-uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
-{
- uint32_t handle;
- unsigned iodata[512/4];
- struct binder_io msg, reply;
-
- bio_init(&msg, iodata, sizeof(iodata), 4);
- bio_put_uint32(&msg, 0); // strict mode header
- bio_put_string16_x(&msg, SVC_MGR_NAME);
- bio_put_string16_x(&msg, name);
-
- if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
- return 0;
-
- handle = bio_get_ref(&reply);
-
- if (handle)
- binder_acquire(bs, handle);
-
- binder_done(bs, &msg, &reply);
-
- return handle;
-}
-
-int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
-{
- int status;
- unsigned iodata[512/4];
- struct binder_io msg, reply;
-
- bio_init(&msg, iodata, sizeof(iodata), 4);
- bio_put_uint32(&msg, 0); // strict mode header
- bio_put_string16_x(&msg, SVC_MGR_NAME);
- bio_put_string16_x(&msg, name);
- bio_put_obj(&msg, ptr);
-
- if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
- return -1;
-
- status = bio_get_uint32(&reply);
-
- binder_done(bs, &msg, &reply);
-
- return status;
-}
-
-unsigned token;
-
-int main(int argc, char **argv)
-{
- struct binder_state *bs;
- uint32_t svcmgr = BINDER_SERVICE_MANAGER;
- uint32_t handle;
-
- bs = binder_open("/dev/binder", 128*1024);
- if (!bs) {
- fprintf(stderr, "failed to open binder driver\n");
- return -1;
- }
-
- argc--;
- argv++;
- while (argc > 0) {
- if (!strcmp(argv[0],"alt")) {
- handle = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr");
- if (!handle) {
- fprintf(stderr,"cannot find alt_svc_mgr\n");
- return -1;
- }
- svcmgr = handle;
- fprintf(stderr,"svcmgr is via %x\n", handle);
- } else if (!strcmp(argv[0],"lookup")) {
- if (argc < 2) {
- fprintf(stderr,"argument required\n");
- return -1;
- }
- handle = svcmgr_lookup(bs, svcmgr, argv[1]);
- fprintf(stderr,"lookup(%s) = %x\n", argv[1], handle);
- argc--;
- argv++;
- } else if (!strcmp(argv[0],"publish")) {
- if (argc < 2) {
- fprintf(stderr,"argument required\n");
- return -1;
- }
- svcmgr_publish(bs, svcmgr, argv[1], &token);
- argc--;
- argv++;
- } else {
- fprintf(stderr,"unknown command %s\n", argv[0]);
- return -1;
- }
- argc--;
- argv++;
- }
- return 0;
-}
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
deleted file mode 100644
index fade8cf..0000000
--- a/cmds/servicemanager/binder.c
+++ /dev/null
@@ -1,656 +0,0 @@
-/* Copyright 2008 The Android Open Source Project
- */
-
-#define LOG_TAG "Binder"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "binder.h"
-
-#define MAX_BIO_SIZE (1 << 30)
-
-#define TRACE 0
-
-void bio_init_from_txn(struct binder_io *io, struct binder_transaction_data *txn);
-
-#if TRACE
-void hexdump(void *_data, size_t len)
-{
- unsigned char *data = _data;
- size_t count;
-
- for (count = 0; count < len; count++) {
- if ((count & 15) == 0)
- fprintf(stderr,"%04zu:", count);
- fprintf(stderr," %02x %c", *data,
- (*data < 32) || (*data > 126) ? '.' : *data);
- data++;
- if ((count & 15) == 15)
- fprintf(stderr,"\n");
- }
- if ((count & 15) != 0)
- fprintf(stderr,"\n");
-}
-
-void binder_dump_txn(struct binder_transaction_data *txn)
-{
- struct flat_binder_object *obj;
- binder_size_t *offs = (binder_size_t *)(uintptr_t)txn->data.ptr.offsets;
- size_t count = txn->offsets_size / sizeof(binder_size_t);
-
- fprintf(stderr," target %016"PRIx64" cookie %016"PRIx64" code %08x flags %08x\n",
- (uint64_t)txn->target.ptr, (uint64_t)txn->cookie, txn->code, txn->flags);
- fprintf(stderr," pid %8d uid %8d data %"PRIu64" offs %"PRIu64"\n",
- txn->sender_pid, txn->sender_euid, (uint64_t)txn->data_size, (uint64_t)txn->offsets_size);
- hexdump((void *)(uintptr_t)txn->data.ptr.buffer, txn->data_size);
- while (count--) {
- obj = (struct flat_binder_object *) (((char*)(uintptr_t)txn->data.ptr.buffer) + *offs++);
- fprintf(stderr," - type %08x flags %08x ptr %016"PRIx64" cookie %016"PRIx64"\n",
- obj->type, obj->flags, (uint64_t)obj->binder, (uint64_t)obj->cookie);
- }
-}
-
-#define NAME(n) case n: return #n
-const char *cmd_name(uint32_t cmd)
-{
- switch(cmd) {
- NAME(BR_NOOP);
- NAME(BR_TRANSACTION_COMPLETE);
- NAME(BR_INCREFS);
- NAME(BR_ACQUIRE);
- NAME(BR_RELEASE);
- NAME(BR_DECREFS);
- NAME(BR_TRANSACTION);
- NAME(BR_REPLY);
- NAME(BR_FAILED_REPLY);
- NAME(BR_DEAD_REPLY);
- NAME(BR_DEAD_BINDER);
- default: return "???";
- }
-}
-#else
-#define hexdump(a,b) do{} while (0)
-#define binder_dump_txn(txn) do{} while (0)
-#endif
-
-#define BIO_F_SHARED 0x01 /* needs to be buffer freed */
-#define BIO_F_OVERFLOW 0x02 /* ran out of space */
-#define BIO_F_IOERROR 0x04
-#define BIO_F_MALLOCED 0x08 /* needs to be free()'d */
-
-struct binder_state
-{
- int fd;
- void *mapped;
- size_t mapsize;
-};
-
-struct binder_state *binder_open(const char* driver, size_t mapsize)
-{
- struct binder_state *bs;
- struct binder_version vers;
-
- bs = malloc(sizeof(*bs));
- if (!bs) {
- errno = ENOMEM;
- return NULL;
- }
-
- bs->fd = open(driver, O_RDWR | O_CLOEXEC);
- if (bs->fd < 0) {
- fprintf(stderr,"binder: cannot open %s (%s)\n",
- driver, strerror(errno));
- goto fail_open;
- }
-
- if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
- (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
- fprintf(stderr,
- "binder: kernel driver version (%d) differs from user space version (%d)\n",
- vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
- goto fail_open;
- }
-
- bs->mapsize = mapsize;
- bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
- if (bs->mapped == MAP_FAILED) {
- fprintf(stderr,"binder: cannot map device (%s)\n",
- strerror(errno));
- goto fail_map;
- }
-
- return bs;
-
-fail_map:
- close(bs->fd);
-fail_open:
- free(bs);
- return NULL;
-}
-
-void binder_close(struct binder_state *bs)
-{
- munmap(bs->mapped, bs->mapsize);
- close(bs->fd);
- free(bs);
-}
-
-int binder_become_context_manager(struct binder_state *bs)
-{
- return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
-}
-
-int binder_write(struct binder_state *bs, void *data, size_t len)
-{
- struct binder_write_read bwr;
- int res;
-
- bwr.write_size = len;
- bwr.write_consumed = 0;
- bwr.write_buffer = (uintptr_t) data;
- bwr.read_size = 0;
- bwr.read_consumed = 0;
- bwr.read_buffer = 0;
- res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
- if (res < 0) {
- fprintf(stderr,"binder_write: ioctl failed (%s)\n",
- strerror(errno));
- }
- return res;
-}
-
-void binder_free_buffer(struct binder_state *bs,
- binder_uintptr_t buffer_to_free)
-{
- struct {
- uint32_t cmd_free;
- binder_uintptr_t buffer;
- } __attribute__((packed)) data;
- data.cmd_free = BC_FREE_BUFFER;
- data.buffer = buffer_to_free;
- binder_write(bs, &data, sizeof(data));
-}
-
-void binder_send_reply(struct binder_state *bs,
- struct binder_io *reply,
- binder_uintptr_t buffer_to_free,
- int status)
-{
- struct {
- uint32_t cmd_free;
- binder_uintptr_t buffer;
- uint32_t cmd_reply;
- struct binder_transaction_data txn;
- } __attribute__((packed)) data;
-
- data.cmd_free = BC_FREE_BUFFER;
- data.buffer = buffer_to_free;
- data.cmd_reply = BC_REPLY;
- data.txn.target.ptr = 0;
- data.txn.cookie = 0;
- data.txn.code = 0;
- if (status) {
- data.txn.flags = TF_STATUS_CODE;
- data.txn.data_size = sizeof(int);
- data.txn.offsets_size = 0;
- data.txn.data.ptr.buffer = (uintptr_t)&status;
- data.txn.data.ptr.offsets = 0;
- } else {
- data.txn.flags = 0;
- data.txn.data_size = reply->data - reply->data0;
- data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
- data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
- data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
- }
- binder_write(bs, &data, sizeof(data));
-}
-
-int binder_parse(struct binder_state *bs, struct binder_io *bio,
- uintptr_t ptr, size_t size, binder_handler func)
-{
- int r = 1;
- uintptr_t end = ptr + (uintptr_t) size;
-
- while (ptr < end) {
- uint32_t cmd = *(uint32_t *) ptr;
- ptr += sizeof(uint32_t);
-#if TRACE
- fprintf(stderr,"%s:\n", cmd_name(cmd));
-#endif
- switch(cmd) {
- case BR_NOOP:
- break;
- case BR_TRANSACTION_COMPLETE:
- break;
- case BR_INCREFS:
- case BR_ACQUIRE:
- case BR_RELEASE:
- case BR_DECREFS:
-#if TRACE
- fprintf(stderr," %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *)));
-#endif
- ptr += sizeof(struct binder_ptr_cookie);
- break;
- 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;
- }
- binder_dump_txn(txn);
- if (func) {
- unsigned rdata[256/4];
- struct binder_io msg;
- struct binder_io reply;
- 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);
- } else {
- binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
- }
- }
- ptr += sizeof(*txn);
- break;
- }
- case BR_REPLY: {
- struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
- if ((end - ptr) < sizeof(*txn)) {
- ALOGE("parse: reply too small!\n");
- return -1;
- }
- binder_dump_txn(txn);
- if (bio) {
- bio_init_from_txn(bio, txn);
- bio = 0;
- } else {
- /* todo FREE BUFFER */
- }
- ptr += sizeof(*txn);
- r = 0;
- break;
- }
- case BR_DEAD_BINDER: {
- struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;
- ptr += sizeof(binder_uintptr_t);
- death->func(bs, death->ptr);
- break;
- }
- case BR_FAILED_REPLY:
- r = -1;
- break;
- case BR_DEAD_REPLY:
- r = -1;
- break;
- default:
- ALOGE("parse: OOPS %d\n", cmd);
- return -1;
- }
- }
-
- return r;
-}
-
-void binder_acquire(struct binder_state *bs, uint32_t target)
-{
- uint32_t cmd[2];
- cmd[0] = BC_ACQUIRE;
- cmd[1] = target;
- binder_write(bs, cmd, sizeof(cmd));
-}
-
-void binder_release(struct binder_state *bs, uint32_t target)
-{
- uint32_t cmd[2];
- cmd[0] = BC_RELEASE;
- cmd[1] = target;
- binder_write(bs, cmd, sizeof(cmd));
-}
-
-void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death)
-{
- struct {
- uint32_t cmd;
- struct binder_handle_cookie payload;
- } __attribute__((packed)) data;
-
- data.cmd = BC_REQUEST_DEATH_NOTIFICATION;
- data.payload.handle = target;
- data.payload.cookie = (uintptr_t) death;
- binder_write(bs, &data, sizeof(data));
-}
-
-int binder_call(struct binder_state *bs,
- struct binder_io *msg, struct binder_io *reply,
- uint32_t target, uint32_t code)
-{
- int res;
- struct binder_write_read bwr;
- struct {
- uint32_t cmd;
- struct binder_transaction_data txn;
- } __attribute__((packed)) writebuf;
- unsigned readbuf[32];
-
- if (msg->flags & BIO_F_OVERFLOW) {
- fprintf(stderr,"binder: txn buffer overflow\n");
- goto fail;
- }
-
- writebuf.cmd = BC_TRANSACTION;
- writebuf.txn.target.handle = target;
- writebuf.txn.code = code;
- writebuf.txn.flags = 0;
- writebuf.txn.data_size = msg->data - msg->data0;
- writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
- writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
- writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
-
- bwr.write_size = sizeof(writebuf);
- bwr.write_consumed = 0;
- bwr.write_buffer = (uintptr_t) &writebuf;
-
- hexdump(msg->data0, msg->data - msg->data0);
- for (;;) {
- bwr.read_size = sizeof(readbuf);
- bwr.read_consumed = 0;
- bwr.read_buffer = (uintptr_t) readbuf;
-
- res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
-
- if (res < 0) {
- fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
- goto fail;
- }
-
- res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0);
- if (res == 0) return 0;
- if (res < 0) goto fail;
- }
-
-fail:
- memset(reply, 0, sizeof(*reply));
- reply->flags |= BIO_F_IOERROR;
- return -1;
-}
-
-void binder_loop(struct binder_state *bs, binder_handler func)
-{
- int res;
- struct binder_write_read bwr;
- uint32_t readbuf[32];
-
- bwr.write_size = 0;
- bwr.write_consumed = 0;
- bwr.write_buffer = 0;
-
- readbuf[0] = BC_ENTER_LOOPER;
- binder_write(bs, readbuf, sizeof(uint32_t));
-
- for (;;) {
- bwr.read_size = sizeof(readbuf);
- bwr.read_consumed = 0;
- bwr.read_buffer = (uintptr_t) readbuf;
-
- res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
-
- if (res < 0) {
- ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
- break;
- }
-
- res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
- if (res == 0) {
- ALOGE("binder_loop: unexpected reply?!\n");
- break;
- }
- if (res < 0) {
- ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
- break;
- }
- }
-}
-
-void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
-{
- bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer;
- bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets;
- bio->data_avail = txn->data_size;
- bio->offs_avail = txn->offsets_size / sizeof(size_t);
- bio->flags = BIO_F_SHARED;
-}
-
-void bio_init(struct binder_io *bio, void *data,
- size_t maxdata, size_t maxoffs)
-{
- size_t n = maxoffs * sizeof(size_t);
-
- if (n > maxdata) {
- bio->flags = BIO_F_OVERFLOW;
- bio->data_avail = 0;
- bio->offs_avail = 0;
- return;
- }
-
- bio->data = bio->data0 = (char *) data + n;
- bio->offs = bio->offs0 = data;
- bio->data_avail = maxdata - n;
- bio->offs_avail = maxoffs;
- bio->flags = 0;
-}
-
-static void *bio_alloc(struct binder_io *bio, size_t size)
-{
- size = (size + 3) & (~3);
- if (size > bio->data_avail) {
- bio->flags |= BIO_F_OVERFLOW;
- return NULL;
- } else {
- void *ptr = bio->data;
- bio->data += size;
- bio->data_avail -= size;
- return ptr;
- }
-}
-
-void binder_done(struct binder_state *bs,
- __unused struct binder_io *msg,
- struct binder_io *reply)
-{
- struct {
- uint32_t cmd;
- uintptr_t buffer;
- } __attribute__((packed)) data;
-
- if (reply->flags & BIO_F_SHARED) {
- data.cmd = BC_FREE_BUFFER;
- data.buffer = (uintptr_t) reply->data0;
- binder_write(bs, &data, sizeof(data));
- reply->flags = 0;
- }
-}
-
-static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio)
-{
- struct flat_binder_object *obj;
-
- obj = bio_alloc(bio, sizeof(*obj));
-
- if (obj && bio->offs_avail) {
- bio->offs_avail--;
- *bio->offs++ = ((char*) obj) - ((char*) bio->data0);
- return obj;
- }
-
- bio->flags |= BIO_F_OVERFLOW;
- return NULL;
-}
-
-void bio_put_uint32(struct binder_io *bio, uint32_t n)
-{
- uint32_t *ptr = bio_alloc(bio, sizeof(n));
- if (ptr)
- *ptr = n;
-}
-
-void bio_put_obj(struct binder_io *bio, void *ptr)
-{
- struct flat_binder_object *obj;
-
- obj = bio_alloc_obj(bio);
- if (!obj)
- return;
-
- obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj->hdr.type = BINDER_TYPE_BINDER;
- obj->binder = (uintptr_t)ptr;
- obj->cookie = 0;
-}
-
-void bio_put_ref(struct binder_io *bio, uint32_t handle)
-{
- struct flat_binder_object *obj;
-
- if (handle)
- obj = bio_alloc_obj(bio);
- else
- obj = bio_alloc(bio, sizeof(*obj));
-
- if (!obj)
- return;
-
- obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj->hdr.type = BINDER_TYPE_HANDLE;
- obj->handle = handle;
- obj->cookie = 0;
-}
-
-void bio_put_string16(struct binder_io *bio, const uint16_t *str)
-{
- size_t len;
- uint16_t *ptr;
-
- if (!str) {
- bio_put_uint32(bio, 0xffffffff);
- return;
- }
-
- len = 0;
- while (str[len]) len++;
-
- if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
- bio_put_uint32(bio, 0xffffffff);
- return;
- }
-
- /* Note: The payload will carry 32bit size instead of size_t */
- bio_put_uint32(bio, (uint32_t) len);
- len = (len + 1) * sizeof(uint16_t);
- ptr = bio_alloc(bio, len);
- if (ptr)
- memcpy(ptr, str, len);
-}
-
-void bio_put_string16_x(struct binder_io *bio, const char *_str)
-{
- unsigned char *str = (unsigned char*) _str;
- size_t len;
- uint16_t *ptr;
-
- if (!str) {
- bio_put_uint32(bio, 0xffffffff);
- return;
- }
-
- len = strlen(_str);
-
- if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
- bio_put_uint32(bio, 0xffffffff);
- return;
- }
-
- /* Note: The payload will carry 32bit size instead of size_t */
- bio_put_uint32(bio, len);
- ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t));
- if (!ptr)
- return;
-
- while (*str)
- *ptr++ = *str++;
- *ptr++ = 0;
-}
-
-static void *bio_get(struct binder_io *bio, size_t size)
-{
- size = (size + 3) & (~3);
-
- if (bio->data_avail < size){
- bio->data_avail = 0;
- bio->flags |= BIO_F_OVERFLOW;
- return NULL;
- } else {
- void *ptr = bio->data;
- bio->data += size;
- bio->data_avail -= size;
- return ptr;
- }
-}
-
-uint32_t bio_get_uint32(struct binder_io *bio)
-{
- uint32_t *ptr = bio_get(bio, sizeof(*ptr));
- return ptr ? *ptr : 0;
-}
-
-uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz)
-{
- size_t len;
-
- /* Note: The payload will carry 32bit size instead of size_t */
- len = (size_t) bio_get_uint32(bio);
- if (sz)
- *sz = len;
- return bio_get(bio, (len + 1) * sizeof(uint16_t));
-}
-
-static struct flat_binder_object *_bio_get_obj(struct binder_io *bio)
-{
- size_t n;
- size_t off = bio->data - bio->data0;
-
- /* TODO: be smarter about this? */
- for (n = 0; n < bio->offs_avail; n++) {
- if (bio->offs[n] == off)
- return bio_get(bio, sizeof(struct flat_binder_object));
- }
-
- bio->data_avail = 0;
- bio->flags |= BIO_F_OVERFLOW;
- return NULL;
-}
-
-uint32_t bio_get_ref(struct binder_io *bio)
-{
- struct flat_binder_object *obj;
-
- obj = _bio_get_obj(bio);
- if (!obj)
- return 0;
-
- if (obj->hdr.type == BINDER_TYPE_HANDLE)
- return obj->handle;
-
- return 0;
-}
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
deleted file mode 100644
index c95b33f..0000000
--- a/cmds/servicemanager/binder.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* Copyright 2008 The Android Open Source Project
- */
-
-#ifndef _BINDER_H_
-#define _BINDER_H_
-
-#include <sys/ioctl.h>
-#include <linux/android/binder.h>
-
-struct binder_state;
-
-struct binder_io
-{
- char *data; /* pointer to read/write from */
- binder_size_t *offs; /* array of offsets */
- size_t data_avail; /* bytes available in data buffer */
- size_t offs_avail; /* entries available in offsets array */
-
- char *data0; /* start of data buffer */
- binder_size_t *offs0; /* start of offsets buffer */
- uint32_t flags;
- uint32_t unused;
-};
-
-struct binder_death {
- void (*func)(struct binder_state *bs, void *ptr);
- void *ptr;
-};
-
-/* the one magic handle */
-#define BINDER_SERVICE_MANAGER 0U
-
-#define SVC_MGR_NAME "android.os.IServiceManager"
-
-enum {
- /* Must match definitions in IBinder.h and IServiceManager.h */
- PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
- SVC_MGR_GET_SERVICE = 1,
- SVC_MGR_CHECK_SERVICE,
- SVC_MGR_ADD_SERVICE,
- SVC_MGR_LIST_SERVICES,
-};
-
-typedef int (*binder_handler)(struct binder_state *bs,
- struct binder_transaction_data *txn,
- struct binder_io *msg,
- struct binder_io *reply);
-
-struct binder_state *binder_open(const char* driver, size_t mapsize);
-void binder_close(struct binder_state *bs);
-
-/* initiate a blocking binder call
- * - returns zero on success
- */
-int binder_call(struct binder_state *bs,
- struct binder_io *msg, struct binder_io *reply,
- uint32_t target, uint32_t code);
-
-/* release any state associate with the binder_io
- * - call once any necessary data has been extracted from the
- * binder_io after binder_call() returns
- * - can safely be called even if binder_call() fails
- */
-void binder_done(struct binder_state *bs,
- struct binder_io *msg, struct binder_io *reply);
-
-/* manipulate strong references */
-void binder_acquire(struct binder_state *bs, uint32_t target);
-void binder_release(struct binder_state *bs, uint32_t target);
-
-void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death);
-
-void binder_loop(struct binder_state *bs, binder_handler func);
-
-int binder_become_context_manager(struct binder_state *bs);
-
-/* allocate a binder_io, providing a stack-allocated working
- * buffer, size of the working buffer, and how many object
- * offset entries to reserve from the buffer
- */
-void bio_init(struct binder_io *bio, void *data,
- size_t maxdata, size_t maxobjects);
-
-void bio_put_obj(struct binder_io *bio, void *ptr);
-void bio_put_ref(struct binder_io *bio, uint32_t handle);
-void bio_put_uint32(struct binder_io *bio, uint32_t n);
-void bio_put_string16(struct binder_io *bio, const uint16_t *str);
-void bio_put_string16_x(struct binder_io *bio, const char *_str);
-
-uint32_t bio_get_uint32(struct binder_io *bio);
-uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz);
-uint32_t bio_get_ref(struct binder_io *bio);
-
-#endif
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
new file mode 100644
index 0000000..9f6193b
--- /dev/null
+++ b/cmds/servicemanager/main.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 <android-base/logging.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+#include "Access.h"
+#include "ServiceManager.h"
+
+using ::android::sp;
+using ::android::ProcessState;
+using ::android::IPCThreadState;
+using ::android::ServiceManager;
+using ::android::Access;
+
+int main(int argc, char** argv) {
+ if (argc > 2) {
+ LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
+ }
+
+ const char* driver = argc == 2 ? argv[1] : "/dev/binder";
+
+ android::base::InitLogging(nullptr, &android::base::KernelLogger);
+
+ sp<ProcessState> ps = ProcessState::initWithDriver(driver);
+ ps->setThreadPoolMaxThreadCount(0);
+ ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
+
+ sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
+ IPCThreadState::self()->setTheContextObject(manager);
+ ps->becomeContextManager(nullptr, nullptr);
+
+ IPCThreadState::self()->joinThreadPool();
+
+ // should not be reached
+ return EXIT_FAILURE;
+}
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
deleted file mode 100644
index d776682..0000000
--- a/cmds/servicemanager/service_manager.c
+++ /dev/null
@@ -1,434 +0,0 @@
-/* Copyright 2008 The Android Open Source Project
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cutils/android_filesystem_config.h>
-#include <cutils/multiuser.h>
-
-#include <selinux/android.h>
-#include <selinux/avc.h>
-
-#include "binder.h"
-
-#ifdef VENDORSERVICEMANAGER
-#define LOG_TAG "VendorServiceManager"
-#else
-#define LOG_TAG "ServiceManager"
-#endif
-#include <log/log.h>
-
-struct audit_data {
- pid_t pid;
- uid_t uid;
- const char *name;
-};
-
-const char *str8(const uint16_t *x, size_t x_len)
-{
- static char buf[128];
- size_t max = 127;
- char *p = buf;
-
- if (x_len < max) {
- max = x_len;
- }
-
- if (x) {
- while ((max > 0) && (*x != '\0')) {
- *p++ = *x++;
- max--;
- }
- }
- *p++ = 0;
- return buf;
-}
-
-int str16eq(const uint16_t *a, const char *b)
-{
- while (*a && *b)
- if (*a++ != *b++) return 0;
- if (*a || *b)
- return 0;
- return 1;
-}
-
-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)
-{
- char *sctx = NULL;
- const char *class = "service_manager";
- bool allowed;
- struct audit_data ad;
-
- if (getpidcon(spid, &sctx) < 0) {
- ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid);
- return false;
- }
-
- ad.pid = spid;
- ad.uid = uid;
- ad.name = name;
-
- int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad);
- allowed = (result == 0);
-
- freecon(sctx);
- return allowed;
-}
-
-static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
-{
- return check_mac_perms(spid, 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)
-{
- bool allowed;
- char *tctx = NULL;
-
- if (!sehandle) {
- ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
- abort();
- }
-
- if (selabel_lookup(sehandle, &tctx, name, 0) != 0) {
- ALOGE("SELinux: No match for %s in service_contexts.\n", name);
- return false;
- }
-
- allowed = check_mac_perms(spid, 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)
-{
- const char *perm = "add";
-
- if (multiuser_get_app_id(uid) >= AID_APP) {
- return 0; /* Don't allow apps to register services */
- }
-
- return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
-}
-
-static int svc_can_list(pid_t spid, uid_t uid)
-{
- const char *perm = "list";
- return check_mac_perms_from_getcon(spid, uid, perm) ? 1 : 0;
-}
-
-static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
-{
- const char *perm = "find";
- return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
-}
-
-struct svcinfo
-{
- struct svcinfo *next;
- uint32_t handle;
- struct binder_death death;
- int allow_isolated;
- uint32_t dumpsys_priority;
- size_t len;
- uint16_t name[0];
-};
-
-struct svcinfo *svclist = NULL;
-
-struct svcinfo *find_svc(const uint16_t *s16, size_t len)
-{
- struct svcinfo *si;
-
- for (si = svclist; si; si = si->next) {
- if ((len == si->len) &&
- !memcmp(s16, si->name, len * sizeof(uint16_t))) {
- return si;
- }
- }
- return NULL;
-}
-
-void svcinfo_death(struct binder_state *bs, void *ptr)
-{
- struct svcinfo *si = (struct svcinfo* ) ptr;
-
- ALOGI("service '%s' died\n", str8(si->name, si->len));
- if (si->handle) {
- binder_release(bs, si->handle);
- si->handle = 0;
- }
-}
-
-uint16_t svcmgr_id[] = {
- 'a','n','d','r','o','i','d','.','o','s','.',
- 'I','S','e','r','v','i','c','e','M','a','n','a','g','e','r'
-};
-
-
-uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
-{
- struct svcinfo *si = find_svc(s, len);
-
- if (!si || !si->handle) {
- return 0;
- }
-
- if (!si->allow_isolated) {
- // If this service doesn't allow access from isolated processes,
- // then check the uid to see if it is isolated.
- uid_t appid = uid % AID_USER;
- if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
- return 0;
- }
- }
-
- if (!svc_can_find(s, len, spid, uid)) {
- return 0;
- }
-
- return si->handle;
-}
-
-int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle,
- uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) {
- struct svcinfo *si;
-
- //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
- // allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
-
- if (!handle || (len == 0) || (len > 127))
- return -1;
-
- if (!svc_can_register(s, len, spid, uid)) {
- ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
- str8(s, len), handle, uid);
- return -1;
- }
-
- si = find_svc(s, len);
- if (si) {
- if (si->handle) {
- ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
- str8(s, len), handle, uid);
- svcinfo_death(bs, si);
- }
- si->handle = handle;
- } else {
- si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
- if (!si) {
- ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
- str8(s, len), handle, uid);
- return -1;
- }
- si->handle = handle;
- si->len = len;
- memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
- si->name[len] = '\0';
- si->death.func = (void*) svcinfo_death;
- si->death.ptr = si;
- si->allow_isolated = allow_isolated;
- si->dumpsys_priority = dumpsys_priority;
- si->next = svclist;
- svclist = si;
- }
-
- binder_acquire(bs, handle);
- binder_link_to_death(bs, handle, &si->death);
- return 0;
-}
-
-int svcmgr_handler(struct binder_state *bs,
- struct binder_transaction_data *txn,
- struct binder_io *msg,
- struct binder_io *reply)
-{
- struct svcinfo *si;
- uint16_t *s;
- size_t len;
- uint32_t handle;
- uint32_t strict_policy;
- int allow_isolated;
- uint32_t dumpsys_priority;
-
- //ALOGI("target=%p code=%d pid=%d uid=%d\n",
- // (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);
-
- if (txn->target.ptr != BINDER_SERVICE_MANAGER)
- return -1;
-
- if (txn->code == PING_TRANSACTION)
- return 0;
-
- // Equivalent to Parcel::enforceInterface(), reading the RPC
- // header with the strict mode policy mask and the interface name.
- // 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);
- s = bio_get_string16(msg, &len);
- if (s == NULL) {
- return -1;
- }
-
- if ((len != (sizeof(svcmgr_id) / 2)) ||
- memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
- fprintf(stderr,"invalid id %s\n", str8(s, len));
- return -1;
- }
-
- if (sehandle && selinux_status_updated() > 0) {
-#ifdef VENDORSERVICEMANAGER
- struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
-#else
- struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
-#endif
- if (tmp_sehandle) {
- selabel_close(sehandle);
- sehandle = tmp_sehandle;
- }
- }
-
- switch(txn->code) {
- case SVC_MGR_GET_SERVICE:
- case SVC_MGR_CHECK_SERVICE:
- s = bio_get_string16(msg, &len);
- if (s == NULL) {
- return -1;
- }
- handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
- if (!handle)
- break;
- bio_put_ref(reply, handle);
- return 0;
-
- case SVC_MGR_ADD_SERVICE:
- s = bio_get_string16(msg, &len);
- if (s == NULL) {
- return -1;
- }
- handle = bio_get_ref(msg);
- 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))
- return -1;
- break;
-
- case SVC_MGR_LIST_SERVICES: {
- uint32_t n = bio_get_uint32(msg);
- uint32_t req_dumpsys_priority = bio_get_uint32(msg);
-
- if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
- ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
- txn->sender_euid);
- return -1;
- }
- si = svclist;
- // walk through the list of services n times skipping services that
- // do not support the requested priority
- while (si) {
- if (si->dumpsys_priority & req_dumpsys_priority) {
- if (n == 0) break;
- n--;
- }
- si = si->next;
- }
- if (si) {
- bio_put_string16(reply, si->name);
- return 0;
- }
- return -1;
- }
- default:
- ALOGE("unknown code %d\n", txn->code);
- return -1;
- }
-
- bio_put_uint32(reply, 0);
- return 0;
-}
-
-
-static int audit_callback(void *data, __unused security_class_t cls, char *buf, size_t len)
-{
- struct audit_data *ad = (struct audit_data *)data;
-
- if (!ad || !ad->name) {
- ALOGE("No service manager audit data");
- return 0;
- }
-
- snprintf(buf, len, "service=%s pid=%d uid=%d", ad->name, ad->pid, ad->uid);
- return 0;
-}
-
-int main(int argc, char** argv)
-{
- struct binder_state *bs;
- union selinux_callback cb;
- char *driver;
-
- if (argc > 1) {
- driver = argv[1];
- } else {
- driver = "/dev/binder";
- }
-
- bs = binder_open(driver, 128*1024);
- if (!bs) {
-#ifdef VENDORSERVICEMANAGER
- ALOGW("failed to open binder driver %s\n", driver);
- while (true) {
- sleep(UINT_MAX);
- }
-#else
- ALOGE("failed to open binder driver %s\n", driver);
-#endif
- return -1;
- }
-
- if (binder_become_context_manager(bs)) {
- ALOGE("cannot become context manager (%s)\n", strerror(errno));
- return -1;
- }
-
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-#ifdef VENDORSERVICEMANAGER
- cb.func_log = selinux_vendor_log_callback;
-#else
- cb.func_log = selinux_log_callback;
-#endif
- selinux_set_callback(SELINUX_CB_LOG, cb);
-
-#ifdef VENDORSERVICEMANAGER
- sehandle = selinux_android_vendor_service_context_handle();
-#else
- sehandle = selinux_android_service_context_handle();
-#endif
- selinux_status_open(true);
-
- if (sehandle == NULL) {
- ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
- abort();
- }
-
- if (getcon(&service_manager_context) != 0) {
- ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
- abort();
- }
-
-
- binder_loop(bs, svcmgr_handler);
-
- return 0;
-}
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/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
new file mode 100644
index 0000000..91485e4
--- /dev/null
+++ b/cmds/servicemanager/test_sm.cpp
@@ -0,0 +1,267 @@
+/*
+ * 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 <binder/ProcessState.h>
+#include <cutils/android_filesystem_config.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "Access.h"
+#include "ServiceManager.h"
+
+using android::sp;
+using android::Access;
+using android::IBinder;
+using android::ServiceManager;
+using android::os::IServiceManager;
+using testing::_;
+using testing::ElementsAre;
+using testing::NiceMock;
+using testing::Return;
+
+static sp<IBinder> getBinder() {
+ // It doesn't matter what remote binder it is, we just need one so that linkToDeath will work.
+ // The context manager (servicemanager) is easy to get and is in another process.
+ return android::ProcessState::self()->getContextObject(nullptr);
+}
+
+class MockAccess : public Access {
+public:
+ MOCK_METHOD0(getCallingContext, CallingContext());
+ MOCK_METHOD2(canAdd, bool(const CallingContext&, const std::string& name));
+ MOCK_METHOD2(canFind, bool(const CallingContext&, const std::string& name));
+ MOCK_METHOD1(canList, bool(const CallingContext&));
+};
+
+static sp<ServiceManager> getPermissiveServiceManager() {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ ON_CALL(*access, getCallingContext()).WillByDefault(Return(Access::CallingContext{}));
+ ON_CALL(*access, canAdd(_, _)).WillByDefault(Return(true));
+ ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
+ ON_CALL(*access, canList(_)).WillByDefault(Return(true));
+
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ return sm;
+}
+
+TEST(AddService, HappyHappy) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, EmptyNameDisallowed) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_FALSE(sm->addService("", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, JustShortEnoughServiceNameHappy) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_TRUE(sm->addService(std::string(127, 'a'), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, TooLongNameDisallowed) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_FALSE(sm->addService(std::string(128, 'a'), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, WeirdCharactersDisallowed) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_FALSE(sm->addService("happy$foo$foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, AddNullServiceDisallowed) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_FALSE(sm->addService("foo", nullptr, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, AddDisallowedFromApp) {
+ for (uid_t uid : { AID_APP_START, AID_APP_START + 1, AID_APP_END }) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+ EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{
+ .debugPid = 1337,
+ .uid = uid,
+ }));
+ EXPECT_CALL(*access, canAdd(_, _)).Times(0);
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+
+ EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+ }
+
+}
+
+TEST(AddService, HappyOverExistingService) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+ EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(AddService, NoPermissions) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
+ EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
+
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+
+ EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+}
+
+TEST(GetService, HappyHappy) {
+ auto sm = getPermissiveServiceManager();
+ EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> out;
+ EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_EQ(getBinder(), out);
+}
+
+TEST(GetService, NonExistant) {
+ auto sm = getPermissiveServiceManager();
+
+ sp<IBinder> out;
+ EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_EQ(nullptr, out.get());
+}
+
+TEST(GetService, NoPermissionsForGettingService) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ EXPECT_CALL(*access, getCallingContext()).WillRepeatedly(Return(Access::CallingContext{}));
+ EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
+
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+
+ EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> out;
+ // returns nullptr but has OK status for legacy compatibility
+ EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_EQ(nullptr, out.get());
+}
+
+TEST(GetService, AllowedFromIsolated) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ EXPECT_CALL(*access, getCallingContext())
+ // something adds it
+ .WillOnce(Return(Access::CallingContext{}))
+ // next call is from isolated app
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
+ EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
+
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+
+ EXPECT_TRUE(sm->addService("foo", getBinder(), true /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> out;
+ EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_EQ(getBinder(), out.get());
+}
+
+TEST(GetService, NotAllowedFromIsolated) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ EXPECT_CALL(*access, getCallingContext())
+ // something adds it
+ .WillOnce(Return(Access::CallingContext{}))
+ // next call is from isolated app
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
+ EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
+
+ // TODO(b/136023468): when security check is first, this should be called first
+ // EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
+
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+
+ EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+
+ sp<IBinder> out;
+ // returns nullptr but has OK status for legacy compatibility
+ EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_EQ(nullptr, out.get());
+}
+
+TEST(ListServices, NoPermissions) {
+ std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
+
+ EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
+ EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
+
+ sp<ServiceManager> sm = new ServiceManager(std::move(access));
+
+ std::vector<std::string> out;
+ EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
+ EXPECT_TRUE(out.empty());
+}
+
+TEST(ListServices, AllServices) {
+ auto sm = getPermissiveServiceManager();
+
+ EXPECT_TRUE(sm->addService("sd", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+ EXPECT_TRUE(sm->addService("sc", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL).isOk());
+ EXPECT_TRUE(sm->addService("sb", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_HIGH).isOk());
+ EXPECT_TRUE(sm->addService("sa", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL).isOk());
+
+ std::vector<std::string> out;
+ EXPECT_TRUE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
+
+ // all there and in the right order
+ EXPECT_THAT(out, ElementsAre("sa", "sb", "sc", "sd"));
+}
+
+TEST(ListServices, CriticalServices) {
+ auto sm = getPermissiveServiceManager();
+
+ EXPECT_TRUE(sm->addService("sd", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
+ EXPECT_TRUE(sm->addService("sc", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL).isOk());
+ EXPECT_TRUE(sm->addService("sb", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_HIGH).isOk());
+ EXPECT_TRUE(sm->addService("sa", getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL).isOk());
+
+ std::vector<std::string> out;
+ EXPECT_TRUE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, &out).isOk());
+
+ // all there and in the right order
+ EXPECT_THAT(out, ElementsAre("sa"));
+}
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.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..ad7791e 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -36,18 +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 to specify if the device supports adding device admins. -->
- <feature name="android.software.device_admin" />
-
- <!-- Feature to specify if the device support managed users. -->
- <feature name="android.software.managed_users" />
+ <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" />
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
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..17b9993 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.
@@ -114,7 +115,8 @@
kKeyStatusType_Expired,
kKeyStatusType_OutputNotAllowed,
kKeyStatusType_StatusPending,
- kKeyStatusType_InternalError
+ kKeyStatusType_InternalError,
+ kKeyStatusType_UsableInFuture
};
// Used by sendKeysChange to report the usability status of each
@@ -139,6 +141,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 +171,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/font.h b/include/android/font.h
new file mode 100644
index 0000000..8001ee1
--- /dev/null
+++ b/include/android/font.h
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup Font
+ * @{
+ */
+
+/**
+ * @file font.h
+ * @brief Provides some constants used in system_fonts.h or fonts_matcher.h
+ *
+ * Available since API level 29.
+ */
+
+#ifndef ANDROID_FONT_H
+#define ANDROID_FONT_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. */
+ AFONT_WEIGHT_MIN = 0,
+
+ /** A font weight value for the thin weight. */
+ AFONT_WEIGHT_THIN = 100,
+
+ /** A font weight value for the extra-light weight. */
+ AFONT_WEIGHT_EXTRA_LIGHT = 200,
+
+ /** A font weight value for the light weight. */
+ AFONT_WEIGHT_LIGHT = 300,
+
+ /** A font weight value for the normal weight. */
+ AFONT_WEIGHT_NORMAL = 400,
+
+ /** A font weight value for the medium weight. */
+ AFONT_WEIGHT_MEDIUM = 500,
+
+ /** A font weight value for the semi-bold weight. */
+ AFONT_WEIGHT_SEMI_BOLD = 600,
+
+ /** A font weight value for the bold weight. */
+ AFONT_WEIGHT_BOLD = 700,
+
+ /** A font weight value for the extra-bold weight. */
+ AFONT_WEIGHT_EXTRA_BOLD = 800,
+
+ /** A font weight value for the black weight. */
+ AFONT_WEIGHT_BLACK = 900,
+
+ /** The maximum value for the font weight value. */
+ AFONT_WEIGHT_MAX = 1000
+};
+
+/**
+ * AFont provides information of the single font configuration.
+ */
+struct AFont;
+
+/**
+ * Close an AFont.
+ *
+ * \param font a font returned by ASystemFontIterator_next or AFontMatchert_match.
+ * Do nothing if NULL is passed.
+ */
+void AFont_close(AFont* _Nullable font) __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 AFont_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 AFont_getFontFilePath(const AFont* _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 AFONT_WEIGHT_THIN}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">200</td>
+ * <td align="center">Extra Light (Ultra Light)</td>
+ * <td align="center">{@link AFONT_WEIGHT_EXTRA_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">300</td>
+ * <td align="center">Light</td>
+ * <td align="center">{@link AFONT_WEIGHT_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">400</td>
+ * <td align="center">Normal (Regular)</td>
+ * <td align="center">{@link AFONT_WEIGHT_NORMAL}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">500</td>
+ * <td align="center">Medium</td>
+ * <td align="center">{@link AFONT_WEIGHT_MEDIUM}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">600</td>
+ * <td align="center">Semi Bold (Demi Bold)</td>
+ * <td align="center">{@link AFONT_WEIGHT_SEMI_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">700</td>
+ * <td align="center">Bold</td>
+ * <td align="center">{@link AFONT_WEIGHT_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">800</td>
+ * <td align="center">Extra Bold (Ultra Bold)</td>
+ * <td align="center">{@link AFONT_WEIGHT_EXTRA_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">900</td>
+ * <td align="center">Black (Heavy)</td>
+ * <td align="center">{@link AFONT_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 AFont_getWeight(const AFont* _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 AFont_isItalic(const AFont* _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 AFont_close() is called.
+ *
+ * \param font a font object. Passing NULL is not allowed.
+ * \return a IETF BCP47 compliant language tag or nullptr if not available.
+ */
+const char* _Nullable AFont_getLocale(const AFont* _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 AFont_getCollectionIndex(const AFont* _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, AFont_getAxisCount returns 2 and AFont_getAxisTag
+ * and AFont_getAxisValue will return following values.
+ * \code{.cpp}
+ * AFont* font = AFontIterator_next(ite);
+ *
+ * // Returns the number of axes
+ * AFont_getAxisCount(font); // Returns 2
+ *
+ * // Returns the tag-value pair for the first axis.
+ * AFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874)
+ * AFont_getAxisValue(font, 0); // Returns 700.0
+ *
+ * // Returns the tag-value pair for the second axis.
+ * AFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74)
+ * AFont_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 AFont_getAxisCount(const AFont* _Nonnull font) __INTRODUCED_IN(29);
+
+
+/**
+ * Return an OpenType axis tag associated with the current font.
+ *
+ * See AFont_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 AFont_getAxisCount} is not allowed.
+ * \return an OpenType axis tag value for the given font variation setting.
+ */
+uint32_t AFont_getAxisTag(const AFont* _Nonnull font, uint32_t axisIndex)
+ __INTRODUCED_IN(29);
+
+/**
+ * Return an OpenType axis value associated with the current font.
+ *
+ * See AFont_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 AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex)
+ __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_FONT_H
+
+/** @} */
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
new file mode 100644
index 0000000..0b8f892
--- /dev/null
+++ b/include/android/font_matcher.h
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup Font
+ * @{
+ */
+
+/**
+ * @file font_matcher.h
+ * @brief Provides the font matching logic with various inputs.
+ *
+ * You can use this class for deciding what font is to be used for drawing text.
+ *
+ * A matcher is created from text style, locales and UI compatibility. The match function for
+ * matcher object can be called multiple times until close function is called.
+ *
+ * Even if no font can render the given text, the match 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' };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a valid font file.
+ * AFontMatcher_destroy(matcher);
+ *
+ * // Querying font for CJK characters
+ * std::vector<uint16_t> text = { 0x9AA8 };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * AFontMatcher_setLocales(matcher, "zh-CN,ja-JP");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Simplified Chinese font.
+ * AFontMatcher_setLocales(matcher, "ja-JP,zh-CN");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Japanese font.
+ * AFontMatcher_destroy(matcher);
+ *
+ * // Querying font for text/color emoji
+ * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 8 and the font will points a color emoji font.
+ * AFontMatcher_destroy(matcher);
+ *
+ * // Mixture of multiple script of characters.
+ * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
+ * std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
+ * AFontMatcher* matcher = AFontMatcher_create("sans-serif");
+ * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ * // runLength will be 1 and the font will points a Hebrew font.
+ * AFontMatcher_destroy(matcher);
+ * \endcode
+ *
+ * Available since API level 29.
+ */
+
+#ifndef ANDROID_FONT_MATCHER_H
+#define ANDROID_FONT_MATCHER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+#include <android/font.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 {
+ /** A family variant value for the system default variant. */
+ AFAMILY_VARIANT_DEFAULT = 0,
+
+ /**
+ * A family variant value for the compact font family variant.
+ *
+ * The compact font family has Latin-based vertical metrics.
+ */
+ AFAMILY_VARIANT_COMPACT = 1,
+
+ /**
+ * A family variant value for the elegant font family variant.
+ *
+ * The elegant font family may have larger vertical metrics than Latin font.
+ */
+ AFAMILY_VARIANT_ELEGANT = 2,
+};
+
+/**
+ * AFontMatcher performs match operation on given parameters and available font files.
+ * This matcher is not a thread-safe object. Do not pass this matcher to other threads.
+ */
+struct AFontMatcher;
+
+/**
+ * Select the best font from given parameters.
+ *
+ */
+
+/**
+ * Creates a new AFontMatcher object
+ */
+AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29);
+
+/**
+ * Destroy the matcher object.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ */
+void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29);
+
+/**
+ * Set font style to matcher.
+ *
+ * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL}
+ * with non-italic style.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param weight a font weight value. Only from 0 to 1000 value is valid
+ * \param italic true if italic, otherwise false.
+ */
+void AFontMatcher_setStyle(
+ AFontMatcher* _Nonnull matcher,
+ uint16_t weight,
+ bool italic) __INTRODUCED_IN(29);
+
+/**
+ * Set font locales to matcher.
+ *
+ * If this function is not called, the matcher performs with empty locale list.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param languageTags a null character terminated comma separated IETF BCP47 compliant language
+ * tags.
+ */
+void AFontMatcher_setLocales(
+ AFontMatcher* _Nonnull matcher,
+ const char* _Nonnull languageTags) __INTRODUCED_IN(29);
+
+/**
+ * Set family variant to matcher.
+ *
+ * If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param familyVariant Must be one of {@link AFAMILY_VARIANT_DEFAULT},
+ * {@link AFAMILY_VARIANT_COMPACT} or {@link AFAMILY_VARIANT_ELEGANT} is valid.
+ */
+void AFontMatcher_setFamilyVariant(
+ AFontMatcher* _Nonnull matcher,
+ uint32_t familyVariant) __INTRODUCED_IN(29);
+
+/**
+ * Performs the matching from the generic font family for the text and select one font.
+ *
+ * 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.
+ *
+ * \param matcher a matcher object. Passing NULL is not allowed.
+ * \param familyName a null character terminated font family name
+ * \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.
+ */
+AFont* _Nonnull AFontMatcher_match(
+ const AFontMatcher* _Nonnull matcher,
+ const char* _Nonnull familyName,
+ const uint16_t* _Nonnull text,
+ const uint32_t textLength,
+ uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_FONT_MATCHER_H
+
+/** @} */
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
index 7c4be24..aedf369 100644
--- a/include/android/hardware_buffer_jni.h
+++ b/include/android/hardware_buffer_jni.h
@@ -15,7 +15,13 @@
*/
/**
+ * @addtogroup AHardwareBuffer
+ * @{
+ */
+
+/**
* @file hardware_buffer_jni.h
+ * @brief JNI glue for native hardware buffers.
*/
#ifndef ANDROID_HARDWARE_BUFFER_JNI_H
@@ -30,23 +36,25 @@
__BEGIN_DECLS
/**
- * Return the AHardwareBuffer associated with a Java HardwareBuffer object,
- * for interacting with it through native code. This method does not acquire any
- * additional reference to the AHardwareBuffer that is returned. To keep the
- * AHardwareBuffer live after the Java HardwareBuffer object got garbage
- * collected, be sure to use AHardwareBuffer_acquire() to acquire an additional
- * reference.
+ * Return the AHardwareBuffer wrapped by a Java HardwareBuffer object.
+ *
+ * This method does not acquire any additional reference to the AHardwareBuffer
+ * that is returned. To keep the AHardwareBuffer live after the Java
+ * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire()
+ * to acquire an additional reference.
*/
AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env,
- jobject hardwareBufferObj);
+ jobject hardwareBufferObj) __INTRODUCED_IN(26);
/**
* Return a new Java HardwareBuffer object that wraps the passed native
* AHardwareBuffer object.
*/
jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env,
- AHardwareBuffer* hardwareBuffer);
+ AHardwareBuffer* hardwareBuffer) __INTRODUCED_IN(26);
__END_DECLS
#endif // ANDROID_HARDWARE_BUFFER_JNI_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..f0485a1
--- /dev/null
+++ b/include/android/system_fonts.h
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup Font
+ * @{
+ */
+
+/**
+ * @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>
+
+#include <android/font.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
+
+/**
+ * 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;
+
+/**
+ * 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.
+ */
+AFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __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/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 009dc52..639df7a 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -20,7 +20,6 @@
namespace android {
// must be kept in sync with definitions in AudioPlaybackConfiguration.java
-
#define PLAYER_PIID_INVALID -1
typedef enum {
@@ -40,6 +39,15 @@
PLAYER_STATE_STOPPED = 4,
} player_state_t;
+// must be kept in sync with definitions in AudioManager.java
+#define RECORD_RIID_INVALID -1
+
+typedef enum {
+ RECORDER_STATE_UNKNOWN = -1,
+ RECORDER_STATE_STARTED = 0,
+ RECORDER_STATE_STOPPED = 1,
+} recorder_state_t;
+
}; // namespace android
#endif // ANDROID_AUDIOMANAGER_H
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index d279bbd..2f5ccb8 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -36,6 +36,9 @@
PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 1,
PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 2,
RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 3,
+ TRACK_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 4,
+ RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 5,
+ RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6,
};
DECLARE_META_INTERFACE(AudioManager)
@@ -48,6 +51,9 @@
audio_content_type_t content)= 0;
/*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
+ virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
+ /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
+ /*oneway*/ virtual status_t releaseRecorder(audio_unique_id_t riid) = 0;
};
// ----------------------------------------------------------------------------
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 79%
rename from services/inputflinger/InputApplication.h
rename to include/input/InputApplication.h
index 724fc2c..7f04611 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);
};
@@ -41,15 +50,19 @@
class InputApplicationHandle : public RefBase {
public:
inline const InputApplicationInfo* getInfo() const {
- return mInfo;
+ return &mInfo;
}
inline std::string getName() const {
- return mInfo ? mInfo->name : "<invalid>";
+ return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+ return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
+ }
+
+ inline sp<IBinder> getApplicationToken() const {
+ return mInfo.token;
}
/**
@@ -62,18 +75,11 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
-
- /**
- * Releases the storage used by the associated information when it is
- * no longer needed.
- */
- void releaseInfo();
-
protected:
InputApplicationHandle();
virtual ~InputApplicationHandle();
- InputApplicationInfo* mInfo;
+ InputApplicationInfo mInfo;
};
} // namespace android
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 4222e36..c6ce576 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -94,7 +94,6 @@
"PermissionController.cpp",
"ProcessInfoService.cpp",
"IpPrefix.cpp",
- ":libbinder_aidl",
],
},
},
@@ -107,6 +106,7 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wzero-as-null-pointer-constant",
],
product_variables: {
binder32bit: {
@@ -140,8 +140,7 @@
name: "libbinder_aidl",
srcs: [
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/os/IServiceManager.aidl",
],
path: "aidl",
}
-
-subdirs = ["tests"]
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index a494e22..525685c 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -93,6 +93,14 @@
: APP_OPS_MANAGER_UNAVAILABLE_MODE;
}
+int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
+ const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ return service != nullptr
+ ? service->checkAudioOperation(op, usage, uid, callingPackage)
+ : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+}
+
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
sp<IAppOpsService> service = getService();
return service != nullptr
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 221c002..693045e 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -241,7 +241,7 @@
if (!e) return; // out of memory
}
- e->mRequestingSid = true;
+ e->mRequestingSid = requestingSid;
}
sp<IBinder> BBinder::getExtension() {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 425ece3..74ffde2 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -215,16 +215,15 @@
// Once a binder has died, it will never come back to life.
if (mAlive) {
// user transactions require a given stability level
- // Cannot add requirement w/o SM update
- // if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
- // using android::internal::Stability;
+ if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
+ using android::internal::Stability;
- // auto stability = Stability::get(this);
+ auto stability = Stability::get(this);
- // if (CC_UNLIKELY(!Stability::check(stability, Stability::kLocalStability))) {
- // return BAD_TYPE;
- // }
- // }
+ if (CC_UNLIKELY(!Stability::check(stability, Stability::kLocalStability))) {
+ return BAD_TYPE;
+ }
+ }
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
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/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index 41a0111..4c151e7 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -55,7 +55,8 @@
case OP_CHANGED_TRANSACTION: {
CHECK_INTERFACE(IAppOpsCallback, data, reply);
int32_t op = data.readInt32();
- String16 packageName = data.readString16();
+ String16 packageName;
+ (void)data.readString16(&packageName);
opChanged(op, packageName);
reply->writeNoException();
return NO_ERROR;
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index c839ba5..c426f3a 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -121,6 +121,22 @@
if (reply.readExceptionCode() != 0) return -1;
return reply.readInt32();
}
+
+ virtual int32_t checkAudioOperation(int32_t code, int32_t usage,
+ int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(usage);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(CHECK_AUDIO_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) {
+ return MODE_ERRORED;
+ }
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
@@ -207,6 +223,17 @@
reply->writeInt32(opCode);
return NO_ERROR;
} break;
+ case CHECK_AUDIO_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ const int32_t code = data.readInt32();
+ const int32_t usage = data.readInt32();
+ const int32_t uid = data.readInt32();
+ const String16 packageName = data.readString16();
+ const int32_t res = checkAudioOperation(code, usage, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
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 4356706..7c45c77 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -114,6 +114,8 @@
"BC_DEAD_BINDER_DONE"
};
+static const int64_t kWorkSourcePropagatedBitIndex = 32;
+
static const char* getReturnString(uint32_t cmd)
{
size_t idx = cmd & _IOC_NRMASK;
@@ -395,6 +397,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;
@@ -760,6 +804,8 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
+ mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0),
mCallRestriction(mProcess->mCallRestriction)
@@ -1017,7 +1063,7 @@
sp<BBinder> the_context_object;
-void setTheContextObject(sp<BBinder> obj)
+void IPCThreadState::setTheContextObject(sp<BBinder> obj)
{
the_context_object = obj;
}
@@ -1133,6 +1179,13 @@
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);
@@ -1188,6 +1241,8 @@
mCallingUid = origUid;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
+ mWorkSource = origWorkSource;
+ mPropagateWorkSource = origPropagateWorkSet;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9d816e8..74f1f47 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -18,6 +18,7 @@
#include <binder/IServiceManager.h>
+#include <android/os/IServiceManager.h>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#ifndef __ANDROID_VNDK__
@@ -34,6 +35,9 @@
namespace android {
+using AidlServiceManager = android::os::IServiceManager;
+using android::binder::Status;
+
sp<IServiceManager> defaultServiceManager()
{
static Mutex gDefaultServiceManagerLock;
@@ -142,11 +146,12 @@
{
public:
explicit BpServiceManager(const sp<IBinder>& impl)
- : BpInterface<IServiceManager>(impl)
+ : BpInterface<IServiceManager>(impl),
+ mTheRealServiceManager(interface_cast<AidlServiceManager>(impl))
{
}
- virtual sp<IBinder> getService(const String16& name) const
+ sp<IBinder> getService(const String16& name) const override
{
static bool gSystemBootCompleted = false;
@@ -179,43 +184,36 @@
return nullptr;
}
- virtual sp<IBinder> checkService( const String16& name) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
- data.writeString16(name);
- remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
- return reply.readStrongBinder();
+ sp<IBinder> checkService(const String16& name) const override {
+ sp<IBinder> ret;
+ if (!mTheRealServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
+ return nullptr;
+ }
+ return ret;
}
- virtual status_t addService(const String16& name, const sp<IBinder>& service,
- bool allowIsolated, int dumpsysPriority) {
- Parcel data, reply;
- data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
- data.writeString16(name);
- data.writeStrongBinder(service);
- data.writeInt32(allowIsolated ? 1 : 0);
- data.writeInt32(dumpsysPriority);
- status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
- return err == NO_ERROR ? reply.readExceptionCode() : err;
+ status_t addService(const String16& name, const sp<IBinder>& service,
+ bool allowIsolated, int dumpsysPriority) override {
+ Status status = mTheRealServiceManager->addService(String8(name).c_str(), service, allowIsolated, dumpsysPriority);
+ return status.exceptionCode();
}
virtual Vector<String16> listServices(int dumpsysPriority) {
- Vector<String16> res;
- int n = 0;
+ std::vector<std::string> ret;
+ if (!mTheRealServiceManager->listServices(dumpsysPriority, &ret).isOk()) {
+ return {};
+ }
- for (;;) {
- Parcel data, reply;
- data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
- data.writeInt32(n++);
- data.writeInt32(dumpsysPriority);
- status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);
- if (err != NO_ERROR)
- break;
- res.add(reply.readString16());
+ Vector<String16> res;
+ res.setCapacity(ret.size());
+ for (const std::string& name : ret) {
+ res.push(String16(name.c_str()));
}
return res;
}
+
+private:
+ sp<AidlServiceManager> mTheRealServiceManager;
};
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
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 485869e..8751ecb 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -74,10 +74,13 @@
}
// 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)
namespace android {
+// many things compile this into prebuilts on the stack
+static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
+
static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
static size_t gParcelGlobalAllocSize = 0;
static size_t gParcelGlobalAllocCount = 0;
@@ -166,27 +169,24 @@
}
status_t Parcel::finishFlattenBinder(
- const sp<IBinder>& /*binder*/, const flat_binder_object& flat)
+ const sp<IBinder>& binder, const flat_binder_object& flat)
{
status_t status = writeObject(flat, false);
if (status != OK) return status;
- // internal::Stability::tryMarkCompilationUnit(binder.get());
- // Cannot change wire protocol w/o SM update
- // return writeInt32(internal::Stability::get(binder.get()));
- return OK;
+ internal::Stability::tryMarkCompilationUnit(binder.get());
+ return writeInt32(internal::Stability::get(binder.get()));
}
status_t Parcel::finishUnflattenBinder(
const sp<IBinder>& binder, sp<IBinder>* out) const
{
- // int32_t stability;
- // Cannot change wire protocol w/o SM update
- // status_t status = readInt32(&stability);
- // if (status != OK) return status;
+ int32_t stability;
+ status_t status = readInt32(&stability);
+ if (status != OK) return status;
- // status = internal::Stability::set(binder.get(), stability, true /*log*/);
- // if (status != OK) return status;
+ status = internal::Stability::set(binder.get(), stability, true /*log*/);
+ if (status != OK) return status;
*out = binder;
return OK;
@@ -500,15 +500,60 @@
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;
+ }
+}
+
+#ifdef __ANDROID_VNDK__
+constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
+#else
+constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
+#endif
+
// 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);
+ writeInt32(kHeader);
// 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());
@@ -517,6 +562,7 @@
bool Parcel::enforceInterface(const String16& interface,
IPCThreadState* threadState) const
{
+ // StrictModePolicy.
int32_t strictPolicy = readInt32();
if (threadState == nullptr) {
threadState = IPCThreadState::self();
@@ -531,6 +577,17 @@
} else {
threadState->setStrictModePolicy(strictPolicy);
}
+ // WorkSource.
+ updateWorkSourceRequestHeaderPosition();
+ int32_t workSource = readInt32();
+ threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+ // vendor header
+ int32_t header = readInt32();
+ if (header != kHeader) {
+ ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header);
+ return false;
+ }
+ // Interface descriptor.
const String16 str(readString16());
if (str == interface) {
return true;
@@ -770,6 +827,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);
@@ -1503,6 +1570,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);
}
@@ -2089,6 +2164,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;
@@ -2526,10 +2610,16 @@
}
release_object(proc, *flat, this, &mOpenAshmemSize);
}
- binder_size_t* objects =
- (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
- if (objects) {
- mObjects = objects;
+
+ if (objectsSize == 0) {
+ free(mObjects);
+ mObjects = nullptr;
+ } else {
+ binder_size_t* objects =
+ (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
+ if (objects) {
+ mObjects = objects;
+ }
}
mObjectsSize = objectsSize;
mNextObjectHint = 0;
@@ -2612,6 +2702,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/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 01e57d3..136bdb0 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -11,6 +11,9 @@
},
{
"name": "binderLibTest"
+ },
+ {
+ "name": "binderStabilityTest"
}
]
}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index c92c81f..a7a7292 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -54,6 +54,12 @@
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);
+
/* ApplicationInfo.isSystemApp() == true */
const int LOCATION_SYSTEM = 0x1;
/* ApplicationInfo.isVendor() == true */
@@ -68,4 +74,17 @@
* LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct()
*/
int getLocationFlags(in @utf8InCpp String packageName);
+
+ /**
+ * Returns the target SDK version for the given package.
+ * Unknown packages will cause the call to fail. The caller must check the
+ * returned Status before using the result of this function.
+ */
+ int getTargetSdkVersionForPackage(in String packageName);
+
+ /**
+ * Returns the name of module metadata package, or empty string if device doesn't have such
+ * package.
+ */
+ @utf8InCpp String getModuleMetadataPackageName();
}
diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS
index a967a7f..b99ca09 100644
--- a/libs/binder/aidl/android/content/pm/OWNERS
+++ b/libs/binder/aidl/android/content/pm/OWNERS
@@ -1,4 +1,4 @@
narayan@google.com
patb@google.com
svetoslavganov@google.com
-toddke@google.com
+toddke@google.com
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
new file mode 100644
index 0000000..50a72aa
--- /dev/null
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+/**
+ * Basic interface for finding and publishing system services.
+ *
+ * You likely want to use android.os.ServiceManager in Java or
+ * android::IServiceManager in C++ in order to use this interface.
+ *
+ * @hide
+ */
+interface IServiceManager {
+ /*
+ * Must update values in IServiceManager.h
+ */
+ /* Allows services to dump sections according to priorities. */
+ const int DUMP_FLAG_PRIORITY_CRITICAL = 1; // 1 << 0
+ const int DUMP_FLAG_PRIORITY_HIGH = 2; // 1 << 1
+ const int DUMP_FLAG_PRIORITY_NORMAL = 4; // 1 << 2
+ /**
+ * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the
+ * same priority as NORMAL priority but the services are not called with dump priority
+ * arguments.
+ */
+ const int DUMP_FLAG_PRIORITY_DEFAULT = 8; // 1 << 3
+
+ const int DUMP_FLAG_PRIORITY_ALL = 15;
+ // DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
+ // | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT;
+
+ /* Allows services to dump sections in protobuf format. */
+ const int DUMP_FLAG_PROTO = 16; // 1 << 4
+
+ /**
+ * Retrieve an existing service called @a name from the
+ * service manager.
+ *
+ * This is the same as checkService (returns immediately) but
+ * exists for legacy purposes.
+ *
+ * Returns null if the service does not exist.
+ */
+ @UnsupportedAppUsage
+ IBinder getService(@utf8InCpp String name);
+
+ /**
+ * Retrieve an existing service called @a name from the service
+ * manager. Non-blocking. Returns null if the service does not
+ * exist.
+ */
+ @UnsupportedAppUsage
+ IBinder checkService(@utf8InCpp String name);
+
+ /**
+ * Place a new @a service called @a name into the service
+ * manager.
+ */
+ void addService(@utf8InCpp String name, IBinder service,
+ boolean allowIsolated, int dumpPriority);
+
+ /**
+ * Return a list of all currently running services.
+ */
+ @utf8InCpp String[] listServices(int dumpPriority);
+}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index b8db091..5f324c7 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,29 @@
};
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_BOUND_TOP = 4,
+ PROCESS_STATE_FOREGROUND_SERVICE = 5,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
+ PROCESS_STATE_BACKUP = 10,
+ PROCESS_STATE_SERVICE = 11,
+ PROCESS_STATE_RECEIVER = 12,
+ PROCESS_STATE_TOP_SLEEPING = 13,
+ PROCESS_STATE_HEAVY_WEIGHT = 14,
+ PROCESS_STATE_HOME = 15,
+ PROCESS_STATE_LAST_ACTIVITY = 16,
+ PROCESS_STATE_CACHED_ACTIVITY = 17,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
+ PROCESS_STATE_CACHED_RECENT = 19,
+ PROCESS_STATE_CACHED_EMPTY = 20,
+ PROCESS_STATE_NONEXISTENT = 21,
};
ActivityManager();
@@ -53,8 +76,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..17493b4 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -95,11 +95,27 @@
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();
int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
+ const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
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/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 7807851..3dbd0d9 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -43,6 +43,8 @@
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
virtual int32_t permissionToOpCode(const String16& permission) = 0;
+ virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
+ const String16& packageName) = 0;
enum {
CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -53,6 +55,7 @@
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+ CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
};
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 a20ef7c..b810f7e 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -52,6 +52,19 @@
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;
@@ -97,6 +110,8 @@
// the maximum number of binder threads threads allowed for this process.
void blockUntilThreadAvailable();
+ // Service manager registration
+ void setTheContextObject(sp<BBinder> obj);
// Is this thread currently serving a binder call. This method
// returns true if while traversing backwards from the function call
@@ -124,6 +139,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();
@@ -162,6 +184,11 @@
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..30786fa 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -71,13 +71,6 @@
*/
// NOLINTNEXTLINE(google-default-arguments)
virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;
-
- enum {
- GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- CHECK_SERVICE_TRANSACTION,
- ADD_SERVICE_TRANSACTION,
- LIST_SERVICES_TRANSACTION,
- };
};
sp<IServiceManager> defaultServiceManager();
@@ -88,7 +81,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 c8f82a3..50ca983 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -135,6 +135,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);
@@ -299,6 +301,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;
@@ -374,6 +378,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,
@@ -408,6 +418,7 @@
void initState();
void scanForFds() const;
status_t validateReadData(size_t len) const;
+ void updateWorkSourceRequestHeaderPosition() const;
status_t finishFlattenBinder(const sp<IBinder>& binder,
const flat_binder_object& flat);
@@ -462,6 +473,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 5f8887b..495b2f9 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -70,6 +70,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)
@@ -178,6 +180,7 @@
public:
virtual void SetUp() {
m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+ IPCThreadState::self()->restoreCallingWorkSource(0);
}
virtual void TearDown() {
}
@@ -895,6 +898,132 @@
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)
+{
+ IPCThreadState::self()->clearPropagateWorkSource();
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagCleared)
+{
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ IPCThreadState::self()->clearPropagateWorkSource();
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagRestored)
+{
+ 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:
@@ -1164,6 +1293,19 @@
reply->writeStrongBinder(binder);
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 ce026e9..09f58cc 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;
@@ -805,6 +821,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 f924863..40f6b43 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -38,6 +38,7 @@
"/system/bin/surfaceflinger",
"/system/bin/vehicle_network_service",
"/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec
+ "/apex/com.android.media.swcodec/bin/mediaswcodec", // media.swcodec
NULL,
};
@@ -48,11 +49,16 @@
"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.c2@1.0::IComponentStore",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
+ "android.hardware.power@1.3::IPower",
+ "android.hardware.power.stats@1.0::IPowerStats",
"android.hardware.sensors@1.0::ISensors",
+ "android.hardware.thermal@2.0::IThermal",
"android.hardware.vr@1.0::IVr",
NULL,
};
@@ -105,13 +111,15 @@
}
bool IsZygote(int pid) {
- static const std::string kZygotePrefix = "zygote";
-
std::string cmdline;
if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid),
&cmdline)) {
return true;
}
- return (cmdline.find(kZygotePrefix) == 0);
+ // cmdline has embedded nulls; only consider argv[0].
+ cmdline = std::string(cmdline.c_str());
+
+ return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" ||
+ cmdline == "usap64";
}
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 006df8c..56521bf 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -16,16 +16,20 @@
name: "libgraphicsenv",
srcs: [
+ "GpuStatsInfo.cpp",
"GraphicsEnv.cpp",
+ "IGpuService.cpp"
],
cflags: ["-Wall", "-Werror"],
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"libdl_android",
"liblog",
+ "libutils",
],
export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
new file mode 100644
index 0000000..4a801be
--- /dev/null
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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;
+ if ((status = parcel->writeInt32(vulkanVersion)) != OK) return status;
+ if ((status = parcel->writeInt32(cpuVulkanVersion)) != OK) return status;
+ if ((status = parcel->writeInt32(glesVersion)) != OK) return status;
+ if ((status = parcel->writeInt32(angleLoadingCount)) != OK) return status;
+ if ((status = parcel->writeInt32(angleLoadingFailureCount)) != 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;
+ if ((status = parcel->readInt32(&vulkanVersion)) != OK) return status;
+ if ((status = parcel->readInt32(&cpuVulkanVersion)) != OK) return status;
+ if ((status = parcel->readInt32(&glesVersion)) != OK) return status;
+ if ((status = parcel->readInt32(&angleLoadingCount)) != OK) return status;
+ if ((status = parcel->readInt32(&angleLoadingFailureCount)) != 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, "angleLoadingCount = %d\n", angleLoadingCount);
+ StringAppendF(&result, "angleLoadingFailureCount = %d\n", angleLoadingFailureCount);
+ StringAppendF(&result, "vkLoadingCount = %d\n", vkLoadingCount);
+ StringAppendF(&result, "vkLoadingFailureCount = %d\n", vkLoadingFailureCount);
+ StringAppendF(&result, "vulkanVersion = %d\n", vulkanVersion);
+ StringAppendF(&result, "cpuVulkanVersion = %d\n", cpuVulkanVersion);
+ StringAppendF(&result, "glesVersion = %d\n", glesVersion);
+ 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;
+ if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status;
+ if ((status = parcel->writeBool(cpuVulkanInUse)) != 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;
+ if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status;
+ if ((status = parcel->readBool(&cpuVulkanInUse)) != 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);
+ StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
+ result.append("glDriverLoadingTime:");
+ for (int32_t loadingTime : glDriverLoadingTime) {
+ StringAppendF(&result, " %d", loadingTime);
+ }
+ result.append("\n");
+ result.append("angleDriverLoadingTime:");
+ for (int32_t loadingTime : angleDriverLoadingTime) {
+ 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 0d031ed..24b6c2d 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -14,20 +14,30 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
//#define LOG_NDEBUG 1
#define LOG_TAG "GraphicsEnv"
+
#include <graphicsenv/GraphicsEnv.h>
#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 <mutex>
+#include <memory>
+#include <string>
+#include <thread>
// TODO(b/37049319) Get this from a header once one exists
extern "C" {
@@ -45,6 +55,20 @@
};
}
+// 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 {
@@ -115,6 +139,14 @@
return env;
}
+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()) {
@@ -129,6 +161,345 @@
mSphalLibraries = sphalLibraries;
}
+void GraphicsEnv::hintActivityLaunch() {
+ ATRACE_CALL();
+
+ std::thread trySendGpuStatsThread([this]() {
+ // If there's already graphics driver preloaded in the process, just send
+ // the stats info to GpuStats directly through async binder.
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ if (mGpuStats.glDriverToSend) {
+ mGpuStats.glDriverToSend = false;
+ sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime);
+ }
+ if (mGpuStats.vkDriverToSend) {
+ mGpuStats.vkDriverToSend = false;
+ sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime);
+ }
+ });
+ trySendGpuStatsThread.detach();
+}
+
+void GraphicsEnv::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, uint64_t driverVersionCode,
+ int64_t driverBuildTime, const std::string& appPackageName,
+ const int vulkanVersion) {
+ 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"
+ "\tvulkanVersion[%d]\n",
+ driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime,
+ appPackageName.c_str(), vulkanVersion);
+
+ mGpuStats.driverPackageName = driverPackageName;
+ mGpuStats.driverVersionName = driverVersionName;
+ mGpuStats.driverVersionCode = driverVersionCode;
+ mGpuStats.driverBuildTime = driverBuildTime;
+ mGpuStats.appPackageName = appPackageName;
+ mGpuStats.vulkanVersion = vulkanVersion;
+}
+
+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 isDriverLoaded,
+ int64_t driverLoadingTime) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ const bool doNotSend = mGpuStats.appPackageName.empty();
+ if (api == GraphicsEnv::Api::API_GL) {
+ if (doNotSend) mGpuStats.glDriverToSend = true;
+ mGpuStats.glDriverLoadingTime = driverLoadingTime;
+ } else {
+ if (doNotSend) mGpuStats.vkDriverToSend = true;
+ mGpuStats.vkDriverLoadingTime = driverLoadingTime;
+ }
+
+ sendGpuStatsLocked(api, isDriverLoaded, driverLoadingTime);
+}
+
+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::setCpuVulkanInUse() {
+ ATRACE_CALL();
+
+ // Use the same stats lock to protect getGpuService() as well.
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ const sp<IGpuService> gpuService = getGpuService();
+ if (gpuService) {
+ gpuService->setCpuVulkanInUse(mGpuStats.appPackageName, mGpuStats.driverVersionCode);
+ }
+}
+
+void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, 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"
+ "\tvulkanVersion[%d]\n"
+ "\tapi[%d]\n"
+ "\tisDriverLoaded[%d]\n"
+ "\tdriverLoadingTime[%" PRId64 "]",
+ mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(),
+ mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(),
+ mGpuStats.vulkanVersion, static_cast<int32_t>(api), isDriverLoaded, driverLoadingTime);
+
+ GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE;
+ bool isIntendedDriverLoaded = false;
+ if (api == GraphicsEnv::Api::API_GL) {
+ driver = mGpuStats.glDriverToLoad;
+ isIntendedDriverLoaded =
+ isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE);
+ } else {
+ driver = mGpuStats.vkDriverToLoad;
+ isIntendedDriverLoaded =
+ isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE);
+ }
+
+ const sp<IGpuService> gpuService = getGpuService();
+ if (gpuService) {
+ gpuService->setGpuStats(mGpuStats.driverPackageName, mGpuStats.driverVersionName,
+ mGpuStats.driverVersionCode, mGpuStats.driverBuildTime,
+ mGpuStats.appPackageName, mGpuStats.vulkanVersion, driver,
+ isIntendedDriverLoaded, 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) {
if (mLayerPaths.empty()) {
mLayerPaths = layerPaths;
@@ -143,78 +514,124 @@
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;
+}
+
+// Return true if all the required libraries from vndk and sphal namespace are
+// linked to the Game Driver namespace correctly.
+bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) {
+ const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
+ if (llndkLibraries.empty()) {
+ return false;
+ }
+ if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) {
+ ALOGE("Failed to link default namespace[%s]", dlerror());
+ return false;
+ }
+
+ const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
+ if (vndkspLibraries.empty()) {
+ return false;
+ }
+ if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) {
+ ALOGE("Failed to link vndk namespace[%s]", dlerror());
+ return false;
+ }
+
+ if (mSphalLibraries.empty()) {
+ return true;
+ }
+
+ // 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());
+ return false;
+ }
+
+ if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) {
+ ALOGE("Failed to link sphal namespace[%s]", dlerror());
+ return false;
+ }
+
+ return true;
+}
+
android_namespace_t* GraphicsEnv::getDriverNamespace() {
- static std::once_flag once;
- std::call_once(once, [this]() {
- if (mDriverPath.empty()) return;
+ std::lock_guard<std::mutex> lock(mNamespaceMutex);
- auto vndkNamespace = android_get_exported_namespace("vndk");
- if (!vndkNamespace) return;
+ if (mDriverNamespace) {
+ return mDriverNamespace;
+ }
- mDriverNamespace = android_create_namespace("gfx driver",
- mDriverPath.c_str(), // ld_library_path
- mDriverPath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_ISOLATED,
- nullptr, // permitted_when_isolated_path
- nullptr);
+ if (mDriverPath.empty()) {
+ return 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;
- }
+ auto vndkNamespace = android_get_exported_namespace("vndk");
+ if (!vndkNamespace) {
+ return nullptr;
+ }
- 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;
- }
+ mDriverNamespace = android_create_namespace("gfx driver",
+ mDriverPath.c_str(), // ld_library_path
+ mDriverPath.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr, // permitted_when_isolated_path
+ nullptr);
- 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;
- }
- });
+ if (!linkDriverNamespaceLocked(vndkNamespace)) {
+ mDriverNamespace = nullptr;
+ }
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..5f96249
--- /dev/null
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -0,0 +1,214 @@
+/*
+ * 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,
+ const int32_t vulkanVersion, 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(vulkanVersion);
+ 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);
+ }
+
+ virtual void setCpuVulkanInUse(const std::string& appPackageName,
+ const uint64_t driverVersionCode) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ data.writeUtf8AsUtf16(appPackageName);
+ data.writeUint64(driverVersionCode);
+
+ remote()->transact(BnGpuService::SET_CPU_VULKAN_IN_USE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+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 vulkanVersion;
+ if ((status = data.readInt32(&vulkanVersion)) != 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, vulkanVersion, 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 SET_CPU_VULKAN_IN_USE: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string appPackageName;
+ if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status;
+
+ uint64_t driverVersionCode;
+ if ((status = data.readUint64(&driverVersionCode)) != OK) return status;
+
+ setCpuVulkanInUse(appPackageName, driverVersionCode);
+
+ 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..edcccfe
--- /dev/null
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -0,0 +1,75 @@
+/*
+ * 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;
+ int32_t vulkanVersion = 0;
+ int32_t cpuVulkanVersion = 0;
+ int32_t glesVersion = 0;
+ int32_t angleLoadingCount = 0;
+ int32_t angleLoadingFailureCount = 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 = {};
+ std::vector<int64_t> angleDriverLoadingTime = {};
+ bool cpuVulkanInUse = false;
+};
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 2e8e099..f5d19db 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,8 +29,59 @@
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;
+ int32_t vulkanVersion;
+ Driver glDriverToLoad;
+ Driver glDriverFallback;
+ Driver vkDriverToLoad;
+ Driver vkDriverFallback;
+ bool glDriverToSend;
+ bool vkDriverToSend;
+ int64_t glDriverLoadingTime;
+ int64_t vkDriverLoadingTime;
+
+ GpuStats()
+ : driverPackageName(""),
+ driverVersionName(""),
+ driverVersionCode(0),
+ driverBuildTime(0),
+ appPackageName(""),
+ vulkanVersion(0),
+ glDriverToLoad(Driver::NONE),
+ glDriverFallback(Driver::NONE),
+ vkDriverToLoad(Driver::NONE),
+ vkDriverFallback(Driver::NONE),
+ glDriverToSend(false),
+ vkDriverToSend(false),
+ glDriverLoadingTime(0),
+ vkDriverLoadingTime(0) {}
+ };
+
+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
@@ -39,37 +92,64 @@
// which is required by android_link_namespaces.
void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
android_namespace_t* getDriverNamespace();
+ void hintActivityLaunch();
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t versionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, const int32_t vulkanVersion);
+ void setCpuVulkanInUse();
+ void setDriverToLoad(Driver driver);
+ void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+ void sendGpuStatsLocked(Api api, 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();
+ bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
+
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..34f1c7e
--- /dev/null
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -0,0 +1,71 @@
+/*
+ * 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,
+ const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+ bool isDriverLoaded, int64_t driverLoadingTime) = 0;
+
+ // set CPU Vulkan in use signal from GraphicsEnvironment.
+ virtual void setCpuVulkanInUse(const std::string& appPackageName,
+ const uint64_t driverVersionCode) = 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,
+ SET_CPU_VULKAN_IN_USE,
+ // 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..34575f5 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -25,50 +25,94 @@
},
double_loadable: true,
+ defaults: ["libgui_bufferqueue-defaults"],
+
+ srcs: [
+ "BitTube.cpp",
+ "BufferHubConsumer.cpp",
+ "BufferHubProducer.cpp",
+ "BufferItemConsumer.cpp",
+ "ConsumerBase.cpp",
+ "CpuConsumer.cpp",
+ "DisplayEventReceiver.cpp",
+ "GLConsumer.cpp",
+ "GuiConfig.cpp",
+ "IDisplayEventConnection.cpp",
+ "IRegionSamplingListener.cpp",
+ "ISurfaceComposer.cpp",
+ "ISurfaceComposerClient.cpp",
+ "ITransactionCompletedListener.cpp",
+ "LayerDebugInfo.cpp",
+ "LayerMetadata.cpp",
+ "LayerState.cpp",
+ "StreamSplitter.cpp",
+ "Surface.cpp",
+ "SurfaceControl.cpp",
+ "SurfaceComposerClient.cpp",
+ "SyncFeatures.cpp",
+ "view/Surface.cpp",
+ ],
+
+ shared_libs: [
+ "android.frameworks.bufferhub@1.0",
+ "libbufferhub",
+ "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
+ "libinput",
+ "libpdx_default_transport",
+ ],
+
+ // bufferhub is not used when building libgui for vendors
+ target: {
+ vendor: {
+ 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",
+ ],
+ },
+ },
+
+ header_libs: [
+ "libdvr_headers",
+ "libpdx_headers",
+ ],
+}
+
+// Used by media codec services exclusively as a static lib for
+// core bufferqueue support only.
+cc_library_static {
+ name: "libgui_bufferqueue_static",
+ vendor_available: true,
+
+ cflags: [
+ "-DNO_BUFFERHUB",
+ ],
+
+ defaults: ["libgui_bufferqueue-defaults"],
+}
+
+// Common build config shared by libgui and libgui_bufferqueue_static.
+cc_defaults {
+ name: "libgui_bufferqueue-defaults",
+
clang: true,
cflags: [
"-Wall",
"-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",
],
@@ -82,84 +126,58 @@
},
srcs: [
- "BitTube.cpp",
- "BufferHubConsumer.cpp",
- "BufferHubProducer.cpp",
"BufferItem.cpp",
- "BufferItemConsumer.cpp",
"BufferQueue.cpp",
"BufferQueueConsumer.cpp",
"BufferQueueCore.cpp",
"BufferQueueProducer.cpp",
+ "BufferQueueThreadState.cpp",
"BufferSlot.cpp",
- "ConsumerBase.cpp",
- "CpuConsumer.cpp",
- "DisplayEventReceiver.cpp",
"FrameTimestamps.cpp",
- "GLConsumer.cpp",
- "GuiConfig.cpp",
+ "GLConsumerUtils.cpp",
"HdrMetadata.cpp",
- "IDisplayEventConnection.cpp",
"IConsumerListener.cpp",
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
- "ISurfaceComposer.cpp",
- "ISurfaceComposerClient.cpp",
- "LayerDebugInfo.cpp",
- "LayerState.cpp",
"OccupancyTracker.cpp",
- "StreamSplitter.cpp",
- "Surface.cpp",
- "SurfaceControl.cpp",
- "SurfaceComposerClient.cpp",
- "SyncFeatures.cpp",
- "view/Surface.cpp",
"bufferqueue/1.0/B2HProducerListener.cpp",
- "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
+ "bufferqueue/1.0/Conversion.cpp",
+ "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
+ "bufferqueue/1.0/H2BProducerListener.cpp",
+ "bufferqueue/1.0/WProducerListener.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.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",
"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",
+ "liblog",
+ "libnativewindow",
+ "libsync",
+ "libui",
+ "libutils",
+ "libvndksupport",
],
- // bufferhub is not used when building libgui for vendors
- target: {
- vendor: {
- cflags: ["-DNO_BUFFERHUB"],
- exclude_srcs: [
- "BufferHubConsumer.cpp",
- "BufferHubProducer.cpp",
- ],
- exclude_shared_libs: [
- "libbufferhubqueue",
- "libpdx_default_transport",
- ],
- },
- },
-
header_libs: [
- "libdvr_headers",
- "libnativebase_headers",
"libgui_headers",
- "libpdx_headers",
+ "libnativebase_headers",
],
export_shared_lib_headers: [
@@ -167,9 +185,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..528bfb1 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>
@@ -57,7 +58,7 @@
int numDroppedBuffers = 0;
sp<IProducerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
// Check that the consumer doesn't currently have the maximum number of
// buffers acquired. We allow the max buffer count to be exceeded by one
@@ -93,8 +94,6 @@
// Skip this if we're in shared buffer mode and the queue is empty,
// since in that case we'll just return the shared buffer.
if (expectedPresent != 0 && !mCore->mQueue.empty()) {
- const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
-
// The 'expectedPresent' argument indicates when the buffer is expected
// to be presented on-screen. If the buffer's desired present time is
// earlier (less) than expectedPresent -- meaning it will be displayed
@@ -189,6 +188,7 @@
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC),
front->mFrameNumber, maxFrameNumber);
+ ATRACE_NAME("PRESENT_LATER");
return PRESENT_LATER;
}
@@ -202,7 +202,7 @@
if (sharedBufferAvailable && mCore->mQueue.empty()) {
// make sure the buffer has finished allocating before acquiring it
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
slot = mCore->mSharedBufferSlot;
@@ -255,7 +255,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);
@@ -263,7 +263,7 @@
// We might have freed a slot while dropping old buffers, or the producer
// may be blocked waiting for the number of buffers in the queue to
// decrease.
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
@@ -272,7 +272,7 @@
VALIDATE_CONSISTENCY();
}
- if (listener != NULL) {
+ if (listener != nullptr) {
for (int i = 0; i < numDroppedBuffers; ++i) {
listener->onBufferReleased();
}
@@ -285,7 +285,7 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
@@ -311,7 +311,7 @@
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
return NO_ERROR;
@@ -321,15 +321,15 @@
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;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mSharedBufferMode) {
BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode");
@@ -413,7 +413,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;
@@ -421,7 +421,7 @@
sp<IProducerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
@@ -460,12 +460,12 @@
listener = mCore->mConnectedProducerListener;
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBufferReleased();
}
@@ -476,7 +476,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;
}
@@ -484,7 +484,7 @@
BQ_LOGV("connect: controlledByApp=%s",
controlledByApp ? "true" : "false");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("connect: BufferQueue has been abandoned");
@@ -502,31 +502,31 @@
BQ_LOGV("disconnect");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> 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;
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
return NO_ERROR;
}
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;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned");
@@ -568,7 +568,7 @@
BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mDefaultWidth = width;
mCore->mDefaultHeight = height;
return NO_ERROR;
@@ -582,7 +582,7 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("setMaxBufferCount: producer is already connected");
@@ -622,8 +622,8 @@
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
BQ_LOGE("setMaxAcquiredBufferCount: consumer is abandoned");
@@ -673,7 +673,7 @@
}
}
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -683,7 +683,7 @@
status_t BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
BQ_LOGV("setConsumerName: '%s'", name.string());
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
return NO_ERROR;
@@ -692,7 +692,7 @@
status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) {
ATRACE_CALL();
BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mDefaultBufferFormat = defaultFormat;
return NO_ERROR;
}
@@ -701,7 +701,7 @@
android_dataspace defaultDataSpace) {
ATRACE_CALL();
BQ_LOGV("setDefaultBufferDataSpace: %u", defaultDataSpace);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mDefaultBufferDataSpace = defaultDataSpace;
return NO_ERROR;
}
@@ -709,7 +709,7 @@
status_t BufferQueueConsumer::setConsumerUsageBits(uint64_t usage) {
ATRACE_CALL();
BQ_LOGV("setConsumerUsageBits: %#" PRIx64, usage);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerUsageBits = usage;
return NO_ERROR;
}
@@ -717,7 +717,7 @@
status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) {
ATRACE_CALL();
BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerIsProtected = isProtected;
return NO_ERROR;
}
@@ -725,26 +725,26 @@
status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
ATRACE_CALL();
BQ_LOGV("setTransformHint: %#x", hint);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mTransformHint = hint;
return NO_ERROR;
}
status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const {
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
*outStream = mCore->mSidebandStream;
return NO_ERROR;
}
status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
std::vector<OccupancyTracker::Segment>* outHistory) {
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
*outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
return NO_ERROR;
}
status_t BufferQueueConsumer::discardFreeBuffers() {
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->discardFreeBuffersLocked();
return NO_ERROR;
}
@@ -758,21 +758,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..e0e3431 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -73,6 +73,8 @@
mActiveBuffers(),
mDequeueCondition(),
mDequeueBufferCannotBlock(false),
+ mQueueBufferCanDrop(false),
+ mLegacyBufferDrop(true),
mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
mDefaultWidth(1),
mDefaultHeight(1),
@@ -110,13 +112,15 @@
BufferQueueCore::~BufferQueueCore() {}
void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
- Mutex::Autolock lock(mMutex);
+ std::lock_guard<std::mutex> lock(mMutex);
outResult->appendFormat("%s- BufferQueue ", prefix.string());
outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n",
mMaxAcquiredBufferCount, mMaxDequeuedBufferCount);
outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(),
mDequeueBufferCannotBlock, mAsyncMode);
+ outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(),
+ mQueueBufferCanDrop, mLegacyBufferDrop);
outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(),
mDefaultWidth, mDefaultHeight, mDefaultBufferFormat);
outResult->appendFormat("transform-hint=%02x frame-counter=%" PRIu64, mTransformHint,
@@ -306,10 +310,10 @@
return true;
}
-void BufferQueueCore::waitWhileAllocatingLocked() const {
+void BufferQueueCore::waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const {
ATRACE_CALL();
while (mIsAllocating) {
- mIsAllocatingCondition.wait(mMutex);
+ mIsAllocatingCondition.wait(lock);
}
}
@@ -349,7 +353,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 +375,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 +398,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 +422,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 9d7f14c..9c311a3 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>
@@ -58,14 +59,15 @@
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
mCallbackCondition(),
- mDequeueTimeout(-1) {}
+ mDequeueTimeout(-1),
+ mDequeueWaitingForAllocation(false) {}
BufferQueueProducer::~BufferQueueProducer() {}
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
BQ_LOGV("requestBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
@@ -100,8 +102,8 @@
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been "
@@ -162,11 +164,11 @@
if (delta < 0) {
listener = mCore->mConsumerListener;
}
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -179,8 +181,8 @@
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
BQ_LOGE("setAsyncMode: BufferQueue has been abandoned");
@@ -214,14 +216,14 @@
}
mCore->mAsyncMode = async;
VALIDATE_CONSISTENCY();
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
if (delta < 0) {
listener = mCore->mConsumerListener;
}
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
return NO_ERROR;
@@ -246,7 +248,7 @@
}
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
- int* found) const {
+ std::unique_lock<std::mutex>& lock, int* found) const {
auto callerString = (caller == FreeSlotCaller::Dequeue) ?
"dequeueBuffer" : "attachBuffer";
bool tryAgain = true;
@@ -272,8 +274,12 @@
// This check is only done if a buffer has already been queued
if (mCore->mBufferHasBeenQueued &&
dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
- BQ_LOGE("%s: attempting to exceed the max dequeued buffer count "
- "(%d)", callerString, mCore->mMaxDequeuedBufferCount);
+ // Supress error logs when timeout is non-negative.
+ if (mDequeueTimeout < 0) {
+ BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
+ "count (%d)", callerString,
+ mCore->mMaxDequeuedBufferCount);
+ }
return INVALID_OPERATION;
}
@@ -333,13 +339,13 @@
return WOULD_BLOCK;
}
if (mDequeueTimeout >= 0) {
- status_t result = mCore->mDequeueCondition.waitRelative(
- mCore->mMutex, mDequeueTimeout);
- if (result == TIMED_OUT) {
- return result;
+ std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
+ std::chrono::nanoseconds(mDequeueTimeout));
+ if (result == std::cv_status::timeout) {
+ return TIMED_OUT;
}
} else {
- mCore->mDequeueCondition.wait(mCore->mMutex);
+ mCore->mDequeueCondition.wait(lock);
}
}
} // while (tryAgain)
@@ -353,7 +359,7 @@
FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
if (mCore->mIsAbandoned) {
@@ -380,7 +386,16 @@
bool attachedByConsumer = false;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+
+ // If we don't have a free buffer, but we are currently allocating, we wait until allocation
+ // is finished such that we don't allocate in parallel.
+ if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
+ mDequeueWaitingForAllocation = true;
+ mCore->waitWhileAllocatingLocked(lock);
+ mDequeueWaitingForAllocation = false;
+ mDequeueWaitingForAllocationCondition.notify_all();
+ }
if (format == 0) {
format = mCore->mDefaultBufferFormat;
@@ -397,8 +412,7 @@
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
- status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
- &found);
+ status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
if (status != NO_ERROR) {
return status;
}
@@ -449,11 +463,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 +485,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);
@@ -505,7 +519,7 @@
status_t error = graphicBuffer->initCheck();
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
@@ -513,7 +527,7 @@
}
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
if (error != NO_ERROR) {
mCore->mFreeSlots.insert(*outSlot);
@@ -572,7 +586,7 @@
sp<IConsumerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
@@ -607,12 +621,12 @@
mCore->mActiveBuffers.erase(slot);
mCore->mFreeSlots.insert(slot);
mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
listener = mCore->mConsumerListener;
}
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -623,17 +637,17 @@
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;
}
sp<IConsumerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
@@ -651,7 +665,7 @@
return BAD_VALUE;
}
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mFreeBuffers.empty()) {
return NO_MEMORY;
@@ -670,7 +684,7 @@
listener = mCore->mConsumerListener;
}
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -681,15 +695,15 @@
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;
}
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("attachBuffer: BufferQueue has been abandoned");
@@ -713,11 +727,11 @@
return BAD_VALUE;
}
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
status_t returnFlags = NO_ERROR;
int found;
- status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found);
+ status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, lock, &found);
if (status != NO_ERROR) {
return status;
}
@@ -766,7 +780,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;
}
@@ -790,7 +804,7 @@
uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
@@ -872,7 +886,8 @@
item.mFence = acquireFence;
item.mFenceTime = acquireFenceTime;
item.mIsDroppable = mCore->mAsyncMode ||
- mCore->mDequeueBufferCannotBlock ||
+ (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
+ (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
item.mSurfaceDamage = surfaceDamage;
item.mQueuedBuffer = true;
@@ -931,7 +946,7 @@
}
mCore->mBufferHasBeenQueued = true;
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
mCore->mLastQueuedSlot = slot;
output->width = mCore->mDefaultWidth;
@@ -957,9 +972,6 @@
item.mGraphicBuffer.clear();
}
- // Don't send the slot number through the callback since the consumer shouldn't need it
- item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
-
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
@@ -967,14 +979,14 @@
sp<Fence> lastQueuedFence;
{ // scope for the lock
- Mutex::Autolock lock(mCallbackMutex);
+ std::unique_lock<std::mutex> lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
- mCallbackCondition.wait(mCallbackMutex);
+ mCallbackCondition.wait(lock);
}
- if (frameAvailableListener != NULL) {
+ if (frameAvailableListener != nullptr) {
frameAvailableListener->onFrameAvailable(item);
- } else if (frameReplacedListener != NULL) {
+ } else if (frameReplacedListener != nullptr) {
frameReplacedListener->onFrameReplaced(item);
}
@@ -986,7 +998,7 @@
mLastQueuedTransform = item.mTransform;
++mCurrentCallbackTicket;
- mCallbackCondition.broadcast();
+ mCallbackCondition.notify_all();
}
// Update and get FrameEventHistory.
@@ -1014,7 +1026,7 @@
status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
ATRACE_CALL();
BQ_LOGV("cancelBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
@@ -1039,7 +1051,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;
}
@@ -1059,7 +1071,7 @@
}
mSlots[slot].mFence = fence;
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
VALIDATE_CONSISTENCY();
return NO_ERROR;
@@ -1067,9 +1079,9 @@
int BufferQueueProducer::query(int what, int *outValue) {
ATRACE_CALL();
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (outValue == NULL) {
+ if (outValue == nullptr) {
BQ_LOGE("query: outValue was NULL");
return BAD_VALUE;
}
@@ -1135,7 +1147,7 @@
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL();
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
producerControlledByApp ? "true" : "false");
@@ -1145,12 +1157,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 +1200,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,12 +1222,14 @@
status = BAD_VALUE;
break;
}
- mCore->mConnectedPid = IPCThreadState::self()->getCallingPid();
+ mCore->mConnectedPid = BufferQueueThreadState::getCallingPid();
mCore->mBufferHasBeenQueued = false;
mCore->mDequeueBufferCannotBlock = false;
- if (mDequeueTimeout < 0) {
- mCore->mDequeueBufferCannotBlock =
- mCore->mConsumerControlledByApp && producerControlledByApp;
+ mCore->mQueueBufferCanDrop = false;
+ mCore->mLegacyBufferDrop = true;
+ if (mCore->mConsumerControlledByApp && producerControlledByApp) {
+ mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0;
+ mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0;
}
mCore->mAllowAllocation = true;
@@ -1230,16 +1244,16 @@
int status = NO_ERROR;
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> 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;
}
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
// It's not really an error to disconnect after the surface has
@@ -1268,7 +1282,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,12 +1292,12 @@
}
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();
- mCore->mDequeueCondition.broadcast();
+ mCore->mDequeueCondition.notify_all();
listener = mCore->mConsumerListener;
} else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("disconnect: not connected (req=%d)", api);
@@ -1302,7 +1316,7 @@
} // Autolock scope
// Call back without lock held
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onBuffersReleased();
listener->onDisconnect();
}
@@ -1313,12 +1327,12 @@
status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
sp<IConsumerListener> listener;
{ // Autolock scope
- Mutex::Autolock _l(mCore->mMutex);
+ std::lock_guard<std::mutex> _l(mCore->mMutex);
mCore->mSidebandStream = stream;
listener = mCore->mConsumerListener;
} // Autolock scope
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->onSidebandStreamChanged();
}
return NO_ERROR;
@@ -1335,8 +1349,8 @@
uint64_t allocUsage = 0;
std::string allocName;
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
- mCore->waitWhileAllocatingLocked();
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked(lock);
if (!mCore->mAllowAllocation) {
BQ_LOGE("allocateBuffers: allocation is not allowed for this "
@@ -1371,16 +1385,16 @@
if (result != NO_ERROR) {
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
" %u, usage %#" PRIx64 ")", width, height, format, usage);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
return;
}
buffers.push_back(graphicBuffer);
}
{ // Autolock scope
- Mutex::Autolock lock(mCore->mMutex);
+ std::unique_lock<std::mutex> lock(mCore->mMutex);
uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
PixelFormat checkFormat = format != 0 ?
@@ -1391,7 +1405,7 @@
// Something changed while we released the lock. Retry.
BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
continue;
}
@@ -1419,8 +1433,14 @@
}
mCore->mIsAllocating = false;
- mCore->mIsAllocatingCondition.broadcast();
+ mCore->mIsAllocatingCondition.notify_all();
VALIDATE_CONSISTENCY();
+
+ // If dequeue is waiting for to allocate a buffer, release the lock until it's not
+ // waiting anymore so it can use the buffer we just allocated.
+ while (mDequeueWaitingForAllocation) {
+ mDequeueWaitingForAllocationCondition.wait(lock);
+ }
} // Autolock scope
}
}
@@ -1429,7 +1449,7 @@
ATRACE_CALL();
BQ_LOGV("allowAllocation: %s", allow ? "true" : "false");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mAllowAllocation = allow;
return NO_ERROR;
}
@@ -1438,14 +1458,14 @@
ATRACE_CALL();
BQ_LOGV("setGenerationNumber: %u", generationNumber);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mGenerationNumber = generationNumber;
return NO_ERROR;
}
String8 BufferQueueProducer::getConsumerName() const {
ATRACE_CALL();
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
BQ_LOGV("getConsumerName: %s", mConsumerName.string());
return mConsumerName;
}
@@ -1454,7 +1474,7 @@
ATRACE_CALL();
BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (!sharedBufferMode) {
mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
}
@@ -1466,7 +1486,7 @@
ATRACE_CALL();
BQ_LOGV("setAutoRefresh: %d", autoRefresh);
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mAutoRefresh = autoRefresh;
return NO_ERROR;
@@ -1476,8 +1496,10 @@
ATRACE_CALL();
BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
- Mutex::Autolock lock(mCore->mMutex);
- int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ bool dequeueBufferCannotBlock =
+ timeout >= 0 ? false : mCore->mDequeueBufferCannotBlock;
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, dequeueBufferCannotBlock,
mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
if (!mCore->adjustAvailableSlotsLocked(delta)) {
BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of "
@@ -1486,18 +1508,30 @@
}
mDequeueTimeout = timeout;
- mCore->mDequeueBufferCannotBlock = false;
+ mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock;
+ if (timeout > 0) {
+ mCore->mQueueBufferCanDrop = false;
+ }
VALIDATE_CONSISTENCY();
return NO_ERROR;
}
+status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) {
+ ATRACE_CALL();
+ BQ_LOGV("setLegacyBufferDrop: drop = %d", drop);
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ mCore->mLegacyBufferDrop = drop;
+ return NO_ERROR;
+}
+
status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) {
ATRACE_CALL();
BQ_LOGV("getLastQueuedBuffer");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
*outBuffer = nullptr;
*outFence = Fence::NO_FENCE;
@@ -1533,10 +1567,10 @@
BQ_LOGV("addAndGetFrameTimestamps");
sp<IConsumerListener> listener;
{
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
- if (listener != NULL) {
+ if (listener != nullptr) {
listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
}
@@ -1560,7 +1594,7 @@
status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const {
BQ_LOGV("getConsumerUsage");
- Mutex::Autolock lock(mCore->mMutex);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
*outUsage = mCore->mConsumerUsageBits;
return NO_ERROR;
}
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..8d66154 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,109 +741,10 @@
GLC_LOGD("computeCurrentTransformMatrixLocked: "
"mCurrentTextureImage is NULL");
}
- computeTransformMatrix(mCurrentTransformMatrix, buf,
- isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop,
+ computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop,
mCurrentTransform, mFilteringEnabled);
}
-void GLConsumer::computeTransformMatrix(float outTransform[16],
- const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform,
- bool filtering) {
- // Transform matrices
- static const mat4 mtxFlipH(
- -1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 1, 0, 0, 1
- );
- static const mat4 mtxFlipV(
- 1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, 1, 0,
- 0, 1, 0, 1
- );
- static const mat4 mtxRot90(
- 0, 1, 0, 0,
- -1, 0, 0, 0,
- 0, 0, 1, 0,
- 1, 0, 0, 1
- );
-
- mat4 xform;
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
- xform *= mtxFlipH;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
- xform *= mtxFlipV;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- xform *= mtxRot90;
- }
-
- if (!cropRect.isEmpty()) {
- float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
- float bufferWidth = buf->getWidth();
- float bufferHeight = buf->getHeight();
- float shrinkAmount = 0.0f;
- if (filtering) {
- // In order to prevent bilinear sampling beyond the edge of the
- // crop rectangle we may need to shrink it by 2 texels in each
- // dimension. Normally this would just need to take 1/2 a texel
- // off each end, but because the chroma channels of YUV420 images
- // are subsampled we may need to shrink the crop region by a whole
- // texel on each side.
- switch (buf->getPixelFormat()) {
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_RGBX_8888:
- case PIXEL_FORMAT_RGBA_FP16:
- case PIXEL_FORMAT_RGBA_1010102:
- case PIXEL_FORMAT_RGB_888:
- case PIXEL_FORMAT_RGB_565:
- case PIXEL_FORMAT_BGRA_8888:
- // We know there's no subsampling of any channels, so we
- // only need to shrink by a half a pixel.
- shrinkAmount = 0.5;
- break;
-
- default:
- // If we don't recognize the format, we must assume the
- // worst case (that we care about), which is YUV420.
- shrinkAmount = 1.0;
- break;
- }
- }
-
- // Only shrink the dimensions that are not the size of the buffer.
- if (cropRect.width() < bufferWidth) {
- tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
- sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
- bufferWidth;
- }
- if (cropRect.height() < bufferHeight) {
- ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
- bufferHeight;
- sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
- bufferHeight;
- }
-
- mat4 crop(
- sx, 0, 0, 0,
- 0, sy, 0, 0,
- 0, 0, 1, 0,
- tx, ty, 0, 1
- );
- 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.
- xform = mtxFlipV * xform;
-
- memcpy(outTransform, xform.asArray(), sizeof(xform));
-}
-
Rect GLConsumer::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
Rect outCrop = crop;
@@ -938,7 +811,7 @@
}
return (mCurrentTextureImage == nullptr) ?
- NULL : mCurrentTextureImage->graphicBuffer();
+ nullptr : mCurrentTextureImage->graphicBuffer();
}
Rect GLConsumer::getCurrentCrop() const {
@@ -1063,8 +936,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 +949,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 +965,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 +987,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/GLConsumerUtils.cpp b/libs/gui/GLConsumerUtils.cpp
new file mode 100644
index 0000000..7a06c3d
--- /dev/null
+++ b/libs/gui/GLConsumerUtils.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "GLConsumerUtils"
+//#define LOG_NDEBUG 0
+
+#include <gui/GLConsumer.h>
+#include <math/mat4.h>
+#include <system/window.h>
+#include <utils/Log.h>
+
+namespace android {
+
+void GLConsumer::computeTransformMatrix(float outTransform[16],
+ const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform,
+ bool filtering) {
+ // Transform matrices
+ static const mat4 mtxFlipH(
+ -1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 1, 0, 0, 1
+ );
+ static const mat4 mtxFlipV(
+ 1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 1, 0, 1
+ );
+ static const mat4 mtxRot90(
+ 0, 1, 0, 0,
+ -1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 1, 0, 0, 1
+ );
+
+ mat4 xform;
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ xform *= mtxFlipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ xform *= mtxFlipV;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ xform *= mtxRot90;
+ }
+
+ if (!cropRect.isEmpty()) {
+ float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+ float bufferWidth = buf->getWidth();
+ float bufferHeight = buf->getHeight();
+ float shrinkAmount = 0.0f;
+ if (filtering) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
+
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
+ }
+ }
+
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
+ bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
+ bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
+ bufferHeight;
+ }
+
+ mat4 crop(
+ sx, 0, 0, 0,
+ 0, sy, 0, 0,
+ 0, 0, 1, 0,
+ tx, ty, 0, 1
+ );
+ xform = crop * xform;
+ }
+
+ // 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));
+}
+
+}; // namespace android
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 60af8b5..0e03b7d 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 {
@@ -67,6 +72,7 @@
GET_FRAME_TIMESTAMPS,
GET_UNIQUE_ID,
GET_CONSUMER_USAGE,
+ SET_LEGACY_BUFFER_DROP,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -190,10 +196,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 +307,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 {
@@ -432,6 +438,20 @@
return reply.readInt32();
}
+ virtual status_t setLegacyBufferDrop(bool drop) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(drop);
+ status_t result = remote()->transact(SET_LEGACY_BUFFER_DROP,
+ data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override {
Parcel data, reply;
@@ -534,7 +554,9 @@
BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
class HpGraphicBufferProducer : public HpInterface<
- BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+ BpGraphicBufferProducer,
+ H2BGraphicBufferProducerV1_0,
+ H2BGraphicBufferProducerV2_0> {
public:
explicit HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
@@ -630,6 +652,10 @@
return mBase->setDequeueTimeout(timeout);
}
+ status_t setLegacyBufferDrop(bool drop) override {
+ return mBase->setLegacyBufferDrop(drop);
+ }
+
status_t getLastQueuedBuffer(
sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence,
@@ -651,11 +677,17 @@
}
};
-IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
"android.gui.IGraphicBufferProducer");
// ----------------------------------------------------------------------
+status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) {
+ // No-op for IGBP other than BufferQueue.
+ (void) drop;
+ return INVALID_OPERATION;
+}
+
status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
status_t res = OK;
res = parcel->writeUint32(USE_BUFFER_QUEUE);
@@ -738,8 +770,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);
@@ -774,6 +806,10 @@
int result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge,
getTimestamps ? &frameTimestamps : nullptr);
+ if (fence == nullptr) {
+ ALOGE("dequeueBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+ fence = Fence::NO_FENCE;
+ }
reply->writeInt32(buf);
reply->write(*fence);
reply->writeUint64(bufferAge);
@@ -797,12 +833,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);
}
}
@@ -956,6 +992,10 @@
ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
return result;
}
+ if (fence == nullptr) {
+ ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+ fence = Fence::NO_FENCE;
+ }
result = reply->write(*fence);
if (result != NO_ERROR) {
ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
@@ -1003,6 +1043,13 @@
}
return NO_ERROR;
}
+ case SET_LEGACY_BUFFER_DROP: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ bool drop = data.readInt32();
+ int result = setLegacyBufferDrop(drop);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
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 8aaa436..9590df7 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,13 @@
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,
+ const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -92,6 +85,19 @@
}
data.writeUint32(flags);
+ data.writeStrongBinder(applyToken);
+ commands.write(data);
+ data.writeInt64(desiredPresentTime);
+ data.writeStrongBinder(uncacheBuffer.token.promote());
+ data.writeUint64(uncacheBuffer.id);
+
+ if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ data.writeStrongBinder(IInterface::asBinder(listener));
+ data.writeInt64Vector(callbackIds);
+ }
+ }
+
remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
@@ -103,63 +109,94 @@
}
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
+ bool& outCapturedSecureLayers, 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));
data.writeInt32(static_cast<int32_t>(captureSecureLayers));
- status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
-
- if (err != NO_ERROR) {
- return err;
+ 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);
outCapturedSecureLayers = reply.readBool();
- return err;
+ return result;
}
- virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder,
- sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
- float frameScale, bool childrenOnly) {
+ virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
+ sp<GraphicBuffer>* outBuffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeUint64(displayOrLayerStack);
+ status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("captureScreen failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32();
+ if (result != NO_ERROR) {
+ ALOGE("captureScreen failed to readInt32: %d", result);
+ return result;
+ }
+
+ *outDataspace = static_cast<ui::Dataspace>(reply.readInt32());
+ *outBuffer = new GraphicBuffer();
+ reply.read(**outBuffer);
+ return result;
+ }
+
+ virtual status_t captureLayers(
+ const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
+ const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+ const Rect& sourceCrop,
+ const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, 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.writeInt32(excludeLayers.size());
+ for (auto el : excludeLayers) {
+ data.writeStrongBinder(el);
+ }
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(
@@ -281,12 +318,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();
}
@@ -336,34 +386,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;
@@ -376,10 +398,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();
}
@@ -413,6 +451,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());
@@ -461,8 +525,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();
}
@@ -568,6 +640,342 @@
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;
+ }
+
+ virtual status_t notifyPowerHint(int32_t hintId) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+ return error;
+ }
+ error = data.writeInt32(hintId);
+ if (error != NO_ERROR) {
+ ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ if (error != NO_ERROR) {
+ ALOGE("notifyPowerHint: failed to transact: %d", error);
+ return error;
+ }
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -588,14 +996,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);
@@ -603,10 +1003,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;
}
@@ -628,7 +1028,28 @@
}
uint32_t stateFlags = data.readUint32();
- setTransactionState(state, displays, stateFlags);
+ sp<IBinder> applyToken = data.readStrongBinder();
+ InputWindowCommands inputWindowCommands;
+ inputWindowCommands.read(data);
+
+ int64_t desiredPresentTime = data.readInt64();
+
+ client_cache_t uncachedBuffer;
+ uncachedBuffer.token = data.readStrongBinder();
+ uncachedBuffer.id = data.readUint64();
+
+ std::vector<ListenerCallbacks> listenerCallbacks;
+ int32_t listenersSize = data.readInt32();
+ for (int32_t i = 0; i < listenersSize; i++) {
+ auto listener =
+ interface_cast<ITransactionCompletedListener>(data.readStrongBinder());
+ std::vector<CallbackId> callbackIds;
+ data.readInt64Vector(&callbackIds);
+ listenerCallbacks.emplace_back(listener, callbackIds);
+ }
+
+ setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+ desiredPresentTime, uncachedBuffer, listenerCallbacks);
return NO_ERROR;
}
case BOOT_FINISHED: {
@@ -639,21 +1060,23 @@
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());
bool capturedSecureLayers = false;
- status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, sourceCrop, reqWidth,
- reqHeight, minLayerZ, maxLayerZ, useIdentityTransform,
- static_cast<ISurfaceComposer::Rotation>(rotation), captureSecureLayers);
+ status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace,
+ reqPixelFormat, sourceCrop, reqWidth, reqHeight,
+ useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation),
+ captureSecureLayers);
reply->writeInt32(res);
if (res == NO_ERROR) {
@@ -662,17 +1085,41 @@
}
return NO_ERROR;
}
+ case CAPTURE_SCREEN_BY_ID: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ uint64_t displayOrLayerStack = data.readUint64();
+ ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB;
+ sp<GraphicBuffer> outBuffer;
+ status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer);
+ reply->writeInt32(res);
+ if (res == NO_ERROR) {
+ reply->writeInt32(static_cast<int32_t>(outDataspace));
+ reply->write(*outBuffer);
+ }
+ return NO_ERROR;
+ }
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);
+
+ std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
+ int numExcludeHandles = data.readInt32();
+ excludeHandles.reserve(numExcludeHandles);
+ for (int i = 0; i < numExcludeHandles; i++) {
+ excludeHandles.emplace(data.readStrongBinder());
+ }
+
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, excludeHandles, frameScale, childrenOnly);
reply->writeInt32(res);
if (res == NO_ERROR) {
reply->write(*outBuffer);
@@ -727,10 +1174,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;
}
@@ -761,26 +1208,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();
@@ -815,6 +1242,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;
@@ -915,12 +1362,235 @@
}
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);
+ }
+ case NOTIFY_POWER_HINT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t hintId;
+ status_t error = data.readInt32(&hintId);
+ if (error != NO_ERROR) {
+ ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+ return error;
+ }
+ return notifyPowerHint(hintId);
+ }
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..74cd4f1
--- /dev/null
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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->writeInt64Vector(callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ 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->readInt64Vector(&callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ 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& stats : transactionStats) {
+ err = output->writeParcelable(stats);
+ 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;
+ status_t err = input->readParcelable(&stats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ transactionStats.push_back(stats);
+ }
+ return NO_ERROR;
+}
+
+ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+ const std::unordered_set<CallbackId>& callbackIds) {
+ ListenerStats listenerStats;
+ listenerStats.listener = listener;
+ listenerStats.transactionStats.emplace_back(callbackIds);
+
+ 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..42eb921 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,67 @@
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);
+ output.writeBool(hasListenerCallbacks);
+ output.writeStrongBinder(cachedBuffer.token.promote());
+ output.writeUint64(cachedBuffer.id);
+ 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 +117,54 @@
} 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();
+ hasListenerCallbacks = input.readBool();
+ cachedBuffer.token = input.readStrongBinder();
+ cachedBuffer.id = input.readUint64();
+ input.readParcelable(&metadata);
+
+ bgColorAlpha = input.readFloat();
+ bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
+ colorSpaceAgnostic = input.readBool();
+
return NO_ERROR;
}
@@ -166,6 +245,7 @@
}
if (other.what & eLayerChanged) {
what |= eLayerChanged;
+ what &= ~eRelativeLayerChanged;
z = other.z;
}
if (other.what & eSizeChanged) {
@@ -194,19 +274,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;
@@ -224,6 +304,7 @@
}
if (other.what & eRelativeLayerChanged) {
what |= eRelativeLayerChanged;
+ what &= ~eLayerChanged;
z = other.z;
relativeLayerHandle = other.relativeLayerHandle;
}
@@ -234,6 +315,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 & eHasListenerCallbacksChanged) {
+ what |= eHasListenerCallbacksChanged;
+ hasListenerCallbacks = other.hasListenerCallbacks;
+ }
+
+#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..e6eb327 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.
@@ -767,8 +829,9 @@
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
- // Disable transform hint if sticky transform is set.
- if (mStickyTransform == 0) {
+ // Ignore transform hint if sticky transform is set or transform to display inverse flag is
+ // set.
+ if (mStickyTransform == 0 && !transformToDisplayInverse()) {
mTransformHint = output.transformHint;
}
@@ -785,6 +848,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 +1033,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 +1191,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);
@@ -1195,6 +1272,11 @@
return getConsumerUsage(usage);
}
+bool Surface::transformToDisplayInverse() {
+ return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+}
+
int Surface::connect(int api) {
static sp<IProducerListener> listener = new DummyProducerListener();
return connect(api, listener);
@@ -1217,8 +1299,10 @@
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
- // Disable transform hint if sticky transform is set.
- if (mStickyTransform == 0) {
+ // Ignore transform hint if sticky transform is set or transform to display inverse flag is
+ // set. Transform hint should be ignored if the client is expected to always submit buffers
+ // in the same orientation.
+ if (mStickyTransform == 0 && !transformToDisplayInverse()) {
mTransformHint = output.transformHint;
}
@@ -1268,7 +1352,7 @@
ATRACE_CALL();
ALOGV("Surface::detachNextBuffer");
- if (outBuffer == NULL || outFence == NULL) {
+ if (outBuffer == nullptr || outFence == nullptr) {
return BAD_VALUE;
}
@@ -1277,8 +1361,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 +1370,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 +1433,7 @@
ATRACE_CALL();
Rect realRect(Rect::EMPTY_RECT);
- if (rect == NULL || rect->isEmpty()) {
+ if (rect == nullptr || rect->isEmpty()) {
realRect.clear();
} else {
realRect = *rect;
@@ -1515,6 +1599,13 @@
ATRACE_CALL();
ALOGV("Surface::setBuffersTransform");
Mutex::Autolock lock(mMutex);
+ // Ensure NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY is sticky. If the client sets the flag, do not
+ // override it until the surface is disconnected. This is a temporary workaround for camera
+ // until they switch to using Buffer State Layers. Currently if client sets the buffer transform
+ // it may be overriden by the buffer producer when the producer sets the buffer transform.
+ if (transformToDisplayInverse()) {
+ transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ }
mTransform = transform;
return NO_ERROR;
}
@@ -1568,6 +1659,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 +1680,7 @@
void Surface::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- mSlots[i].buffer = 0;
+ mSlots[i].buffer = nullptr;
}
}
@@ -1616,12 +1720,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 +1773,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 +1805,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 +1867,7 @@
status_t Surface::unlockAndPost()
{
- if (mLockedBuffer == 0) {
+ if (mLockedBuffer == nullptr) {
ALOGE("Surface::unlockAndPost failed, no locked buffer");
return INVALID_OPERATION;
}
@@ -1777,7 +1881,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 1002576..d6f88fc 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,231 @@
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& transactionStats : listenerStats.transactionStats) {
+ for (auto callbackId : transactionStats.callbackIds) {
+ auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+ surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+ }
+ }
+
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto callbackId : transactionStats.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);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId);
+
+class BufferCache : public Singleton<BufferCache> {
+public:
+ BufferCache() : token(new BBinder()) {}
+
+ sp<IBinder> getToken() {
+ return IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ }
+
+ status_t getCacheId(const sp<GraphicBuffer>& buffer, uint64_t* cacheId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ auto itr = mBuffers.find(buffer->getId());
+ if (itr == mBuffers.end()) {
+ return BAD_VALUE;
+ }
+ itr->second = getCounter();
+ *cacheId = buffer->getId();
+ return NO_ERROR;
+ }
+
+ uint64_t cache(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mBuffers.size() >= BUFFER_CACHE_MAX_SIZE) {
+ evictLeastRecentlyUsedBuffer();
+ }
+
+ buffer->addDeathCallback(bufferCacheCallback, nullptr);
+
+ mBuffers[buffer->getId()] = getCounter();
+ return buffer->getId();
+ }
+
+ void uncache(uint64_t cacheId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ uncacheLocked(cacheId);
+ }
+
+ void uncacheLocked(uint64_t cacheId) REQUIRES(mMutex) {
+ mBuffers.erase(cacheId);
+ SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+ }
+
+private:
+ void evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+ auto itr = mBuffers.begin();
+ uint64_t minCounter = itr->second;
+ auto minBuffer = itr;
+ itr++;
+
+ while (itr != mBuffers.end()) {
+ uint64_t counter = itr->second;
+ if (counter < minCounter) {
+ minCounter = counter;
+ minBuffer = itr;
+ }
+ itr++;
+ }
+ uncacheLocked(minBuffer->first);
+ }
+
+ uint64_t getCounter() REQUIRES(mMutex) {
+ static uint64_t counter = 0;
+ return counter++;
+ }
+
+ std::mutex mMutex;
+ std::map<uint64_t /*Cache id*/, uint64_t /*counter*/> mBuffers GUARDED_BY(mMutex);
+
+ // Used by ISurfaceComposer to identify which process is sending the cached buffer.
+ sp<IBinder> token;
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache);
+
+void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId) {
+ // GraphicBuffer id's are used as the cache ids.
+ BufferCache::getInstance().uncache(graphicBufferId);
+}
+
+// ---------------------------------------------------------------------------
+
+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 +348,93 @@
}
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();
+
+ mContainsBuffer = other.mContainsBuffer;
+ other.mContainsBuffer = false;
+
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);
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {});
+}
+
+void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+
+ client_cache_t uncacheBuffer;
+ uncacheBuffer.token = BufferCache::getInstance().getToken();
+ uncacheBuffer.id = cacheId;
+
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {});
+}
+
+void SurfaceComposerClient::Transaction::cacheBuffers() {
+ if (!mContainsBuffer) {
+ return;
+ }
+
+ size_t count = 0;
+ for (auto& [sc, cs] : mComposerStates) {
+ layer_state_t* s = getLayerState(sc);
+ if (!(s->what & layer_state_t::eBufferChanged)) {
+ continue;
+ }
+
+ // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
+ // time trying to cache them.
+ if (!s->buffer) {
+ continue;
+ }
+
+ uint64_t cacheId = 0;
+ status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
+ if (ret == NO_ERROR) {
+ s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
+ s->buffer = nullptr;
+ } else {
+ cacheId = BufferCache::getInstance().cache(s->buffer);
+ }
+ s->what |= layer_state_t::eCachedBufferChanged;
+ s->cachedBuffer.token = BufferCache::getInstance().getToken();
+ s->cachedBuffer.id = cacheId;
+
+ // If we have more buffers than the size of the cache, we should stop caching so we don't
+ // evict other buffers in this transaction
+ count++;
+ if (count >= BUFFER_CACHE_MAX_SIZE) {
+ break;
+ }
+ }
+}
+
status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
if (mStatus != NO_ERROR) {
return mStatus;
@@ -137,6 +442,32 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ std::vector<ListenerCallbacks> listenerCallbacks;
+
+ // For every listener with registered callbacks
+ for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
+ auto& [callbackIds, surfaceControls] = callbackInfo;
+ if (callbackIds.empty()) {
+ continue;
+ }
+
+ listenerCallbacks.emplace_back(listener, std::move(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::eHasListenerCallbacksChanged;
+ s->hasListenerCallbacks = true;
+ }
+ }
+ mListenerCallbacks.clear();
+
+ cacheBuffers();
+
Vector<ComposerState> composerStates;
Vector<DisplayState> displayStates;
uint32_t flags = 0;
@@ -166,7 +497,12 @@
mAnimation = false;
mEarlyWakeup = false;
- sf->setTransactionState(composerStates, displayStates, flags);
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
+ mDesiredPresentTime,
+ {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
+ listenerCallbacks);
+ mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
}
@@ -182,8 +518,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 +554,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 +573,8 @@
s->what |= layer_state_t::ePositionChanged;
s->x = x;
s->y = y;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -240,9 +599,7 @@
s->w = w;
s->h = h;
- // Resizing a surface makes the transaction synchronous.
- mForceSynchronous = true;
-
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -254,7 +611,10 @@
return *this;
}
s->what |= layer_state_t::eLayerChanged;
+ s->what &= ~layer_state_t::eRelativeLayerChanged;
s->z = z;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -265,8 +625,11 @@
mStatus = BAD_INDEX;
}
s->what |= layer_state_t::eRelativeLayerChanged;
+ s->what &= ~layer_state_t::eLayerChanged;
s->relativeLayerHandle = relativeTo;
s->z = z;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -286,6 +649,8 @@
s->flags &= ~mask;
s->flags |= (flags & mask);
s->mask |= mask;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -299,6 +664,8 @@
}
s->what |= layer_state_t::eTransparentRegionChanged;
s->transparentRegion = transparentRegion;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -311,6 +678,8 @@
}
s->what |= layer_state_t::eAlphaChanged;
s->alpha = alpha;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -323,6 +692,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 +726,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 +801,8 @@
}
s->what |= layer_state_t::eReparentChildren;
s->reparentHandle = newParentHandle;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -418,6 +816,8 @@
}
s->what |= layer_state_t::eReparent;
s->parentHandleForChild = newParentHandle;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -431,6 +831,219 @@
}
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;
+ }
+ s->what |= layer_state_t::eBufferChanged;
+ s->buffer = buffer;
+
+ registerSurfaceControlForCallback(sc);
+
+ mContainsBuffer = true;
+ 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 +1054,8 @@
mStatus = BAD_INDEX;
}
s->what |= layer_state_t::eDetachChildren;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -468,6 +1083,8 @@
s->what |= layer_state_t::eOverrideScalingModeChanged;
s->overrideScalingMode = overrideScalingMode;
+
+ registerSurfaceControlForCallback(sc);
return *this;
}
@@ -479,17 +1096,104 @@
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 sourceWidth = source.getWidth();
+ float sourceHeight = source.getHeight();
+
+ float xScale = sourceWidth < 0 ? 1.0f : dst.getWidth() / sourceWidth;
+ float yScale = sourceHeight < 0 ? 1.0f : dst.getHeight() / sourceHeight;
+ 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 +1263,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 +1270,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 +1303,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 +1357,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 +1368,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 +1419,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 +1427,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 +1463,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,49 +1491,121 @@
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 SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
+ return ComposerService::getComposerService()->notifyPowerHint(hintId);
+}
+
// ----------------------------------------------------------------------------
-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,
- bool captureSecureLayers, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers) {
+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, bool& outCapturedSecureLayers) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
- if (s == NULL) return NO_INIT;
- status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, sourceCrop,
- reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform,
- static_cast<ISurfaceComposer::Rotation>(rotation),
- captureSecureLayers);
+ if (s == nullptr) return NO_INIT;
+ status_t ret =
+ s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
+ reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation),
+ captureSecureLayers);
if (ret != NO_ERROR) {
return ret;
}
return ret;
}
-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, sp<GraphicBuffer>* outBuffer) {
bool ignored;
- return capture(display, sourceCrop, reqWidth, reqHeight,
- minLayerZ, maxLayerZ, useIdentityTransform, rotation, false, outBuffer, ignored);
+ return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight,
+ useIdentityTransform, rotation, false, outBuffer, ignored);
}
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop,
+status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
+ sp<GraphicBuffer>* outBuffer) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == nullptr) return NO_INIT;
+ return s->captureScreen(displayOrLayerStack, outDataspace, 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,
- float frameScale, sp<GraphicBuffer>* outBuffer) {
+status_t ScreenshotClient::captureChildLayers(
+ const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
+ 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,
+ excludeHandles, 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/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
new file mode 100644
index 0000000..5cb3593
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -0,0 +1,1542 @@
+/*
+ * 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/bufferqueue/1.0/Conversion.h>
+
+namespace android {
+namespace conversion {
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+native_handle_t* native_handle_create_from_fd(int fd) {
+ if (fd < 2) {
+ return native_handle_create(0, 0);
+ }
+ native_handle_t* nh = native_handle_create(1, 0);
+ if (nh == nullptr) {
+ return nullptr;
+ }
+ nh->data[0] = fd;
+ return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+int native_handle_read_fd(native_handle_t const* nh, int index) {
+ return ((nh == nullptr) || (nh->numFds == 0) ||
+ (nh->numFds <= index) || (index < 0)) ?
+ -1 : nh->data[index];
+}
+
+/**
+ * Conversion functions
+ * ====================
+ *
+ * There are two main directions of conversion:
+ * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the
+ * input. The wrapper has type `TargetType`.
+ * - `toTargetType(...)`: Create a standalone object of type `TargetType` that
+ * corresponds to the input. The lifetime of the output does not depend on the
+ * lifetime of the input.
+ * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType`
+ * that cannot be copied and/or moved efficiently, or when there are multiple
+ * output arguments.
+ * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for
+ * `TargetType` that cannot be copied and/or moved efficiently, or when there
+ * are multiple output arguments.
+ *
+ * `wrapIn()` and `convertTo()` functions will take output arguments before
+ * input arguments. Some of these functions might return a value to indicate
+ * success or error.
+ *
+ * In converting or wrapping something as a Treble type that contains a
+ * `hidl_handle`, `native_handle_t*` will need to be created and returned as
+ * an additional output argument, hence only `wrapIn()` or `convertTo()` would
+ * be available. The caller must call `native_handle_delete()` to deallocate the
+ * returned native handle when it is no longer needed.
+ *
+ * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do
+ * not perform duplication of file descriptors, while `toTargetType()` and
+ * `convertTo()` do.
+ */
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+status_t toStatusT(Return<void> const& t) {
+ return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR);
+}
+
+/**
+ * \brief Convert `Return<void>` to `binder::Status`.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `binder::Status`.
+ */
+// convert: Return<void> -> ::android::binder::Status
+::android::binder::Status toBinderStatus(
+ Return<void> const& t) {
+ return ::android::binder::Status::fromExceptionCode(
+ toStatusT(t),
+ t.description().c_str());
+}
+
+/**
+ * \brief Wrap `native_handle_t*` in `hidl_handle`.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \return The `hidl_handle` that points to \p nh.
+ */
+// wrap: native_handle_t* -> hidl_handle
+hidl_handle inHidlHandle(native_handle_t const* nh) {
+ return hidl_handle(nh);
+}
+
+/**
+ * \brief Convert `int32_t` to `Dataspace`.
+ *
+ * \param[in] l The source `int32_t`.
+ * \result The corresponding `Dataspace`.
+ */
+// convert: int32_t -> Dataspace
+Dataspace toHardwareDataspace(int32_t l) {
+ return static_cast<Dataspace>(l);
+}
+
+/**
+ * \brief Convert `Dataspace` to `int32_t`.
+ *
+ * \param[in] t The source `Dataspace`.
+ * \result The corresponding `int32_t`.
+ */
+// convert: Dataspace -> int32_t
+int32_t toRawDataspace(Dataspace const& t) {
+ return static_cast<int32_t>(t);
+}
+
+/**
+ * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that points to the buffer.
+ */
+// wrap: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) {
+ hidl_vec<uint8_t> t;
+ t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false);
+ return t;
+}
+
+/**
+ * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer.
+ */
+// convert: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size) {
+ hidl_vec<uint8_t> t;
+ t.resize(size);
+ uint8_t const* src = static_cast<uint8_t const*>(l);
+ std::copy(src, src + size, t.data());
+ return t;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+ t->attr.width = l.getWidth();
+ t->attr.height = l.getHeight();
+ t->attr.stride = l.getStride();
+ t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+ t->attr.layerCount = l.getLayerCount();
+ t->attr.usage = static_cast<uint32_t>(l.getUsage());
+ t->attr.id = l.getId();
+ t->attr.generationNumber = l.getGenerationNumber();
+ t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+ native_handle_t* handle = t.nativeHandle == nullptr ?
+ nullptr : native_handle_clone(t.nativeHandle);
+
+ size_t const numInts = 12 + static_cast<size_t>(handle ? handle->numInts : 0);
+ int32_t* ints = new int32_t[numInts];
+
+ size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+ int* fds = new int[numFds];
+
+ ints[0] = 'GBFR';
+ ints[1] = static_cast<int32_t>(t.attr.width);
+ ints[2] = static_cast<int32_t>(t.attr.height);
+ ints[3] = static_cast<int32_t>(t.attr.stride);
+ ints[4] = static_cast<int32_t>(t.attr.format);
+ ints[5] = static_cast<int32_t>(t.attr.layerCount);
+ ints[6] = static_cast<int32_t>(t.attr.usage);
+ ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+ ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+ ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+ ints[10] = 0;
+ ints[11] = 0;
+ if (handle) {
+ ints[10] = static_cast<int32_t>(handle->numFds);
+ ints[11] = static_cast<int32_t>(handle->numInts);
+ int* intsStart = handle->data + handle->numFds;
+ std::copy(handle->data, intsStart, fds);
+ std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+ }
+
+ void const* constBuffer = static_cast<void const*>(ints);
+ size_t size = numInts * sizeof(int32_t);
+ int const* constFds = static_cast<int const*>(fds);
+ status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+ delete [] fds;
+ delete [] ints;
+ native_handle_delete(handle);
+ return status == NO_ERROR;
+}
+
+/**
+ * Conversion functions for types outside media
+ * ============================================
+ *
+ * Some objects in libui and libgui that were made to go through binder calls do
+ * not expose ways to read or write their fields to the public. To pass an
+ * object of this kind through the HIDL boundary, translation functions need to
+ * work around the access restriction by using the publicly available
+ * `flatten()` and `unflatten()` functions.
+ *
+ * All `flatten()` and `unflatten()` overloads follow the same convention as
+ * follows:
+ *
+ * status_t flatten(ObjectType const& object,
+ * [OtherType const& other, ...]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * status_t unflatten(ObjectType* object,
+ * [OtherType* other, ...,]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * The number of `other` parameters varies depending on the `ObjectType`. For
+ * example, in the process of unflattening an object that contains
+ * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will
+ * be created.
+ *
+ * The last four parameters always work the same way in all overloads of
+ * `flatten()` and `unflatten()`:
+ * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled,
+ * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`,
+ * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the
+ * size (in ints) of the fd buffer pointed to by `fds`.
+ * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read
+ * from, `size` is the size (in bytes) of the non-fd buffer pointed to by
+ * `buffer`, `fds` is the pointer to the fd buffer to be read from, and
+ * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`.
+ * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds`
+ * will be advanced, while `size` and `numFds` will be decreased to reflect
+ * how much storage/data of the two buffers (fd and non-fd) have been used.
+ * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and
+ * `numFds` are invalid.
+ *
+ * The return value of a successful `flatten()` or `unflatten()` call will be
+ * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure.
+ *
+ * For each object type that supports flattening, there will be two accompanying
+ * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will
+ * return the size of the non-fd buffer that the object will need for
+ * flattening. `getFdCount()` will return the size of the fd buffer that the
+ * object will need for flattening.
+ *
+ * The set of these four functions, `getFlattenedSize()`, `getFdCount()`,
+ * `flatten()` and `unflatten()`, are similar to functions of the same name in
+ * the abstract class `Flattenable`. The only difference is that functions in
+ * this file are not member functions of the object type. For example, we write
+ *
+ * flatten(x, buffer, size, fds, numFds)
+ *
+ * instead of
+ *
+ * x.flatten(buffer, size, fds, numFds)
+ *
+ * because we cannot modify the type of `x`.
+ *
+ * There is one exception to the naming convention: `hidl_handle` that
+ * represents a fence. The four functions for this "Fence" type have the word
+ * "Fence" attched to their names because the object type, which is
+ * `hidl_handle`, does not carry the special meaning that the object itself can
+ * only contain zero or one file descriptor.
+ */
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+ return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+size_t getFenceFdCount(hidl_handle const& fence) {
+ return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < 4) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numFdsInHandle;
+ FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+ if (numFdsInHandle > 1) {
+ return BAD_VALUE;
+ }
+
+ if (numFds < numFdsInHandle) {
+ return NO_MEMORY;
+ }
+
+ if (numFdsInHandle) {
+ *nh = native_handle_create_from_fd(*fds);
+ if (*nh == nullptr) {
+ return NO_MEMORY;
+ }
+ *fence = *nh;
+ ++fds;
+ --numFds;
+ } else {
+ *nh = nullptr;
+ *fence = hidl_handle();
+ }
+
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] t The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flattenFence(hidl_handle const& fence,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFenceFlattenedSize(fence) ||
+ numFds < getFenceFdCount(fence)) {
+ return NO_MEMORY;
+ }
+ // Cast to uint32_t since the size of a size_t can vary between 32- and
+ // 64-bit processes
+ FlattenableUtils::write(buffer, size,
+ static_cast<uint32_t>(getFenceFdCount(fence)));
+ int fd = native_handle_read_fd(fence);
+ if (fd != -1) {
+ *fds = fd;
+ ++fds;
+ --numFds;
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+ != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+bool convertTo(Fence* l, hidl_handle const& t) {
+ int fd = native_handle_read_fd(t);
+ if (fd != -1) {
+ fd = dup(fd);
+ if (fd == -1) {
+ return false;
+ }
+ }
+ native_handle_t* nh = native_handle_create_from_fd(fd);
+ if (nh == nullptr) {
+ if (fd != -1) {
+ close(fd);
+ }
+ return false;
+ }
+
+ size_t const baseSize = getFenceFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ size_t const baseNumFds = getFenceFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+ native_handle_delete(nh);
+ return false;
+ }
+ native_handle_delete(nh);
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ constexpr size_t min = sizeof(t.state);
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ return min;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ return min + getFenceFlattenedSize(t.fence);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ return min + sizeof(
+ ::android::FenceTime::Snapshot::signalTime);
+ }
+ return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ return t.state ==
+ HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+ getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`.
+ */
+status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::EMPTY);
+ return NO_ERROR;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::FENCE);
+ return flattenFence(t.fence, buffer, size, fds, numFds);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+ FlattenableUtils::write(buffer, size, t.signalTimeNs);
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Unflatten `FenceTimeSnapshot`.
+ *
+ * \param[out] t The destination `FenceTimeSnapshot`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and the constructed snapshot contains a
+ * file descriptor, \p nh will be created to hold that file descriptor. In this
+ * case, \p nh needs to be deleted with `native_handle_delete()` afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < sizeof(t->state)) {
+ return NO_MEMORY;
+ }
+
+ *nh = nullptr;
+ ::android::FenceTime::Snapshot::State state;
+ FlattenableUtils::read(buffer, size, state);
+ switch (state) {
+ case ::android::FenceTime::Snapshot::State::EMPTY:
+ t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY;
+ return NO_ERROR;
+ case ::android::FenceTime::Snapshot::State::FENCE:
+ t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE;
+ return unflattenFence(&t->fence, nh, buffer, size, fds, numFds);
+ case ::android::FenceTime::Snapshot::State::SIGNAL_TIME:
+ t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME;
+ if (size < sizeof(t->signalTimeNs)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, t->signalTimeNs);
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+ return sizeof(uint64_t) + // mFrameNumber
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(nsecs_t) + // mPostedTime
+ sizeof(nsecs_t) + // mRequestedPresentTime
+ sizeof(nsecs_t) + // mLatchTime
+ sizeof(nsecs_t) + // mFirstRefreshStartTime
+ sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return minFlattenedSize(t) +
+ getFlattenedSize(t.gpuCompositionDoneFence) +
+ getFlattenedSize(t.displayPresentFence) +
+ getFlattenedSize(t.displayRetireFence) +
+ getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return getFdCount(t.gpuCompositionDoneFence) +
+ getFdCount(t.displayPresentFence) +
+ getFdCount(t.displayRetireFence) +
+ getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Unflatten `FrameEventsDelta`.
+ *
+ * \param[out] t The destination `FrameEventsDelta`.
+ * \param[out] nh The underlying array of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be
+ * populated with `nullptr` or newly created handles. Each non-null slot in \p
+ * nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t,
+ std::vector<native_handle_t*>* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < minFlattenedSize(*t)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, t->frameNumber);
+
+ // These were written as uint8_t for alignment.
+ uint8_t temp = 0;
+ FlattenableUtils::read(buffer, size, temp);
+ size_t index = static_cast<size_t>(temp);
+ if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ t->index = static_cast<uint32_t>(index);
+
+ FlattenableUtils::read(buffer, size, temp);
+ t->addPostCompositeCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ t->addRetireCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ t->addReleaseCalled = static_cast<bool>(temp);
+
+ FlattenableUtils::read(buffer, size, t->postedTimeNs);
+ FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs);
+ FlattenableUtils::read(buffer, size, t->latchTimeNs);
+ FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs);
+ FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs);
+ FlattenableUtils::read(buffer, size, t->dequeueReadyTime);
+
+ // Fences
+ HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4];
+ tSnapshot[0] = &t->gpuCompositionDoneFence;
+ tSnapshot[1] = &t->displayPresentFence;
+ tSnapshot[2] = &t->displayRetireFence;
+ tSnapshot[3] = &t->releaseFence;
+ nh->resize(4);
+ for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+ status_t status = unflatten(
+ tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ while (snapshotIndex > 0) {
+ --snapshotIndex;
+ if ((*nh)[snapshotIndex] != nullptr) {
+ native_handle_delete((*nh)[snapshotIndex]);
+ }
+ }
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+// FrameEventsDelta::flatten
+status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t numFds) {
+ // Check that t.index is within a valid range.
+ if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+ || t.index > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, t.frameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, t.postedTimeNs);
+ FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+ FlattenableUtils::write(buffer, size, t.latchTimeNs);
+ FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+ // Fences
+ HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+ tSnapshot[0] = &t.gpuCompositionDoneFence;
+ tSnapshot[1] = &t.displayPresentFence;
+ tSnapshot[2] = &t.displayRetireFence;
+ tSnapshot[3] = &t.releaseFence;
+ for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+ status_t status = flatten(
+ *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t size = 4 + // mDeltas.size()
+ sizeof(t.compositorTiming);
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ size += getFlattenedSize(t.deltas[i]);
+ }
+ return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t numFds = 0;
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ numFds += getFdCount(t.deltas[i]);
+ }
+ return numFds;
+}
+
+/**
+ * \brief Unflatten `FrameEventHistoryDelta`.
+ *
+ * \param[out] t The destination `FrameEventHistoryDelta`.
+ * \param[out] nh The underlying array of arrays of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or
+ * newly created handles. The second dimension of \p nh will be 4. Each non-null
+ * slot in \p nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < 4) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, t->compositorTiming);
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (static_cast<size_t>(deltaCount) >
+ ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ t->deltas.resize(deltaCount);
+ nh->resize(deltaCount);
+ for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) {
+ status_t status = unflatten(
+ &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+status_t flatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+ FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+ for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+ status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `::android::FrameEventHistoryData` in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `::android::FrameEventHistoryDelta`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ ::android::FrameEventHistoryDelta const& l) {
+
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+bool convertTo(
+ ::android::FrameEventHistoryDelta* l,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = getFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Return the size of the buffer required to flatten `Region`.
+ *
+ * \param[in] t The input `Region`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(Region const& t) {
+ return sizeof(uint32_t) + t.size() * sizeof(::android::Rect);
+}
+
+/**
+ * \brief Unflatten `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t unflatten(Region* t, void const*& buffer, size_t& size) {
+ if (size < sizeof(uint32_t)) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numRects = 0;
+ FlattenableUtils::read(buffer, size, numRects);
+ if (size < numRects * sizeof(Rect)) {
+ return NO_MEMORY;
+ }
+ if (numRects > (UINT32_MAX / sizeof(Rect))) {
+ return NO_MEMORY;
+ }
+
+ t->resize(numRects);
+ for (size_t r = 0; r < numRects; ++r) {
+ ::android::Rect rect(::android::Rect::EMPTY_RECT);
+ status_t status = rect.unflatten(buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::advance(buffer, size, sizeof(rect));
+ (*t)[r] = Rect{
+ static_cast<int32_t>(rect.left),
+ static_cast<int32_t>(rect.top),
+ static_cast<int32_t>(rect.right),
+ static_cast<int32_t>(rect.bottom)};
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `Region`.
+ *
+ * \param[in] t The source `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flatten(Region const& t, void*& buffer, size_t& size) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.size()));
+ for (size_t r = 0; r < t.size(); ++r) {
+ ::android::Rect rect(
+ static_cast<int32_t>(t[r].left),
+ static_cast<int32_t>(t[r].top),
+ static_cast<int32_t>(t[r].right),
+ static_cast<int32_t>(t[r].bottom));
+ status_t status = rect.flatten(buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::advance(buffer, size, sizeof(rect));
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Convert `::android::Region` to `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in] l The source `::android::Region`.
+ */
+// convert: ::android::Region -> Region
+bool convertTo(Region* t, ::android::Region const& l) {
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ if (l.flatten(buffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ if (unflatten(t, constBuffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `Region` to `::android::Region`.
+ *
+ * \param[out] l The destination `::android::Region`.
+ * \param[in] t The source `Region`.
+ */
+// convert: Region -> ::android::Region
+bool convertTo(::android::Region* l, Region const& t) {
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ if (flatten(t, buffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ if (l->unflatten(constBuffer, size) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+ return sizeof(int64_t) + // timestamp
+ sizeof(int) + // isAutoTimestamp
+ sizeof(android_dataspace) + // dataSpace
+ sizeof(::android::Rect) + // crop
+ sizeof(int) + // scalingMode
+ sizeof(uint32_t) + // transform
+ sizeof(uint32_t) + // stickyTransform
+ sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Return the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) {
+ return minFlattenedSize(t) +
+ getFenceFlattenedSize(t.fence) +
+ getFlattenedSize(t.surfaceDamage) +
+ sizeof(HdrMetadata::validTypes);
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::QueueBufferInput const& t) {
+ return getFenceFdCount(t.fence);
+}
+
+/**
+ * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The native handle cloned from `t.fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence`. */
+status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t,
+ native_handle_t** nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, t.timestamp);
+ FlattenableUtils::write(buffer, size, static_cast<int>(t.isAutoTimestamp));
+ FlattenableUtils::write(buffer, size,
+ static_cast<android_dataspace_t>(t.dataSpace));
+ FlattenableUtils::write(buffer, size, ::android::Rect(
+ static_cast<int32_t>(t.crop.left),
+ static_cast<int32_t>(t.crop.top),
+ static_cast<int32_t>(t.crop.right),
+ static_cast<int32_t>(t.crop.bottom)));
+ FlattenableUtils::write(buffer, size, static_cast<int>(t.scalingMode));
+ FlattenableUtils::write(buffer, size, t.transform);
+ FlattenableUtils::write(buffer, size, t.stickyTransform);
+ FlattenableUtils::write(buffer, size, t.getFrameTimestamps);
+
+ *nh = t.fence.getNativeHandle() == nullptr ?
+ nullptr : native_handle_clone(t.fence);
+ status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = flatten(t.surfaceDamage, buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0));
+ return NO_ERROR;
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < minFlattenedSize(*t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, t->timestamp);
+ int lIsAutoTimestamp;
+ FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+ t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+ android_dataspace_t lDataSpace;
+ FlattenableUtils::read(buffer, size, lDataSpace);
+ t->dataSpace = static_cast<Dataspace>(lDataSpace);
+ Rect lCrop;
+ FlattenableUtils::read(buffer, size, lCrop);
+ t->crop = Rect{
+ static_cast<int32_t>(lCrop.left),
+ static_cast<int32_t>(lCrop.top),
+ static_cast<int32_t>(lCrop.right),
+ static_cast<int32_t>(lCrop.bottom)};
+ int lScalingMode;
+ FlattenableUtils::read(buffer, size, lScalingMode);
+ t->scalingMode = static_cast<int32_t>(lScalingMode);
+ FlattenableUtils::read(buffer, size, t->transform);
+ FlattenableUtils::read(buffer, size, t->stickyTransform);
+ FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+ status_t status = unflattenFence(&(t->fence), nh,
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ // HdrMetadata ignored
+ return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+bool wrapAs(
+ HGraphicBufferProducer::QueueBufferInput* t,
+ native_handle_t** nh,
+ BGraphicBufferProducer::QueueBufferInput const& l) {
+
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
+ * `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If `t.fence` has a valid file descriptor, it will be duplicated.
+ */
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferInput* l,
+ HGraphicBufferProducer::QueueBufferInput const& t) {
+
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = getFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ native_handle_t* nh;
+ if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ if (nh != nullptr) {
+ native_handle_close(nh);
+ native_handle_delete(nh);
+ }
+ return false;
+ }
+
+ native_handle_delete(nh);
+ return true;
+}
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+// wrap: BGraphicBufferProducer::QueueBufferOutput ->
+// HGraphicBufferProducer::QueueBufferOutput
+bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ BGraphicBufferProducer::QueueBufferOutput const& l) {
+ if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) {
+ return false;
+ }
+ t->width = l.width;
+ t->height = l.height;
+ t->transformHint = l.transformHint;
+ t->numPendingBuffers = l.numPendingBuffers;
+ t->nextFrameNumber = l.nextFrameNumber;
+ t->bufferReplaced = l.bufferReplaced;
+ return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// BGraphicBufferProducer::QueueBufferOutput
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferOutput* l,
+ HGraphicBufferProducer::QueueBufferOutput const& t) {
+ if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+ return false;
+ }
+ l->width = t.width;
+ l->height = t.height;
+ l->transformHint = t.transformHint;
+ l->numPendingBuffers = t.numPendingBuffers;
+ l->nextFrameNumber = t.nextFrameNumber;
+ l->bufferReplaced = t.bufferReplaced;
+ return true;
+}
+
+/**
+ * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode(
+ BGraphicBufferProducer::DisconnectMode l) {
+ switch (l) {
+ case BGraphicBufferProducer::DisconnectMode::Api:
+ return HGraphicBufferProducer::DisconnectMode::API;
+ case BGraphicBufferProducer::DisconnectMode::AllLocal:
+ return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+ }
+ return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
+ * `BGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `BGraphicBufferProducer::DisconnectMode`.
+ */
+BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
+ HGraphicBufferProducer::DisconnectMode t) {
+ switch (t) {
+ case HGraphicBufferProducer::DisconnectMode::API:
+ return BGraphicBufferProducer::DisconnectMode::Api;
+ case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL:
+ return BGraphicBufferProducer::DisconnectMode::AllLocal;
+ }
+ return BGraphicBufferProducer::DisconnectMode::Api;
+}
+
+} // namespace conversion
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index ee88c23..cee1b81 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/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
new file mode 100644
index 0000000..78dc4e8
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/bufferqueue/1.0/WProducerListener.h>
+
+namespace android {
+
+// TWProducerListener
+TWProducerListener::TWProducerListener(
+ sp<BProducerListener> const& base):
+ mBase(base) {
+}
+
+Return<void> TWProducerListener::onBufferReleased() {
+ mBase->onBufferReleased();
+ return Void();
+}
+
+Return<bool> TWProducerListener::needsReleaseNotify() {
+ return mBase->needsReleaseNotify();
+}
+
+// LWProducerListener
+LWProducerListener::LWProducerListener(
+ sp<HProducerListener> const& base):
+ mBase(base) {
+}
+
+void LWProducerListener::onBufferReleased() {
+ mBase->onBufferReleased();
+}
+
+bool LWProducerListener::needsReleaseNotify() {
+ return static_cast<bool>(mBase->needsReleaseNotify());
+}
+
+} // 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..e891ec5
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -0,0 +1,339 @@
+/*
+ * 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 "B2HGraphicBufferProducer@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 {
+
+namespace /* unnamed */ {
+
+using BQueueBufferInput = ::android::
+ IGraphicBufferProducer::QueueBufferInput;
+using HQueueBufferInput = ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer::QueueBufferInput;
+using BQueueBufferOutput = ::android::
+ IGraphicBufferProducer::QueueBufferOutput;
+using HQueueBufferOutput = ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer::QueueBufferOutput;
+
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b;
+
+bool b2h(BQueueBufferOutput const& from, HQueueBufferOutput* to) {
+ to->width = from.width;
+ to->height = from.height;
+ to->transformHint = static_cast<int32_t>(from.transformHint);
+ to->numPendingBuffers = from.numPendingBuffers;
+ to->nextFrameNumber = from.nextFrameNumber;
+ to->bufferReplaced = from.bufferReplaced;
+ return true;
+}
+
+} // unnamed namespace
+
+// 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) {
+ BQueueBufferInput 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, QueueBufferOutput{});
+ return {};
+ }
+
+ // Convert surfaceDamage.
+ if (!h2b(hInput.surfaceDamage, &bInput.surfaceDamage)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{});
+ return {};
+ }
+
+ // Convert fence.
+ if (!h2b(hInput.fence, &bInput.fence)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{});
+ return {};
+ }
+
+ BQueueBufferOutput bOutput{};
+ HStatus hStatus{};
+ QueueBufferOutput hOutput{};
+ bool converted =
+ b2h(
+ mBase->queueBuffer(static_cast<int>(slot), bInput, &bOutput),
+ &hStatus) &&
+ b2h(bOutput, &hOutput);
+
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput);
+ 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) {
+ sp<BProducerListener> bListener = new H2BProducerListener(hListener);
+ int bConnectionType{};
+ if (!bListener || !h2b(hConnectionType, &bConnectionType)) {
+ _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{});
+ return {};
+ }
+ BQueueBufferOutput bOutput{};
+ HStatus hStatus{};
+ QueueBufferOutput hOutput{};
+ bool converted =
+ b2h(mBase->connect(bListener,
+ bConnectionType,
+ producerControlledByApp,
+ &bOutput),
+ &hStatus) &&
+ b2h(bOutput, &hOutput);
+ _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput);
+ 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..2f5b73c
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,506 @@
+/*
+ * 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 {
+
+namespace /* unnamed */ {
+
+using BQueueBufferInput = ::android::
+ IGraphicBufferProducer::QueueBufferInput;
+using HQueueBufferInput = ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer::QueueBufferInput;
+using BQueueBufferOutput = ::android::
+ IGraphicBufferProducer::QueueBufferOutput;
+using HQueueBufferOutput = ::android::hardware::graphics::bufferqueue::V2_0::
+ IGraphicBufferProducer::QueueBufferOutput;
+
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b;
+
+bool b2h(BQueueBufferInput const& from, HQueueBufferInput* to,
+ HFenceWrapper* hFenceWrapper) {
+ to->timestamp = from.timestamp;
+ to->isAutoTimestamp = static_cast<bool>(from.isAutoTimestamp);
+ to->dataSpace = static_cast<int32_t>(from.dataSpace);
+ to->transform = static_cast<int32_t>(from.transform);
+ to->stickyTransform = static_cast<int32_t>(from.stickyTransform);
+ if (!b2h(from.crop, &to->crop) ||
+ !b2h(from.surfaceDamage, &to->surfaceDamage) ||
+ !b2h(from.fence, hFenceWrapper)) {
+ return false;
+ }
+ to->fence = hFenceWrapper->getHandle();
+ return true;
+}
+
+bool h2b(HQueueBufferOutput const& from, BQueueBufferOutput* to) {
+ to->width = from.width;
+ to->height = from.height;
+ to->transformHint = static_cast<uint32_t>(from.transformHint);
+ to->numPendingBuffers = from.numPendingBuffers;
+ to->nextFrameNumber = from.nextFrameNumber;
+ to->bufferReplaced = from.bufferReplaced;
+ return true;
+}
+
+} // unnamed namespace
+
+// 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) {
+ HQueueBufferInput hInput{};
+ HFenceWrapper hFenceWrapper;
+ if (!b2h(input, &hInput, &hFenceWrapper)) {
+ LOG(ERROR) << "queueBuffer: corrupted input.";
+ return UNKNOWN_ERROR;
+ }
+
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->queueBuffer(
+ static_cast<int32_t>(slot),
+ hInput,
+ [&converted, &bStatus, output](
+ HStatus hStatus,
+ HQueueBufferOutput const& hOutput) {
+ converted = h2b(hStatus, &bStatus) && h2b(hOutput, output);
+ });
+
+ 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;
+ }
+ }
+
+ bool converted{};
+ status_t bStatus{};
+ Return<void> transResult = mBase->connect(
+ hListener,
+ hConnectionType,
+ producerControlledByApp,
+ [&converted, &bStatus, output](
+ HStatus hStatus,
+ HQueueBufferOutput const& hOutput) {
+ converted = h2b(hStatus, &bStatus) && h2b(hOutput, output);
+ });
+ 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..cbd6cad
--- /dev/null
+++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -0,0 +1,302 @@
+/*
+ * 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);
+ AHardwareBuffer_release(hwBuffer);
+ return true;
+}
+
+} // namespace utils
+} // namespace V2_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
diff --git a/libs/gui/bufferqueue/OWNERS b/libs/gui/bufferqueue/OWNERS
new file mode 100644
index 0000000..cbe9317
--- /dev/null
+++ b/libs/gui/bufferqueue/OWNERS
@@ -0,0 +1,5 @@
+chz@google.com
+lajos@google.com
+pawin@google.com
+taklee@google.com
+wonsik@google.com
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/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index aa13c0c..7db69ec 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -171,6 +171,9 @@
// End functions required for backwards compatibility
+ // Value used to determine if present time is valid.
+ constexpr static int MAX_REASONABLE_NSEC = 1'000'000'000ULL; // 1 second
+
private:
sp<BufferQueueCore> mCore;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index b377a41..690a85f 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -22,8 +22,6 @@
#include <gui/BufferSlot.h>
#include <gui/OccupancyTracker.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
#include <utils/NativeHandle.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
@@ -33,6 +31,8 @@
#include <list>
#include <set>
+#include <mutex>
+#include <condition_variable>
#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
@@ -134,7 +134,7 @@
bool adjustAvailableSlotsLocked(int delta);
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
- void waitWhileAllocatingLocked() const;
+ void waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const;
#if DEBUG_ONLY_CODE
// validateConsistencyLocked ensures that the free lists are in sync with
@@ -145,7 +145,7 @@
// mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueueCore objects. It must be locked whenever any
// member variable is accessed.
- mutable Mutex mMutex;
+ mutable std::mutex mMutex;
// mIsAbandoned indicates that the BufferQueue will no longer be used to
// consume image buffers pushed to it using the IGraphicBufferProducer
@@ -219,13 +219,24 @@
// mDequeueCondition is a condition variable used for dequeueBuffer in
// synchronous mode.
- mutable Condition mDequeueCondition;
+ mutable std::condition_variable mDequeueCondition;
// mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
// block. This flag is set during connect when both the producer and
// consumer are controlled by the application.
bool mDequeueBufferCannotBlock;
+ // mQueueBufferCanDrop indicates whether queueBuffer is allowed to drop
+ // buffers in non-async mode. This flag is set during connect when both the
+ // producer and consumer are controlled by application.
+ bool mQueueBufferCanDrop;
+
+ // mLegacyBufferDrop indicates whether mQueueBufferCanDrop is in effect.
+ // If this flag is set mQueueBufferCanDrop is working as explained. If not
+ // queueBuffer will not drop buffers unless consumer is SurfaceFlinger and
+ // mQueueBufferCanDrop is set.
+ bool mLegacyBufferDrop;
+
// mDefaultBufferFormat can be set so it will override the buffer format
// when it isn't specified in dequeueBuffer.
PixelFormat mDefaultBufferFormat;
@@ -282,7 +293,7 @@
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
// becomes false.
- mutable Condition mIsAllocatingCondition;
+ mutable std::condition_variable mIsAllocatingCondition;
// mAllowAllocation determines whether dequeueBuffer is allowed to allocate
// new buffers
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 73bc5dd..d2a47a6 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -174,6 +174,9 @@
// See IGraphicBufferProducer::setDequeueTimeout
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
+ // see IGraphicBufferProducer::setLegacyBufferDrop
+ virtual status_t setLegacyBufferDrop(bool drop);
+
// See IGraphicBufferProducer::getLastQueuedBuffer
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override;
@@ -211,7 +214,8 @@
Dequeue,
Attach,
};
- status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const;
+ status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, std::unique_lock<std::mutex>& lock,
+ int* found) const;
sp<BufferQueueCore> mCore;
@@ -243,15 +247,22 @@
// (mCore->mMutex) is held, a ticket is retained by the producer. After
// dropping the BufferQueue lock, the producer must wait on the condition
// variable until the current callback ticket matches its retained ticket.
- Mutex mCallbackMutex;
+ std::mutex mCallbackMutex;
int mNextCallbackTicket; // Protected by mCore->mMutex
int mCurrentCallbackTicket; // Protected by mCallbackMutex
- Condition mCallbackCondition;
+ std::condition_variable mCallbackCondition;
// Sets how long dequeueBuffer or attachBuffer will block if a buffer or
// slot is not yet available.
nsecs_t mDequeueTimeout;
+ // If set to true, dequeueBuffer() is currently waiting for buffer allocation to complete.
+ bool mDequeueWaitingForAllocation;
+
+ // Condition variable to signal allocateBuffers() that dequeueBuffer() is no longer waiting for
+ // allocation to complete.
+ std::condition_variable mDequeueWaitingForAllocationCondition;
+
}; // class BufferQueueProducer
} // namespace android
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..3dde8c8 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -25,6 +25,7 @@
#include <binder/IInterface.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
@@ -35,6 +36,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 +44,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,15 +62,24 @@
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
// requestBuffer immediately thereafter.
- BUFFER_NEEDS_REALLOCATION = 0x1,
+ BUFFER_NEEDS_REALLOCATION = BufferQueueDefs::BUFFER_NEEDS_REALLOCATION,
// A flag returned by dequeueBuffer when all mirrored slots should be
// released by the client. This flag should always be processed first.
- RELEASE_ALL_BUFFERS = 0x2,
+ RELEASE_ALL_BUFFERS = BufferQueueDefs::RELEASE_ALL_BUFFERS,
};
enum {
@@ -345,7 +354,7 @@
*outScalingMode = scalingMode;
*outTransform = transform;
*outFence = fence;
- if (outStickyTransform != NULL) {
+ if (outStickyTransform != nullptr) {
*outStickyTransform = stickyTransform;
}
if (outGetFrameTimestamps) {
@@ -366,7 +375,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};
@@ -584,12 +592,20 @@
// non-blocking mode and its corresponding spare buffer (which is used to
// ensure a buffer is always available).
//
+ // Note well: queueBuffer will stop buffer dropping behavior if timeout is
+ // strictly positive. If timeout is zero or negative, previous buffer
+ // dropping behavior will not be changed.
+ //
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - Failure to adjust the number of available slots. This can
// happen because of trying to allocate/deallocate the async
// buffer.
virtual status_t setDequeueTimeout(nsecs_t timeout) = 0;
+ // Used to enable/disable buffer drop behavior of queueBuffer.
+ // If it's not used, legacy drop behavior will be retained.
+ virtual status_t setLegacyBufferDrop(bool drop);
+
// Returns the last queued buffer along with a fence which must signal
// before the contents of the buffer are read. If there are no buffers in
// the queue, outBuffer will be populated with nullptr and outFence will be
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 53b9a90..e2f7736 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -20,32 +20,42 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/RefBase.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+
+#include <gui/ITransactionCompletedListener.h>
+
+#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayedFrameStats.h>
+#include <ui/FrameStats.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+
#include <utils/Errors.h>
+#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
-#include <binder/IInterface.h>
-
-#include <ui/FrameStats.h>
-#include <ui/PixelFormat.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
-
+#include <optional>
+#include <unordered_set>
#include <vector>
namespace android {
// ----------------------------------------------------------------------------
+struct client_cache_t;
struct ComposerState;
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 +78,6 @@
eEarlyWakeup = 0x04
};
- enum {
- eDisplayIdMain = 0,
- eDisplayIdHdmi = 1
- };
-
enum Rotation {
eRotateNone = 0,
eRotate90 = 1,
@@ -85,22 +90,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 +110,35 @@
*/
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,
+ const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@@ -157,9 +172,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 +182,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 +197,36 @@
* 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.
+ *
+ * sourceCrop is the crop on the logical display.
+ *
+ * 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,
+ bool& outCapturedSecureLayers, 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.
*
@@ -195,28 +239,46 @@
* it) around its center.
*/
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, Rect sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ,
- int32_t maxLayerZ, bool useIdentityTransform,
- Rotation rotation = eRotateNone,
- bool captureSecureLayers = false) = 0;
-
- 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,
- bool captureSecureLayers = false) {
- bool ignored;
- return captureScreen(display, outBuffer, ignored, sourceCrop, reqWidth, reqHeight, minLayerZ,
- maxLayerZ, useIdentityTransform, rotation, captureSecureLayers);
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ bool useIdentityTransform, Rotation rotation = eRotateNone) {
+ bool outIgnored;
+ return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight,
+ useIdentityTransform, rotation);
}
+
+ virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
+ sp<GraphicBuffer>* outBuffer) = 0;
+
+ template <class AA>
+ struct SpHash {
+ size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); }
+ };
+
/**
* 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,
- float frameScale = 1.0, bool childrenOnly = false) = 0;
+ virtual status_t captureLayers(
+ const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
+ const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+ const Rect& sourceCrop,
+ const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles,
+ 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.
*
@@ -246,29 +308,156 @@
* 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;
+
+ /*
+ * Sends a power hint to the composer. This function is asynchronous.
+ *
+ * hintId
+ * hint id according to android::hardware::power::V1_0::PowerHint
+ *
+ * Returns NO_ERROR upon success.
+ */
+ virtual status_t notifyPowerHint(int32_t hintId) = 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,
@@ -282,8 +471,24 @@
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,
+ CAPTURE_SCREEN_BY_ID,
+ NOTIFY_POWER_HINT,
+ // 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..774ad46
--- /dev/null
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -0,0 +1,113 @@
+/*
+ * 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;
+
+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;
+
+ TransactionStats() = default;
+ TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
+ TransactionStats(const std::unordered_set<CallbackId>& ids)
+ : callbackIds(ids.begin(), ids.end()) {}
+ TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
+ const std::vector<SurfaceStats>& surfaces)
+ : callbackIds(ids), latchTime(latch), presentFence(present), surfaceStats(surfaces) {}
+
+ std::vector<CallbackId> callbackIds;
+ 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::vector<TransactionStats> 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..f438eb3 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -22,127 +22,211 @@
#include <utils/Errors.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
#include <gui/IGraphicBufferProducer.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 {
class Parcel;
class ISurfaceComposerClient;
+struct client_cache_t {
+ wp<IBinder> token = nullptr;
+ uint64_t id;
+
+ bool operator==(const client_cache_t& other) const { return id == other.id; }
+
+ bool isValid() const { return token != nullptr; }
+};
+
/*
* 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,
+ eHasListenerCallbacksChanged = 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()),
+ hasListenerCallbacks(false),
+ 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};
+ };
+ 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;
+
+ bool hasListenerCallbacks;
+#ifndef NO_INPUT
+ InputWindowInfo inputInfo;
+#endif
+
+ client_cache_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 +258,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..0c471bb 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);
@@ -229,6 +230,7 @@
int dispatchGetWideColorSupport(va_list args);
int dispatchGetHdrSupport(va_list args);
int dispatchGetConsumerUsage64(va_list args);
+ bool transformToDisplayInverse();
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -249,6 +251,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 49bb687..0e17c7b 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,18 @@
#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/ISurfaceComposer.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 +51,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 +95,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 +105,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 +113,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 +143,107 @@
/* 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);
+
+ /**
+ * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
+ * in order with other transactions that use buffers.
+ */
+ static void doUncacheBufferTransaction(uint64_t cacheId);
+
+ // 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);
+
+ /*
+ * Sends a power hint to the composer. This function is asynchronous.
+ *
+ * hintId
+ * hint id according to android::hardware::power::V1_0::PowerHint
+ *
+ * Returns NO_ERROR upon success.
+ */
+ static status_t notifyPowerHint(int32_t hintId);
+
// ------------------------------------------------------------------------
// 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 +252,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 +270,56 @@
}
};
+ 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;
+ // Indicates that the Transaction contains a buffer that should be cached
+ bool mContainsBuffer = 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 cacheBuffers();
+ void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+
public:
Transaction() = default;
virtual ~Transaction() = default;
@@ -203,22 +359,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 +388,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 +436,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 +473,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 +488,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,22 +516,74 @@
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,
- bool captureSecureLayers, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers);
- 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,
+ 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, bool& outCapturedSecureLayers);
+ 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 capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
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,
- float frameScale, 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,
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+ excludeHandles,
+ 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..23bfc02 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,17 @@
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);
+
+ SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
+ const sp<IGraphicBufferProducer>& gbp, bool owned);
private:
// can't be copied
@@ -84,17 +95,10 @@
friend class SurfaceComposerClient;
friend class Surface;
- SurfaceControl(
- const sp<SurfaceComposerClient>& client,
- const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp,
- bool owned);
-
~SurfaceControl();
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/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
new file mode 100644
index 0000000..627845c
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -0,0 +1,765 @@
+/*
+ * 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
+
+#include <vector>
+#include <list>
+
+#include <unistd.h>
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <binder/Status.h>
+#include <ui/FenceTime.h>
+#include <cutils/native_handle.h>
+#include <gui/IGraphicBufferProducer.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace conversion {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using ::android::status_t;
+
+using ::android::String8;
+
+using ::android::hardware::media::V1_0::Rect;
+using ::android::hardware::media::V1_0::Region;
+
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+
+using ::android::hardware::media::V1_0::AnwBuffer;
+using ::android::GraphicBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+ HGraphicBufferProducer;
+typedef ::android::IGraphicBufferProducer
+ BGraphicBufferProducer;
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+native_handle_t* native_handle_create_from_fd(int fd);
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+int native_handle_read_fd(native_handle_t const* nh, int index = 0);
+
+/**
+ * Conversion functions
+ * ====================
+ *
+ * There are two main directions of conversion:
+ * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the
+ * input. The wrapper has type `TargetType`.
+ * - `toTargetType(...)`: Create a standalone object of type `TargetType` that
+ * corresponds to the input. The lifetime of the output does not depend on the
+ * lifetime of the input.
+ * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType`
+ * that cannot be copied and/or moved efficiently, or when there are multiple
+ * output arguments.
+ * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for
+ * `TargetType` that cannot be copied and/or moved efficiently, or when there
+ * are multiple output arguments.
+ *
+ * `wrapIn()` and `convertTo()` functions will take output arguments before
+ * input arguments. Some of these functions might return a value to indicate
+ * success or error.
+ *
+ * In converting or wrapping something as a Treble type that contains a
+ * `hidl_handle`, `native_handle_t*` will need to be created and returned as
+ * an additional output argument, hence only `wrapIn()` or `convertTo()` would
+ * be available. The caller must call `native_handle_delete()` to deallocate the
+ * returned native handle when it is no longer needed.
+ *
+ * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do
+ * not perform duplication of file descriptors, while `toTargetType()` and
+ * `convertTo()` do.
+ */
+
+/**
+ * \brief Convert `Return<void>` to `binder::Status`.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `binder::Status`.
+ */
+// convert: Return<void> -> ::android::binder::Status
+::android::binder::Status toBinderStatus(Return<void> const& t);
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+status_t toStatusT(Return<void> const& t);
+
+/**
+ * \brief Wrap `native_handle_t*` in `hidl_handle`.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \return The `hidl_handle` that points to \p nh.
+ */
+// wrap: native_handle_t* -> hidl_handle
+hidl_handle inHidlHandle(native_handle_t const* nh);
+
+/**
+ * \brief Convert `int32_t` to `Dataspace`.
+ *
+ * \param[in] l The source `int32_t`.
+ * \result The corresponding `Dataspace`.
+ */
+// convert: int32_t -> Dataspace
+Dataspace toHardwareDataspace(int32_t l);
+
+/**
+ * \brief Convert `Dataspace` to `int32_t`.
+ *
+ * \param[in] t The source `Dataspace`.
+ * \result The corresponding `int32_t`.
+ */
+// convert: Dataspace -> int32_t
+int32_t toRawDataspace(Dataspace const& t);
+
+/**
+ * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that points to the buffer.
+ */
+// wrap: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size);
+
+/**
+ * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer.
+ */
+// convert: void*, size_t -> hidl_vec<uint8_t>
+hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size);
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+void wrapAs(AnwBuffer* t, GraphicBuffer const& l);
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+bool convertTo(GraphicBuffer* l, AnwBuffer const& t);
+
+/**
+ * Conversion functions for types outside media
+ * ============================================
+ *
+ * Some objects in libui and libgui that were made to go through binder calls do
+ * not expose ways to read or write their fields to the public. To pass an
+ * object of this kind through the HIDL boundary, translation functions need to
+ * work around the access restriction by using the publicly available
+ * `flatten()` and `unflatten()` functions.
+ *
+ * All `flatten()` and `unflatten()` overloads follow the same convention as
+ * follows:
+ *
+ * status_t flatten(ObjectType const& object,
+ * [OtherType const& other, ...]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * status_t unflatten(ObjectType* object,
+ * [OtherType* other, ...,]
+ * void*& buffer, size_t& size,
+ * int*& fds, size_t& numFds)
+ *
+ * The number of `other` parameters varies depending on the `ObjectType`. For
+ * example, in the process of unflattening an object that contains
+ * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will
+ * be created.
+ *
+ * The last four parameters always work the same way in all overloads of
+ * `flatten()` and `unflatten()`:
+ * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled,
+ * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`,
+ * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the
+ * size (in ints) of the fd buffer pointed to by `fds`.
+ * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read
+ * from, `size` is the size (in bytes) of the non-fd buffer pointed to by
+ * `buffer`, `fds` is the pointer to the fd buffer to be read from, and
+ * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`.
+ * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds`
+ * will be advanced, while `size` and `numFds` will be decreased to reflect
+ * how much storage/data of the two buffers (fd and non-fd) have been used.
+ * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and
+ * `numFds` are invalid.
+ *
+ * The return value of a successful `flatten()` or `unflatten()` call will be
+ * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure.
+ *
+ * For each object type that supports flattening, there will be two accompanying
+ * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will
+ * return the size of the non-fd buffer that the object will need for
+ * flattening. `getFdCount()` will return the size of the fd buffer that the
+ * object will need for flattening.
+ *
+ * The set of these four functions, `getFlattenedSize()`, `getFdCount()`,
+ * `flatten()` and `unflatten()`, are similar to functions of the same name in
+ * the abstract class `Flattenable`. The only difference is that functions in
+ * this file are not member functions of the object type. For example, we write
+ *
+ * flatten(x, buffer, size, fds, numFds)
+ *
+ * instead of
+ *
+ * x.flatten(buffer, size, fds, numFds)
+ *
+ * because we cannot modify the type of `x`.
+ *
+ * There is one exception to the naming convention: `hidl_handle` that
+ * represents a fence. The four functions for this "Fence" type have the word
+ * "Fence" attched to their names because the object type, which is
+ * `hidl_handle`, does not carry the special meaning that the object itself can
+ * only contain zero or one file descriptor.
+ */
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+size_t getFenceFlattenedSize(hidl_handle const& fence);
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+size_t getFenceFdCount(hidl_handle const& fence);
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] t The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flattenFence(hidl_handle const& fence,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l);
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+bool convertTo(Fence* l, hidl_handle const& t);
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::FenceTimeSnapshot const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+size_t getFdCount(HGraphicBufferProducer::FenceTimeSnapshot const& t);
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`.
+ */
+status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Unflatten `FenceTimeSnapshot`.
+ *
+ * \param[out] t The destination `FenceTimeSnapshot`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and the constructed snapshot contains a
+ * file descriptor, \p nh will be created to hold that file descriptor. In this
+ * case, \p nh needs to be deleted with `native_handle_delete()` afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::FrameEventsDelta const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(HGraphicBufferProducer::FrameEventsDelta const& t);
+
+/**
+ * \brief Unflatten `FrameEventsDelta`.
+ *
+ * \param[out] t The destination `FrameEventsDelta`.
+ * \param[out] nh The underlying array of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be
+ * populated with `nullptr` or newly created handles. Each non-null slot in \p
+ * nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t,
+ std::vector<native_handle_t*>* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+// FrameEventsDelta::flatten
+status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t numFds);
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t);
+
+/**
+ * \brief Unflatten `FrameEventHistoryDelta`.
+ *
+ * \param[out] t The destination `FrameEventHistoryDelta`.
+ * \param[out] nh The underlying array of arrays of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or
+ * newly created handles. The second dimension of \p nh will be 4. Each non-null
+ * slot in \p nh will need to be deleted manually with `native_handle_delete()`.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+status_t flatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Wrap `::android::FrameEventHistoryData` in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `::android::FrameEventHistoryDelta`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ ::android::FrameEventHistoryDelta const& l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+bool convertTo(
+ ::android::FrameEventHistoryDelta* l,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t);
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Return the size of the buffer required to flatten `Region`.
+ *
+ * \param[in] t The input `Region`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(Region const& t);
+
+/**
+ * \brief Unflatten `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t unflatten(Region* t, void const*& buffer, size_t& size);
+
+/**
+ * \brief Flatten `Region`.
+ *
+ * \param[in] t The source `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+status_t flatten(Region const& t, void*& buffer, size_t& size);
+
+/**
+ * \brief Convert `::android::Region` to `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in] l The source `::android::Region`.
+ */
+// convert: ::android::Region -> Region
+bool convertTo(Region* t, ::android::Region const& l);
+
+/**
+ * \brief Convert `Region` to `::android::Region`.
+ *
+ * \param[out] l The destination `::android::Region`.
+ * \param[in] t The source `Region`.
+ */
+// convert: Region -> ::android::Region
+bool convertTo(::android::Region* l, Region const& t);
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The required size of the flat buffer.
+ */
+size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t);
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The number of file descriptors contained in \p t.
+ */
+size_t getFdCount(
+ HGraphicBufferProducer::QueueBufferInput const& t);
+/**
+ * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The native handle cloned from `t.fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence`. */
+status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t,
+ native_handle_t** nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds);
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+status_t unflatten(
+ HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+bool wrapAs(
+ HGraphicBufferProducer::QueueBufferInput* t,
+ native_handle_t** nh,
+ BGraphicBufferProducer::QueueBufferInput const& l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
+ * `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If `t.fence` has a valid file descriptor, it will be duplicated.
+ */
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferInput* l,
+ HGraphicBufferProducer::QueueBufferInput const& t);
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+// BGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+// wrap: BGraphicBufferProducer::QueueBufferOutput ->
+// HGraphicBufferProducer::QueueBufferOutput
+bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ BGraphicBufferProducer::QueueBufferOutput const& l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// BGraphicBufferProducer::QueueBufferOutput
+bool convertTo(
+ BGraphicBufferProducer::QueueBufferOutput* l,
+ HGraphicBufferProducer::QueueBufferOutput const& t);
+
+/**
+ * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode(
+ BGraphicBufferProducer::DisconnectMode l);
+
+/**
+ * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
+ * `BGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `BGraphicBufferProducer::DisconnectMode`.
+ */
+BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
+ HGraphicBufferProducer::DisconnectMode t);
+
+} // namespace conversion
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
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/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
new file mode 100644
index 0000000..029dcc0
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -0,0 +1,380 @@
+/*
+ * 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <gui/bufferqueue/1.0/Conversion.h>
+#include <gui/bufferqueue/1.0/WProducerListener.h>
+#include <system/window.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+
+using ::android::hardware::media::V1_0::AnwBuffer;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::
+ IProducerListener HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+typedef ::android::IProducerListener BProducerListener;
+using ::android::BnGraphicBufferProducer;
+
+#ifndef LOG
+struct LOG_dummy {
+ template <typename T>
+ LOG_dummy& operator<< (const T&) { return *this; }
+};
+
+#define LOG(x) LOG_dummy()
+#endif
+
+// Instantiate only if HGraphicBufferProducer is base of BASE.
+template <typename BASE,
+ typename = typename std::enable_if<std::is_base_of<HGraphicBufferProducer, BASE>::value>::type>
+struct TWGraphicBufferProducer : public BASE {
+ TWGraphicBufferProducer(sp<BGraphicBufferProducer> const& base) : mBase(base) {}
+ Return<void> requestBuffer(int32_t slot, HGraphicBufferProducer::requestBuffer_cb _hidl_cb) override {
+ sp<GraphicBuffer> buf;
+ status_t status = mBase->requestBuffer(slot, &buf);
+ AnwBuffer anwBuffer{};
+ if (buf != nullptr) {
+ ::android::conversion::wrapAs(&anwBuffer, *buf);
+ }
+ _hidl_cb(static_cast<int32_t>(status), anwBuffer);
+ return Void();
+ }
+
+ Return<int32_t> setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers) override {
+ return static_cast<int32_t>(mBase->setMaxDequeuedBufferCount(
+ static_cast<int>(maxDequeuedBuffers)));
+ }
+
+ Return<int32_t> setAsyncMode(bool async) override {
+ return static_cast<int32_t>(mBase->setAsyncMode(async));
+ }
+
+ Return<void> dequeueBuffer(
+ uint32_t width, uint32_t height,
+ ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage,
+ bool getFrameTimestamps, HGraphicBufferProducer::dequeueBuffer_cb _hidl_cb) override {
+ int slot{};
+ sp<Fence> fence;
+ ::android::FrameEventHistoryDelta outTimestamps;
+ status_t status = mBase->dequeueBuffer(
+ &slot, &fence, width, height,
+ static_cast<::android::PixelFormat>(format), usage, nullptr,
+ getFrameTimestamps ? &outTimestamps : nullptr);
+ hidl_handle tFence{};
+ HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps{};
+
+ native_handle_t* nh = nullptr;
+ if ((fence == nullptr) || !::android::conversion::wrapAs(&tFence, &nh, *fence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output fence";
+ _hidl_cb(static_cast<int32_t>(status),
+ static_cast<int32_t>(slot),
+ tFence,
+ tOutTimestamps);
+ return Void();
+ }
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (getFrameTimestamps && !::android::conversion::wrapAs(&tOutTimestamps, &nhAA, outTimestamps)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output timestamps";
+ _hidl_cb(static_cast<int32_t>(status),
+ static_cast<int32_t>(slot),
+ tFence,
+ tOutTimestamps);
+ native_handle_delete(nh);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status),
+ static_cast<int32_t>(slot),
+ tFence,
+ tOutTimestamps);
+ native_handle_delete(nh);
+ if (getFrameTimestamps) {
+ for (auto& nhA : nhAA) {
+ for (auto& handle : nhA) {
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return Void();
+ }
+
+ Return<int32_t> detachBuffer(int32_t slot) override {
+ return static_cast<int32_t>(mBase->detachBuffer(slot));
+ }
+
+ Return<void> detachNextBuffer(HGraphicBufferProducer::detachNextBuffer_cb _hidl_cb) override {
+ sp<GraphicBuffer> outBuffer;
+ sp<Fence> outFence;
+ status_t status = mBase->detachNextBuffer(&outBuffer, &outFence);
+ AnwBuffer tBuffer{};
+ hidl_handle tFence{};
+
+ if (outBuffer == nullptr) {
+ LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output buffer";
+ _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+ return Void();
+ }
+ ::android::conversion::wrapAs(&tBuffer, *outBuffer);
+ native_handle_t* nh = nullptr;
+ if ((outFence != nullptr) && !::android::conversion::wrapAs(&tFence, &nh, *outFence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output fence";
+ _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+ native_handle_delete(nh);
+ return Void();
+ }
+
+ Return<void> attachBuffer(const AnwBuffer& buffer, HGraphicBufferProducer::attachBuffer_cb _hidl_cb) override {
+ int outSlot;
+ sp<GraphicBuffer> lBuffer = new GraphicBuffer();
+ if (!::android::conversion::convertTo(lBuffer.get(), buffer)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::attachBuffer - "
+ "Invalid input native window buffer";
+ _hidl_cb(static_cast<int32_t>(BAD_VALUE), -1);
+ return Void();
+ }
+ status_t status = mBase->attachBuffer(&outSlot, lBuffer);
+
+ _hidl_cb(static_cast<int32_t>(status), static_cast<int32_t>(outSlot));
+ return Void();
+ }
+
+ Return<void> queueBuffer(
+ int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input,
+ HGraphicBufferProducer::queueBuffer_cb _hidl_cb) override {
+ HGraphicBufferProducer::QueueBufferOutput tOutput{};
+ BGraphicBufferProducer::QueueBufferInput lInput(
+ 0, false, HAL_DATASPACE_UNKNOWN,
+ ::android::Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ 0, ::android::Fence::NO_FENCE);
+ if (!::android::conversion::convertTo(&lInput, input)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
+ "Invalid input";
+ _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
+ return Void();
+ }
+ BGraphicBufferProducer::QueueBufferOutput lOutput;
+ status_t status = mBase->queueBuffer(
+ static_cast<int>(slot), lInput, &lOutput);
+
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
+ "Invalid output";
+ _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status), tOutput);
+ for (auto& nhA : nhAA) {
+ for (auto& nh : nhA) {
+ native_handle_delete(nh);
+ }
+ }
+ return Void();
+ }
+
+ Return<int32_t> cancelBuffer(int32_t slot, const hidl_handle& fence) override {
+ sp<Fence> lFence = new Fence();
+ if (!::android::conversion::convertTo(lFence.get(), fence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::cancelBuffer - "
+ "Invalid input fence";
+ return static_cast<int32_t>(BAD_VALUE);
+ }
+ return static_cast<int32_t>(mBase->cancelBuffer(static_cast<int>(slot), lFence));
+ }
+
+ Return<void> query(int32_t what, HGraphicBufferProducer::query_cb _hidl_cb) override {
+ int lValue;
+ int lReturn = mBase->query(static_cast<int>(what), &lValue);
+ _hidl_cb(static_cast<int32_t>(lReturn), static_cast<int32_t>(lValue));
+ return Void();
+ }
+
+ Return<void> connect(const sp<HProducerListener>& listener,
+ int32_t api, bool producerControlledByApp,
+ HGraphicBufferProducer::connect_cb _hidl_cb) override {
+ sp<BProducerListener> lListener = listener == nullptr ?
+ nullptr : new LWProducerListener(listener);
+ BGraphicBufferProducer::QueueBufferOutput lOutput;
+ status_t status = mBase->connect(lListener,
+ static_cast<int>(api),
+ producerControlledByApp,
+ &lOutput);
+
+ HGraphicBufferProducer::QueueBufferOutput tOutput{};
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::connect - "
+ "Invalid output";
+ _hidl_cb(static_cast<int32_t>(status), tOutput);
+ return Void();
+ }
+
+ _hidl_cb(static_cast<int32_t>(status), tOutput);
+ for (auto& nhA : nhAA) {
+ for (auto& nh : nhA) {
+ native_handle_delete(nh);
+ }
+ }
+ return Void();
+ }
+
+ Return<int32_t> disconnect(
+ int32_t api,
+ HGraphicBufferProducer::DisconnectMode mode) override {
+ return static_cast<int32_t>(mBase->disconnect(
+ static_cast<int>(api),
+ ::android::conversion::toGuiDisconnectMode(mode)));
+ }
+
+ Return<int32_t> setSidebandStream(const hidl_handle& stream) override {
+ return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create(
+ stream ? native_handle_clone(stream) : NULL, true)));
+ }
+
+ Return<void> allocateBuffers(
+ uint32_t width, uint32_t height,
+ ::android::hardware::graphics::common::V1_0::PixelFormat format,
+ uint32_t usage) override {
+ mBase->allocateBuffers(
+ width, height,
+ static_cast<::android::PixelFormat>(format),
+ usage);
+ return Void();
+ }
+
+ Return<int32_t> allowAllocation(bool allow) override {
+ return static_cast<int32_t>(mBase->allowAllocation(allow));
+ }
+
+ Return<int32_t> setGenerationNumber(uint32_t generationNumber) override {
+ return static_cast<int32_t>(mBase->setGenerationNumber(generationNumber));
+ }
+
+ Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override {
+ _hidl_cb(mBase->getConsumerName().string());
+ return Void();
+ }
+
+ Return<int32_t> setSharedBufferMode(bool sharedBufferMode) override {
+ return static_cast<int32_t>(mBase->setSharedBufferMode(sharedBufferMode));
+ }
+
+ Return<int32_t> setAutoRefresh(bool autoRefresh) override {
+ return static_cast<int32_t>(mBase->setAutoRefresh(autoRefresh));
+ }
+
+ Return<int32_t> setDequeueTimeout(int64_t timeoutNs) override {
+ return static_cast<int32_t>(mBase->setDequeueTimeout(timeoutNs));
+ }
+
+ Return<void> getLastQueuedBuffer(HGraphicBufferProducer::getLastQueuedBuffer_cb _hidl_cb) override {
+ sp<GraphicBuffer> lOutBuffer = new GraphicBuffer();
+ sp<Fence> lOutFence = new Fence();
+ float lOutTransformMatrix[16];
+ status_t status = mBase->getLastQueuedBuffer(
+ &lOutBuffer, &lOutFence, lOutTransformMatrix);
+
+ AnwBuffer tOutBuffer{};
+ if (lOutBuffer != nullptr) {
+ ::android::conversion::wrapAs(&tOutBuffer, *lOutBuffer);
+ }
+ hidl_handle tOutFence{};
+ native_handle_t* nh = nullptr;
+ if ((lOutFence == nullptr) || !::android::conversion::wrapAs(&tOutFence, &nh, *lOutFence)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - "
+ "Invalid output fence";
+ _hidl_cb(static_cast<int32_t>(status),
+ tOutBuffer,
+ tOutFence,
+ hidl_array<float, 16>());
+ return Void();
+ }
+ hidl_array<float, 16> tOutTransformMatrix(lOutTransformMatrix);
+
+ _hidl_cb(static_cast<int32_t>(status), tOutBuffer, tOutFence, tOutTransformMatrix);
+ native_handle_delete(nh);
+ return Void();
+ }
+
+ Return<void> getFrameTimestamps(HGraphicBufferProducer::getFrameTimestamps_cb _hidl_cb) override {
+ ::android::FrameEventHistoryDelta lDelta;
+ mBase->getFrameTimestamps(&lDelta);
+
+ HGraphicBufferProducer::FrameEventHistoryDelta tDelta{};
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (!::android::conversion::wrapAs(&tDelta, &nhAA, lDelta)) {
+ LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - "
+ "Invalid output frame timestamps";
+ _hidl_cb(tDelta);
+ return Void();
+ }
+
+ _hidl_cb(tDelta);
+ for (auto& nhA : nhAA) {
+ for (auto& nh : nhA) {
+ native_handle_delete(nh);
+ }
+ }
+ return Void();
+ }
+
+ Return<void> getUniqueId(HGraphicBufferProducer::getUniqueId_cb _hidl_cb) override {
+ uint64_t outId{};
+ status_t status = mBase->getUniqueId(&outId);
+ _hidl_cb(static_cast<int32_t>(status), outId);
+ return Void();
+ }
+
+private:
+ sp<BGraphicBufferProducer> mBase;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
new file mode 100644
index 0000000..51dff5b
--- /dev/null
+++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -0,0 +1,62 @@
+/*
+ * 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_
+#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/IBinder.h>
+#include <gui/IProducerListener.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener
+ HProducerListener;
+typedef ::android::IProducerListener
+ BProducerListener;
+using ::android::BnProducerListener;
+
+struct TWProducerListener : public HProducerListener {
+ sp<BProducerListener> mBase;
+ TWProducerListener(sp<BProducerListener> const& base);
+ Return<void> onBufferReleased() override;
+ Return<bool> needsReleaseNotify() override;
+};
+
+class LWProducerListener : public BnProducerListener {
+public:
+ sp<HProducerListener> mBase;
+ LWProducerListener(sp<HProducerListener> const& base);
+ void onBufferReleased() override;
+ bool needsReleaseNotify() override;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_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..ff1ba0a
--- /dev/null
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -0,0 +1,522 @@
+/*
+ * 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 a surface whose insets are scaled, handles the touch offset correctly.
+TEST_F(InputSurfacesTest, input_respects_scaled_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);
+
+ fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
+
+ // expect = touch / scale - inset
+ injectTap(112, 124);
+ fgSurface->expectTap(1, 1);
+
+ injectTap(101, 101);
+ bgSurface->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 0fd4231..960cf18 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,12 +131,15 @@
// 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;
bool ignored;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, ignored, Rect(),
- 64, 64, 0, 0x7fffffff, false));
+ ASSERT_EQ(NO_ERROR,
+ sf->captureScreen(display, &outBuffer, ignored, 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));
@@ -146,7 +149,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) {
@@ -166,8 +169,9 @@
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, ignored, Rect(),
- 64, 64, 0, 0x7fffffff, false));
+ ASSERT_EQ(NO_ERROR,
+ sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false));
}
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -206,7 +210,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);
@@ -365,10 +369,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) {
@@ -537,10 +548,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;
@@ -548,10 +555,16 @@
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*/, const client_cache_t& /*cachedBuffer*/,
+ const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
+ }
+
void bootFinished() override {}
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& /*surface*/) const override {
@@ -582,9 +595,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 {
@@ -594,23 +604,36 @@
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*/,
- bool& /* outCapturedSecureLayers */,
- Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
- int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/,
- 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 {
+ status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
+ bool& /* outCapturedSecureLayers */,
+ 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;
+ }
+ status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
+ sp<GraphicBuffer>* /*outBuffer*/) override {
+ return NO_ERROR;
+ }
+ virtual status_t captureLayers(
+ const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/,
+ const ui::Dataspace /*reqDataspace*/, const ui::PixelFormat /*reqPixelFormat*/,
+ const Rect& /*sourceCrop*/,
+ const std::unordered_set<sp<IBinder>,
+ ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/,
+ float /*frameScale*/, bool /*childrenOnly*/) override {
return NO_ERROR;
}
status_t clearAnimationFrameStats() override { return NO_ERROR; }
@@ -628,6 +651,60 @@
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;
+ }
+ status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
index 45eab00..63411b9 100644
--- a/libs/incidentcompanion/Android.bp
+++ b/libs/incidentcompanion/Android.bp
@@ -35,8 +35,12 @@
},
srcs: [
":incidentcompanion_aidl",
+ "src/IncidentManager.cpp",
],
- export_include_dirs: ["binder"],
+ export_include_dirs: [
+ "binder",
+ "include",
+ ],
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
index 6bf98d2..98c2814 100644
--- a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IIncidentAuthListener;
+import android.os.IncidentManager;
/**
* Helper service for incidentd and dumpstated to provide user feedback
@@ -35,6 +36,10 @@
* returns via the callback whether the application should be trusted. It is up
* to the caller to actually implement the restriction to take or not take
* the incident or bug report.
+ * @param receiverClass The class that will be the eventual broacast receiver for the
+ * INCIDENT_REPORT_READY message. Used as part of the id in incidentd.
+ * @param reportId The incident report ID. Incidentd should call with this parameter, but
+ * everyone else should pass null or empty string.
* @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise
* a dialog will be shown as a notification.
* @param callback Interface to receive results. The results may not come back for
@@ -44,6 +49,7 @@
* to send their report.
*/
oneway void authorizeReport(int callingUid, String callingPackage,
+ String receiverClass, String reportId,
int flags, IIncidentAuthListener callback);
/**
@@ -52,6 +58,11 @@
oneway void cancelAuthorization(IIncidentAuthListener callback);
/**
+ * Send the report ready broadcast on behalf of incidentd.
+ */
+ oneway void sendReportReadyBroadcast(String pkg, String cls);
+
+ /**
* Return the list of pending approvals.
*/
List<String> getPendingReports();
@@ -69,4 +80,26 @@
* @param uri the report.
*/
void denyReport(String uri);
+
+ /**
+ * List the incident reports for the given ComponentName. The receiver
+ * must be for a package inside the caller.
+ */
+ List<String> getIncidentReportList(String pkg, String cls);
+
+ /**
+ * Get the IncidentReport object.
+ */
+ IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with this incident report and it can be deleted.
+ */
+ void deleteIncidentReports(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with all incident reports from this package.
+ * Especially useful for testing.
+ */
+ void deleteAllIncidentReports(String pkg);
}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/incidentcompanion/binder/android/os/IncidentManager.aidl
index e6ac6bf..d17823e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open 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"
+package android.os;
-namespace android {
-namespace mock {
+parcelable IncidentManager.IncidentReport cpp_header "android/os/IncidentManager.h";
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
-
-} // namespace mock
-} // namespace android
diff --git a/libs/incidentcompanion/include/android/os/IncidentManager.h b/libs/incidentcompanion/include/android/os/IncidentManager.h
new file mode 100644
index 0000000..07b6d82
--- /dev/null
+++ b/libs/incidentcompanion/include/android/os/IncidentManager.h
@@ -0,0 +1,71 @@
+/**
+ * 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/Parcel.h>
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+
+class IncidentManager : public virtual RefBase {
+public:
+ class IncidentReport : public Parcelable {
+ public:
+ IncidentReport();
+ virtual ~IncidentReport();
+
+ virtual status_t writeToParcel(Parcel* out) const;
+ virtual status_t readFromParcel(const Parcel* in);
+
+ void setTimestampNs(int64_t val) { mTimestampNs = val; }
+ int64_t getTimestampNs() const { return mTimestampNs; }
+ int64_t getTimestampMs() const { return mTimestampNs / 1000000; }
+
+ void setPrivacyPolicy(int32_t val) { mPrivacyPolicy = val; }
+ // This was accidentally published as a long in the java api.
+ int64_t getPrivacyPolicy() const { return mPrivacyPolicy; }
+ // Dups the fd, so you retain ownership of the original one. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ status_t setFileDescriptor(int fd);
+
+ // Does not dup the fd, so ownership is passed to this object. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ void takeFileDescriptor(int fd);
+
+ // Returns the fd, which you don't own. Call dup if you need a copy.
+ int getFileDescriptor() const { return mFileDescriptor; }
+
+ private:
+ int64_t mTimestampNs;
+ int32_t mPrivacyPolicy;
+ int mFileDescriptor;
+ };
+
+
+private:
+ // Not implemented for now.
+ IncidentManager();
+ virtual ~IncidentManager();
+};
+}
+}
+
diff --git a/libs/incidentcompanion/src/IncidentManager.cpp b/libs/incidentcompanion/src/IncidentManager.cpp
new file mode 100644
index 0000000..f7c8a5e
--- /dev/null
+++ b/libs/incidentcompanion/src/IncidentManager.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/os/IncidentManager.h>
+
+namespace android {
+namespace os {
+
+// ============================================================
+IncidentManager::IncidentReport::IncidentReport()
+ :mTimestampNs(0),
+ mPrivacyPolicy(0),
+ mFileDescriptor(-1) {
+}
+
+IncidentManager::IncidentReport::~IncidentReport() {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+}
+
+status_t IncidentManager::IncidentReport::writeToParcel(Parcel* out) const {
+ status_t err;
+
+ err = out->writeInt64(mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+
+ err = out->writeInt32(mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (mFileDescriptor >= 0) {
+ err = out->writeInt32(1);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = out->writeDupParcelFileDescriptor(mFileDescriptor);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ } else {
+ err = out->writeInt32(0);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::readFromParcel(const Parcel* in) {
+ status_t err;
+ int32_t hasField;
+
+ err = in->readInt64(&mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&hasField);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (hasField) {
+ int fd = in->readParcelFileDescriptor();
+ if (fd >= 0) {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::setFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ return NO_ERROR;
+}
+
+void IncidentManager::IncidentReport::takeFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = fd;
+ }
+}
+
+// ============================================================
+IncidentManager::~IncidentManager() {
+}
+
+}
+}
+
+
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 60%
rename from services/inputflinger/InputApplication.cpp
rename to libs/input/InputApplication.cpp
index 9e90631..1d9f8a7 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>
@@ -24,19 +24,27 @@
// --- InputApplicationHandle ---
-InputApplicationHandle::InputApplicationHandle() :
- mInfo(NULL) {
+InputApplicationHandle::InputApplicationHandle() {
}
InputApplicationHandle::~InputApplicationHandle() {
- delete mInfo;
}
-void InputApplicationHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = NULL;
- }
+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..d02cb8e 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -27,11 +27,16 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
#include <cutils/properties.h>
#include <log/log.h>
+#include <utils/Trace.h>
#include <input/InputTransport.h>
+using android::base::StringPrintf;
+
namespace android {
// Socket buffer size. The default is typically about 128KB, which is much larger than
@@ -58,6 +63,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;
@@ -67,6 +84,10 @@
return a + alpha * (b - a);
}
+inline static bool isPointerEvent(int32_t source) {
+ return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
+}
+
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
@@ -156,6 +177,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 +223,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 +241,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 +361,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 +419,7 @@
uint32_t seq,
int32_t deviceId,
int32_t source,
+ int32_t displayId,
int32_t action,
int32_t flags,
int32_t keyCode,
@@ -353,6 +428,11 @@
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
+ mChannel->getName().c_str(), keyCode);
+ ATRACE_NAME(message.c_str());
+ }
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
@@ -372,6 +452,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 +475,7 @@
int32_t edgeFlags,
int32_t metaState,
int32_t buttonState,
+ MotionClassification classification,
float xOffset,
float yOffset,
float xPrecision,
@@ -403,14 +485,22 @@
uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
+ mChannel->getName().c_str(), action);
+ ATRACE_NAME(message.c_str());
+ }
#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 +527,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 +576,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 +602,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",
@@ -563,12 +641,22 @@
mChannel->getName().c_str());
#endif
break;
+ } else if (isPointerEvent(mMsg.body.motion.source) &&
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
+ // No need to process events that we are going to cancel anyways
+ const size_t count = batch.samples.size();
+ for (size_t i = 0; i < count; i++) {
+ const InputMessage& msg = batch.samples.itemAt(i);
+ sendFinishedSignal(msg.body.motion.seq, false);
+ }
+ batch.samples.removeItemsAt(0, count);
+ mBatches.removeAt(batchIndex);
} else {
// We cannot append to the batch in progress, so we need to consume
// the previous batch right now and defer the new message until later.
mMsgDeferred = true;
status_t result = consumeSamples(factory,
- batch, batch.samples.size(), outSeq, outEvent, displayId);
+ batch, batch.samples.size(), outSeq, outEvent);
mBatches.removeAt(batchIndex);
if (result) {
return result;
@@ -602,7 +690,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 +708,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 +728,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 +746,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 +761,6 @@
mSeqChains.push(seqChain);
addSample(motionEvent, &msg);
} else {
- *displayId = msg.body.motion.displayId;
initializeMotionEvent(motionEvent, &msg);
}
chain = msg.body.motion.seq;
@@ -687,8 +773,7 @@
}
void InputConsumer::updateTouchState(InputMessage& msg) {
- if (!mResampleTouch ||
- !(msg.body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) {
+ if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) {
return;
}
@@ -800,7 +885,7 @@
void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
const InputMessage* next) {
if (!mResampleTouch
- || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER)
+ || !(isPointerEvent(event->getSource()))
|| event->getAction() != AMOTION_EVENT_ACTION_MOVE) {
return;
}
@@ -1030,6 +1115,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 +1138,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..ec28757
--- /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.writeStrongBinder(touchableRegionCropHandle.promote());
+ 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.readStrongBinder();
+
+ 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 3de2a46..c6cc4fc 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 f7a2ca0..368446f 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -29,6 +29,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
@@ -174,9 +176,10 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
action, 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*/, entry.eventTime.count(), pointerCount, properties, coords);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 7e26b0b..9bd3095 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,100 @@
outDesc->rfu1 = 0;
}
+int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, void** outVirtualAddress,
+ int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
+ if (outBytesPerPixel) *outBytesPerPixel = -1;
+ if (outBytesPerStride) *outBytesPerStride = -1;
+
+ 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));
+ }
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride);
+
+ // if hardware returns -1 for bytes per pixel or bytes per stride, we fail
+ // and unlock the buffer
+ if (bytesPerPixel == -1 || bytesPerStride == -1) {
+ gbuffer->unlock();
+ return INVALID_OPERATION;
+ }
+
+ if (outBytesPerPixel) *outBytesPerPixel = bytesPerPixel;
+ if (outBytesPerStride) *outBytesPerStride = bytesPerStride;
+ return result;
+}
+
int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
- int32_t fence, const ARect* rect, void** outVirtualAddress) {
+ 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 +199,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 +365,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 +445,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 +593,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 +604,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 +614,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 +672,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 +694,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 +710,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..da959e3 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,34 @@
int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
/**
+ * 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 +485,41 @@
#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);
+
+/**
+ * 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);
+#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..bad8b11 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -5,7 +5,10 @@
AHardwareBuffer_createFromHandle; # vndk
AHardwareBuffer_describe;
AHardwareBuffer_getNativeHandle; # vndk
+ AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
+ AHardwareBuffer_lockAndGetInfo; # introduced=29
+ AHardwareBuffer_lockPlanes; # 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..46a8e9e
--- /dev/null
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -0,0 +1,1499 @@
+/*
+ * 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 ((featureFlags & RenderEngine::ENABLE_PROTECTED_CONTEXT) &&
+ 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));
+
+ 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;
+ }
+
+ // release the fd and transfer the ownership to EGLSync
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), 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;
+ }
+
+ // 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) {
+ glScissor(region.left, region.top, 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 (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
+ glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
+ }
+}
+
+status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return cacheExternalTextureBufferLocked(buffer);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
+}
+
+status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ ATRACE_CALL();
+
+ if (mImageCache.count(buffer->getId()) > 0) {
+ 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());
+ return NO_INIT;
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+
+ return NO_ERROR;
+}
+
+status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
+ ATRACE_CALL();
+ status_t cacheResult = cacheExternalTextureBufferLocked(buffer);
+
+ if (cacheResult != NO_ERROR) {
+ return cacheResult;
+ }
+
+ auto cachedImage = mImageCache.find(buffer->getId());
+
+ if (cachedImage == mImageCache.end()) {
+ // We failed creating the image if we got here, so bail out.
+ bindExternalTextureImage(texName, *createImage());
+ return NO_INIT;
+ }
+
+ bindExternalTextureImage(texName, *cachedImage->second);
+
+ // 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;
+ }
+ }
+ }
+
+ 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;
+}
+
+void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
+ const LayerSettings& layer, const Mesh& mesh) {
+ // We separate the layer into 3 parts essentially, such that we only turn on blending for the
+ // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
+ FloatRect bounds = layer.geometry.roundedCornersCrop;
+
+ // Firstly, we need to convert the coordination from layer native coordination space to
+ // device coordination space.
+ const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform;
+ const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
+ const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
+ const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
+ const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
+ bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1],
+ rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]);
+
+ // Secondly, if the display is rotated, we need to undo the rotation on coordination and
+ // align the (left, top) and (right, bottom) coordination with the device coordination
+ // space.
+ switch (display.orientation) {
+ case ui::Transform::ROT_90:
+ std::swap(bounds.left, bounds.right);
+ break;
+ case ui::Transform::ROT_180:
+ std::swap(bounds.left, bounds.right);
+ std::swap(bounds.top, bounds.bottom);
+ break;
+ case ui::Transform::ROT_270:
+ std::swap(bounds.top, bounds.bottom);
+ break;
+ default:
+ break;
+ }
+
+ // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
+ // and the middle part without rounded corners.
+ const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
+ setScissor(topRect);
+ drawMesh(mesh);
+ const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
+ setScissor(bottomRect);
+ drawMesh(mesh);
+
+ // The middle part of the layer can turn off blending.
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ disableScissor();
+}
+
+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);
+ 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);
+
+ 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();
+
+ // 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,
+ bool useFramebufferCache) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
+ if (useFramebufferCache) {
+ for (const auto& image : mFramebufferImageCache) {
+ if (image.first == graphicBuffer->getId()) {
+ 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 (useFramebufferCache) {
+ if (image != EGL_NO_IMAGE_KHR) {
+ if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ }
+ mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
+ }
+ }
+ return image;
+}
+
+status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* const buffer,
+ const bool useFramebufferCache, 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, useFramebufferCache);
+
+ 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);
+
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
+ } else {
+ 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 supports protected context: %d\n",
+ supportsProtectedContent());
+ 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..de793c2
--- /dev/null
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -0,0 +1,263 @@
+/*
+ * 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 genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+ status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) 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, const bool useFramebufferCache,
+ 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,
+ bool useFramebufferCache);
+
+ // 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);
+ void setScissor(const Rect& region);
+ void disableScissor();
+ 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);
+
+ // We do a special handling for rounded corners when it's possible to turn off blending
+ // for the majority of the layer. The rounded corners needs to turn on blending such that
+ // we can set the alpha value correctly, however, only the corners need this, and since
+ // blending is an expensive operation, we want to turn off blending when it's not necessary.
+ void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
+ const 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;
+ // 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, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) REQUIRES(mRenderingMutex);
+ // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex
+ // is held.
+ status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer)
+ 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..dacf8d3
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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,
+ const bool useFramebufferCache) {
+ ATRACE_CALL();
+ if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ if (!usingFramebufferCache) {
+ eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+ }
+ mEGLImage = EGL_NO_IMAGE_KHR;
+ mBufferWidth = 0;
+ mBufferHeight = 0;
+ }
+
+ if (nativeBuffer) {
+ mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected,
+ useFramebufferCache);
+ if (mEGLImage == EGL_NO_IMAGE_KHR) {
+ return false;
+ }
+ usingFramebufferCache = useFramebufferCache;
+ 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..b7650bb
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -0,0 +1,59 @@
+/*
+ * 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,
+ const bool useFramebufferCache) 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;
+ bool usingFramebufferCache = false;
+ 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 82%
rename from services/surfaceflinger/RenderEngine/ProgramCache.cpp
rename to libs/renderengine/gl/ProgramCache.cpp
index 2073b05..086a324 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,29 +93,32 @@
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,
- Key::BLEND_PREMULT | Key::TEXTURE_EXT | Key::OUTPUT_TRANSFORM_MATRIX_ON |
- Key::INPUT_TF_SRGB | Key::OUTPUT_TF_SRGB);
- for (int i = 0; i < 4; i++) {
+ shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK |
+ Key::OUTPUT_TF_MASK,
+ Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB |
+ Key::OUTPUT_TF_SRGB);
+ for (int i = 0; i < 16; i++) {
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);
+
+ // Cache rounded corners
+ shaderKey.set(Key::ROUNDED_CORNERS_MASK,
+ (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
+
+ // Cache texture off option for window transition
+ shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF);
+ if (cache.count(shaderKey) == 0) {
+ cache.emplace(shaderKey, generateProgram(shaderKey));
shaderCount++;
}
}
@@ -132,31 +132,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 +176,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 +526,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 +537,9 @@
if (needs.isTexturing()) {
vs << "outTexCoords = (texture * texCoords).st;";
}
+ if (needs.hasRoundedCorners()) {
+ vs << "outCropCoords = cropCoords.st;";
+ }
vs << dedent << "}";
return vs.getString();
}
@@ -550,6 +561,29 @@
<< "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;
+ // Increase precision here so that a large corner radius doesn't
+ // cause floating point error
+ highp 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 +674,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 +703,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..9c9884a
--- /dev/null
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -0,0 +1,66 @@
+/*
+ * 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>
+#include <ui/Transform.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;
+
+ // The orientation of the physical display.
+ uint32_t orientation = ui::Transform::ROT_0;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h
new file mode 100644
index 0000000..6511127
--- /dev/null
+++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -0,0 +1,35 @@
+/*
+ * 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>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+
+class Framebuffer {
+public:
+ virtual ~Framebuffer() = default;
+
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
+ const bool useFramebufferCache) = 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..e707004
--- /dev/null
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -0,0 +1,249 @@
+/*
+ * 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
+
+ // Create a protected context when if possible
+ ENABLE_PROTECTED_CONTEXT = 1 << 2,
+ };
+
+ 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 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;
+ // Legacy public method used by devices that don't support native fence
+ // synchronization in their GPU driver, as this method provides implicit
+ // synchronization for latching buffers.
+ virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) = 0;
+ // Caches Image resources for this buffer, but does not bind the buffer to
+ // a particular texture.
+ virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 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 useFramebufferCache True if the framebuffer cache should be used.
+ // If an implementation does not cache output framebuffers, then this
+ // parameter does nothing.
+ // @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, const bool useFramebufferCache,
+ 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,
+ const bool useFramebufferCache)
+ : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
+ useFramebufferCache)
+ ? mEngine.bindFrameBuffer(mFramebuffer)
+ : NO_MEMORY;
+ }
+ ~BindNativeBufferAsFramebuffer() {
+ mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
+ 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 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/renderengine/include/renderengine/mock/Framebuffer.h
index e6ac6bf..dfb6a4e 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_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const 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..e33bcfd
--- /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_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_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&));
+ MOCK_METHOD3(bindExternalTextureBuffer,
+ status_t(uint32_t, const sp<GraphicBuffer>&, const 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_METHOD6(drawLayers,
+ status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+ ANativeWindowBuffer*, const bool, 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..7acaecf
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -0,0 +1,1040 @@
+/*
+ * 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(), true,
+ 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, true, 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(), true,
+ base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
+TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+ 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(), false,
+ base::unique_fd(), nullptr);
+ sCurrentBuffer = mBuffer;
+ ASSERT_EQ(NO_ERROR, status);
+ ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+ 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));
+}
+
+TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) {
+ status_t result = sRE->cacheExternalTextureBuffer(nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint64_t bufferId = buf->getId();
+ sRE->cacheExternalTextureBuffer(buf);
+ 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..139987e 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -22,6 +22,13 @@
#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
+/*
+ * The permission to use for activity recognition sensors (like step counter).
+ * See sensor types for more details on what sensors should require this
+ * permission.
+ */
+#define SENSOR_PERMISSION_ACTIVITY_RECOGNITION "android.permission.ACTIVITY_RECOGNITION"
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -116,7 +123,7 @@
mStringType = SENSOR_STRING_TYPE_HEART_RATE;
mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
AppOpsManager appOps;
- mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
+ mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission));
mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
} break;
case SENSOR_TYPE_LIGHT:
@@ -165,14 +172,22 @@
mFlags |= SENSOR_FLAG_WAKE_UP;
}
break;
- case SENSOR_TYPE_STEP_COUNTER:
+ case SENSOR_TYPE_STEP_COUNTER: {
mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
+ mRequiredPermission = SENSOR_PERMISSION_ACTIVITY_RECOGNITION;
+ AppOpsManager appOps;
+ mRequiredAppOp =
+ appOps.permissionToOpCode(String16(mRequiredPermission));
mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
- break;
- case SENSOR_TYPE_STEP_DETECTOR:
+ } break;
+ case SENSOR_TYPE_STEP_DETECTOR: {
mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
+ mRequiredPermission = SENSOR_PERMISSION_ACTIVITY_RECOGNITION;
+ AppOpsManager appOps;
+ mRequiredAppOp =
+ appOps.permissionToOpCode(String16(mRequiredPermission));
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
- break;
+ } break;
case SENSOR_TYPE_TEMPERATURE:
mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
@@ -318,7 +333,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..96d5eb9 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,8 +93,8 @@
}
SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
- // okay we're not locked here, but it's not needed during construction
+ : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+ Mutex::Autolock _l(mLock);
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..4a606ff
--- /dev/null
+++ b/libs/sensorprivacy/Android.bp
@@ -0,0 +1,55 @@
+// 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: [
+ ":libsensorprivacy_aidl",
+ "SensorPrivacyManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libhardware",
+ ],
+
+ export_include_dirs: ["include"],
+
+ export_shared_lib_headers: ["libbinder"],
+}
+
+filegroup {
+ name: "libsensorprivacy_aidl",
+ srcs: [
+ "aidl/android/hardware/ISensorPrivacyListener.aidl",
+ "aidl/android/hardware/ISensorPrivacyManager.aidl",
+ ],
+ path: "aidl",
+}
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 6f570af..2cc6857 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",
],
@@ -75,13 +82,17 @@
"frameworks/native/include",
],
+ // Uncomment the following line to enable VALIDATE_REGIONS traces
+ //defaults: ["libui-validate-regions-defaults"],
+
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",
"libhidlbase",
@@ -89,12 +100,11 @@
"libhwbinder",
"libsync",
"libutils",
- "libutilscallstack",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
],
static_libs: [
@@ -103,9 +113,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",
@@ -119,6 +152,7 @@
export_header_lib_headers: [
"libbase_headers",
"libnativebase_headers",
+ "libnativewindow_headers",
"libhardware_headers",
"libui_headers",
],
@@ -130,9 +164,23 @@
vendor_available: true,
target: {
vendor: {
+ cflags: ["-DLIBUI_IN_VNDK"],
override_export_include_dirs: ["include_vndk"],
},
},
+ header_libs: [
+ "libnativewindow_headers",
+ ],
+ export_header_lib_headers: [
+ "libnativewindow_headers",
+ ],
+}
+
+// defaults to enable VALIDATE_REGIONS traces
+cc_defaults {
+ name: "libui-validate-regions-defaults",
+ shared_libs: ["libutilscallstack"],
+ cflags: ["-DVALIDATE_REGIONS"],
}
subdirs = [
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/Fence.cpp b/libs/ui/Fence.cpp
index ed7ccb0b..4ce891e 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -110,7 +110,7 @@
}
struct sync_file_info* finfo = sync_file_info(mFenceFd);
- if (finfo == NULL) {
+ if (finfo == nullptr) {
ALOGE("sync_file_info returned NULL for fd %d", mFenceFd.get());
return SIGNAL_TIME_INVALID;
}
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 1414766..bdfe04b 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),
@@ -291,8 +279,8 @@
}
void FenceTimeline::updateSignalTimes() {
+ std::lock_guard<std::mutex> lock(mMutex);
while (!mQueue.empty()) {
- std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<FenceTime> fence = mQueue.front().lock();
if (!fence) {
// The shared_ptr no longer exists and no one cares about the
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..eb43765
--- /dev/null
+++ b/libs/ui/Gralloc3.cpp
@@ -0,0 +1,405 @@
+/*
+ * 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_2::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_2::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_2::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..3fc6a2d 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)
@@ -55,7 +76,7 @@
usage_deprecated = 0;
usage = 0;
layerCount = 0;
- handle = NULL;
+ handle = nullptr;
}
// deprecated
@@ -90,11 +111,31 @@
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();
}
+ for (auto& [callback, context] : mDeathCallbacks) {
+ callback(context, mId);
+ }
}
void GraphicBuffer::free_handle()
@@ -105,7 +146,7 @@
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
}
- handle = NULL;
+ handle = nullptr;
}
status_t GraphicBuffer::initCheck() const {
@@ -140,7 +181,7 @@
if (handle) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
- handle = 0;
+ handle = nullptr;
}
return initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, "[Reallocation]");
}
@@ -153,6 +194,7 @@
if (inFormat != format) return true;
if (inLayerCount != layerCount) return true;
if ((usage & inUsage) != inUsage) return true;
+ if ((usage & USAGE_PROTECTED) != (inUsage & USAGE_PROTECTED)) return true;
return false;
}
@@ -217,15 +259,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 +275,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 +309,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 +332,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,15 +367,37 @@
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 {
+#ifndef LIBUI_IN_VNDK
+ if (mBufferHubBuffer != nullptr) {
+ return 48;
+ }
+#endif
return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
+#ifndef LIBUI_IN_VNDK
+ if (mBufferHubBuffer != nullptr) {
+ return 0;
+ }
+#endif
return static_cast<size_t>(handle ? mTransportNumFds : 0);
}
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
+#ifndef LIBUI_IN_VNDK
+ if (mBufferHubBuffer != nullptr) {
+ return flattenBufferHubBuffer(buffer, size);
+ }
+#endif
size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
if (size < sizeNeeded) return NO_MEMORY;
@@ -355,7 +424,7 @@
buf[11] = int32_t(mTransportNumInts);
memcpy(fds, handle->data, static_cast<size_t>(mTransportNumFds) * sizeof(int));
memcpy(buf + 13, handle->data + handle->numFds,
- static_cast<size_t>(mTransportNumInts) * sizeof(int));
+ static_cast<size_t>(mTransportNumInts) * sizeof(int));
}
buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
@@ -364,14 +433,13 @@
fds += mTransportNumFds;
count -= static_cast<size_t>(mTransportNumFds);
}
-
return NO_ERROR;
}
-status_t GraphicBuffer::unflatten(
- void const*& buffer, size_t& size, int const*& fds, size_t& count) {
- if (size < 12 * sizeof(int)) {
- android_errorWriteLog(0x534e4554, "114223584");
+status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count) {
+ // Check if size is not smaller than buf[0] is supposed to take.
+ if (size < sizeof(int)) {
return NO_MEMORY;
}
@@ -386,10 +454,21 @@
} else if (buf[0] == 'GBFR') {
// old version, when usage bits were 32-bits
flattenWordCount = 12;
+ } else if (buf[0] == 'BHBB') { // BufferHub backed buffer.
+#ifndef LIBUI_IN_VNDK
+ return unflattenBufferHubBuffer(buffer, size);
+#else
+ return BAD_TYPE;
+#endif
} else {
return BAD_TYPE;
}
+ if (size < 12 * sizeof(int)) {
+ android_errorWriteLog(0x534e4554, "114223584");
+ return NO_MEMORY;
+ }
+
const size_t numFds = static_cast<size_t>(buf[10]);
const size_t numInts = static_cast<size_t>(buf[11]);
@@ -402,7 +481,7 @@
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
usage = 0;
- handle = NULL;
+ handle = nullptr;
ALOGE("unflatten: numFds or numInts is too large: %zd, %zd", numFds, numInts);
return BAD_VALUE;
}
@@ -430,13 +509,13 @@
} else {
usage = uint64_t(usage_deprecated);
}
- native_handle* h = native_handle_create(
- static_cast<int>(numFds), static_cast<int>(numInts));
+ native_handle* h =
+ native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
if (!h) {
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
usage = 0;
- handle = NULL;
+ handle = nullptr;
ALOGE("unflatten: native_handle_create failed");
return NO_MEMORY;
}
@@ -447,7 +526,7 @@
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
usage = 0;
- handle = NULL;
+ handle = nullptr;
}
mId = static_cast<uint64_t>(buf[7]) << 32;
@@ -457,7 +536,7 @@
mOwner = ownHandle;
- if (handle != 0) {
+ if (handle != nullptr) {
buffer_handle_t importedHandle;
status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height),
uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle);
@@ -465,7 +544,7 @@
width = height = stride = format = usage_deprecated = 0;
layerCount = 0;
usage = 0;
- handle = NULL;
+ handle = nullptr;
ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err);
return err;
}
@@ -480,28 +559,83 @@
size -= sizeNeeded;
fds += numFds;
count -= numFds;
-
return NO_ERROR;
}
-bool GraphicBuffer::isDetachedBuffer() const {
- return mDetachedBufferHandle && mDetachedBufferHandle->isValid();
+void GraphicBuffer::addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context) {
+ mDeathCallbacks.emplace_back(deathCallback, context);
}
-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.");
+#ifndef LIBUI_IN_VNDK
+status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const {
+ sp<NativeHandle> tokenHandle = mBufferHubBuffer->duplicate();
+ if (tokenHandle == nullptr || tokenHandle->handle() == nullptr ||
+ tokenHandle->handle()->numFds != 0) {
+ return BAD_VALUE;
}
- mDetachedBufferHandle = std::move(channel);
+ // Size needed for one label, one number of ints inside the token, one generation number and
+ // the token itself.
+ int numIntsInToken = tokenHandle->handle()->numInts;
+ const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int);
+ if (size < sizeNeeded) {
+ ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__,
+ static_cast<int>(sizeNeeded), static_cast<int>(size));
+ return NO_MEMORY;
+ }
+ size -= sizeNeeded;
+
+ int* buf = static_cast<int*>(buffer);
+ buf[0] = 'BHBB';
+ buf[1] = numIntsInToken;
+ memcpy(buf + 2, tokenHandle->handle()->data, static_cast<size_t>(numIntsInToken) * sizeof(int));
+ buf[2 + numIntsInToken] = static_cast<int32_t>(mGenerationNumber);
+
return NO_ERROR;
}
-std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() {
- return std::move(mDetachedBufferHandle);
+status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) {
+ const int* buf = static_cast<const int*>(buffer);
+ int numIntsInToken = buf[1];
+ // Size needed for one label, one number of ints inside the token, one generation number and
+ // the token itself.
+ const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int);
+ if (size < sizeNeeded) {
+ ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__,
+ static_cast<int>(sizeNeeded), static_cast<int>(size));
+ return NO_MEMORY;
+ }
+ size -= sizeNeeded;
+ native_handle_t* importToken = native_handle_create(/*numFds=*/0, /*numInts=*/numIntsInToken);
+ memcpy(importToken->data, buf + 2, static_cast<size_t>(buf[1]) * sizeof(int));
+ sp<NativeHandle> importTokenHandle = NativeHandle::create(importToken, /*ownHandle=*/true);
+ std::unique_ptr<BufferHubBuffer> bufferHubBuffer = BufferHubBuffer::import(importTokenHandle);
+ if (bufferHubBuffer == nullptr || bufferHubBuffer.get() == nullptr) {
+ return BAD_VALUE;
+ }
+ // Reconstruct this GraphicBuffer object using the new BufferHubBuffer object.
+ if (handle) {
+ free_handle();
+ }
+ mId = 0;
+ mGenerationNumber = static_cast<uint32_t>(buf[2 + numIntsInToken]);
+ mInitCheck =
+ initWithHandle(bufferHubBuffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+ bufferHubBuffer->desc().width, bufferHubBuffer->desc().height,
+ static_cast<PixelFormat>(bufferHubBuffer->desc().format),
+ bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage,
+ bufferHubBuffer->desc().stride);
+ mBufferId = bufferHubBuffer->id();
+ mBufferHubBuffer.reset(std::move(bufferHubBuffer.get()));
+
+ return NO_ERROR;
}
+bool GraphicBuffer::isBufferHubBuffer() const {
+ return mBufferHubBuffer != nullptr;
+}
+#endif // LIBUI_IN_VNDK
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index eaba1ed..0861a1f 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -24,72 +24,82 @@
#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
-{
+size_t GraphicBufferAllocator::getTotalSize() const {
+ Mutex::Autolock _l(sLock);
+ size_t total = 0;
+ for (size_t i = 0; i < sAllocList.size(); ++i) {
+ total += sAllocList.valueAt(i).size;
+ }
+ return total;
+}
+
+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 +118,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..55e3b99 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -19,9 +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>
#include <ui/Region.h>
@@ -30,10 +30,18 @@
#include <private/ui/RegionHelper.h>
// ----------------------------------------------------------------------------
-#define VALIDATE_REGIONS (false)
+
+// ### VALIDATE_REGIONS ###
+// To enable VALIDATE_REGIONS traces, use the "libui-validate-regions-defaults"
+// in Android.bp. Do not #define VALIDATE_REGIONS here as it requires extra libs.
+
#define VALIDATE_WITH_CORECG (false)
// ----------------------------------------------------------------------------
+#if defined(VALIDATE_REGIONS)
+#include <utils/CallStack.h>
+#endif
+
#if VALIDATE_WITH_CORECG
#include <core/SkRegion.h>
#endif
@@ -41,6 +49,8 @@
namespace android {
// ----------------------------------------------------------------------------
+using base::StringAppendF;
+
enum {
op_nand = region_operator<Rect>::op_nand,
op_and = region_operator<Rect>::op_and,
@@ -64,7 +74,7 @@
Region::Region(const Region& rhs)
: mStorage(rhs.mStorage)
{
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(rhs, "rhs copy-ctor");
#endif
}
@@ -200,7 +210,7 @@
outputRegion.mStorage, direction_LTR);
outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(outputRegion, "T-Junction free region");
#endif
@@ -209,7 +219,7 @@
Region& Region::operator = (const Region& rhs)
{
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(*this, "this->operator=");
validate(rhs, "rhs.operator=");
#endif
@@ -325,6 +335,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 {
@@ -582,10 +606,12 @@
result = false;
ALOGE_IF(!silent, "%s: mStorage size is 2, which is never valid", name);
}
+#if defined(VALIDATE_REGIONS)
if (result == false && !silent) {
reg.dump(name);
CallStack stack(LOG_TAG);
}
+#endif
return result;
}
@@ -593,7 +619,7 @@
const Region& lhs,
const Region& rhs, int dx, int dy)
{
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(lhs, "boolean_operation (before): lhs");
validate(rhs, "boolean_operation (before): rhs");
validate(dst, "boolean_operation (before): dst");
@@ -613,7 +639,7 @@
operation(r);
}
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(lhs, "boolean_operation: lhs");
validate(rhs, "boolean_operation: rhs");
validate(dst, "boolean_operation: dst");
@@ -711,7 +737,7 @@
return;
}
-#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS
+#if VALIDATE_WITH_CORECG || defined(VALIDATE_REGIONS)
boolean_operation(op, dst, lhs, Region(rhs), dx, dy);
#else
size_t lhs_count;
@@ -743,7 +769,7 @@
void Region::translate(Region& reg, int dx, int dy)
{
if ((dx || dy) && !reg.isEmpty()) {
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(reg, "translate (before)");
#endif
size_t count = reg.mStorage.size();
@@ -753,7 +779,7 @@
rects++;
count--;
}
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(reg, "translate (after)");
#endif
}
@@ -772,7 +798,7 @@
}
status_t Region::flatten(void* buffer, size_t size) const {
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(*this, "Region::flatten");
#endif
if (size < getFlattenedSize()) {
@@ -803,7 +829,7 @@
}
if (numRects > (UINT32_MAX / sizeof(Rect))) {
- android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, NULL, 0);
+ android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, nullptr, 0);
return NO_MEMORY;
}
@@ -819,7 +845,7 @@
result.mStorage.push_back(rect);
}
-#if VALIDATE_REGIONS
+#if defined(VALIDATE_REGIONS)
validate(result, "Region::unflatten");
#endif
@@ -854,16 +880,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/BufferQueueDefs.h b/libs/ui/include/ui/BufferQueueDefs.h
index 56de181..0fecda9 100644
--- a/libs/ui/include/ui/BufferQueueDefs.h
+++ b/libs/ui/include/ui/BufferQueueDefs.h
@@ -23,6 +23,16 @@
// Attempts at runtime to increase the number of buffers past this
// will fail.
static constexpr int NUM_BUFFER_SLOTS = 64;
+
+ enum {
+ // A flag returned by dequeueBuffer when the client needs to call
+ // requestBuffer immediately thereafter.
+ BUFFER_NEEDS_REALLOCATION = 0x1,
+ // A flag returned by dequeueBuffer when all mirrored slots should be
+ // released by the client. This flag should always be processed first.
+ RELEASE_ALL_BUFFERS = 0x2,
+ };
+
} // namespace BufferQueueDefs
} // namespace android
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..ecba7f7 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -19,6 +19,7 @@
#include <ui/Fence.h>
#include <utils/Flattenable.h>
+#include <utils/Mutex.h>
#include <utils/Timers.h>
#include <atomic>
@@ -113,11 +114,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.
@@ -164,7 +160,7 @@
private:
mutable std::mutex mMutex;
- std::queue<std::weak_ptr<FenceTime>> mQueue;
+ std::queue<std::weak_ptr<FenceTime>> mQueue GUARDED_BY(mMutex);
};
// Used by test code to create or get FenceTimes for a given Fence.
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..c195342 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -21,8 +21,12 @@
#include <sys/types.h>
#include <string>
+#include <utility>
+#include <vector>
+#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,9 +38,14 @@
namespace android {
-class DetachedBufferHandle;
+#ifndef LIBUI_IN_VNDK
+class BufferHubBuffer;
+#endif // LIBUI_IN_VNDK
+
class GraphicBufferMapper;
+using GraphicBufferDeathCallback = std::function<void(void* /*context*/, uint64_t bufferId)>;
+
// ===========================================================================
// GraphicBuffer
// ===========================================================================
@@ -75,6 +84,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 +147,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 +163,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 +179,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 +219,16 @@
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();
+ }
+
+ void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context);
+
+#ifndef LIBUI_IN_VNDK
+ // Returns whether this GraphicBuffer is backed by BufferHubBuffer.
+ bool isBufferHubBuffer() const;
+#endif // LIBUI_IN_VNDK
private:
~GraphicBuffer();
@@ -239,21 +275,46 @@
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;
+ // Send a callback when a GraphicBuffer dies.
+ //
+ // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When
+ // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes
+ // problems when trying to implicitcly cache across process boundaries. Ideally, both sides
+ // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer
+ // would be destroyed. Unfortunately, when SurfaceFlinger has only a wp<> reference to the
+ // GraphicBuffer, it immediately goes out of scope in the SurfaceFlinger process. SurfaceFlinger
+ // must hold onto a sp<> to the buffer. When the GraphicBuffer goes out of scope in the app's
+ // process, the client side cache will get this callback. It erases the buffer from its cache
+ // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer.
+ std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>>
+ mDeathCallbacks;
+
+#ifndef LIBUI_IN_VNDK
+ // Flatten this GraphicBuffer object if backed by BufferHubBuffer.
+ status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const;
+
+ // Unflatten into BufferHubBuffer backed GraphicBuffer.
+ // Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a
+ // GraphicBuffer backed by BufferHubBuffer_1 flatten in process/thread A, transport the token
+ // to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the
+ // token. Race condition occurs between the invalidation of the token in bufferhub process and
+ // process/thread B trying to unflatten and import the buffer with that token.
+ status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size);
+
+ // 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..25d4512 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,9 @@
status_t free(buffer_handle_t handle);
- void dump(String8& res) const;
+ size_t getTotalSize() const;
+
+ void dump(std::string& res) const;
static void dumpToSystemLog();
private:
@@ -76,7 +74,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..0c73a72
--- /dev/null
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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/frameworks/bufferhub/1.0/IBufferHub.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 ::android::frameworks::bufferhub::V1_0::IBufferHub;
+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();
+
+ if (!BufferHubServiceRunning()) {
+ // TODO(b/112940221): Enforce the test cross all devices once BufferHub lands in Android
+ // R for all Android varieties.
+ GTEST_SKIP() << "Skip test as the BufferHub service is not running.";
+ }
+ }
+
+ bool BufferHubServiceRunning() {
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ return bufferhub.get() != nullptr;
+ }
+};
+
+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();
+
+ if (IsSkipped()) {
+ // If the base class' SetUp() stated the test should be skipped, we should short
+ // circuit this sub-class' logic.
+ return;
+ }
+
+ 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..a7c248c 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,88 @@
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());
+ sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+ EXPECT_TRUE(gb->isBufferHubBuffer());
+ EXPECT_EQ(gb->getBufferId(), b1_id);
+}
- buffer->setDetachedBufferHandle(std::move(handle));
- EXPECT_TRUE(handle == nullptr);
- EXPECT_TRUE(buffer->isDetachedBuffer());
+TEST_F(GraphicBufferTest, flattenAndUnflatten) {
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ kTestUsage, /*userMetadataSize=*/0);
+ ASSERT_NE(b1, nullptr);
+ sp<GraphicBuffer> gb1(new GraphicBuffer(std::move(b1)));
+ gb1->setGenerationNumber(42);
- handle = buffer->takeDetachedBufferHandle();
- EXPECT_TRUE(handle != nullptr);
- EXPECT_TRUE(handle->isValid());
- EXPECT_FALSE(buffer->isDetachedBuffer());
+ size_t flattenedSize = gb1->getFlattenedSize();
+ EXPECT_EQ(flattenedSize, 48);
+ size_t fdCount = gb1->getFdCount();
+ EXPECT_EQ(fdCount, 0);
+
+ int data[flattenedSize];
+ int fds[0];
+
+ // Make copies of needed items since flatten modifies them.
+ size_t flattenedSizeCopy = flattenedSize;
+ size_t fdCountCopy = fdCount;
+ void* dataStart = data;
+ int* fdsStart = fds;
+ status_t err = gb1->flatten(dataStart, flattenedSizeCopy, fdsStart, fdCountCopy);
+ ASSERT_EQ(err, NO_ERROR);
+ EXPECT_EQ(flattenedSizeCopy, 0);
+ EXPECT_EQ(fdCountCopy, 0);
+
+ size_t unflattenSize = flattenedSize;
+ size_t unflattenFdCount = fdCount;
+ const void* unflattenData = static_cast<const void*>(dataStart);
+ const int* unflattenFdData = static_cast<const int*>(fdsStart);
+
+ GraphicBuffer* gb2 = new GraphicBuffer();
+ err = gb2->unflatten(unflattenData, unflattenSize, unflattenFdData, unflattenFdCount);
+ ASSERT_EQ(err, NO_ERROR);
+ EXPECT_TRUE(gb2->isBufferHubBuffer());
+
+ EXPECT_EQ(gb2->getWidth(), kTestWidth);
+ EXPECT_EQ(gb2->getHeight(), kTestHeight);
+ EXPECT_EQ(static_cast<uint32_t>(gb2->getPixelFormat()), kTestFormat);
+ EXPECT_EQ(gb2->getUsage(), kTestUsage);
+ EXPECT_EQ(gb2->getLayerCount(), kTestLayerCount);
+ EXPECT_EQ(gb1->getBufferId(), gb2->getBufferId());
+ EXPECT_EQ(gb2->getGenerationNumber(), 42);
}
} // 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/ui/tools/lutgen.cpp b/libs/ui/tools/lutgen.cpp
index 97b0822..85a1ceb 100644
--- a/libs/ui/tools/lutgen.cpp
+++ b/libs/ui/tools/lutgen.cpp
@@ -85,11 +85,11 @@
static int handleCommandLineArgments(int argc, char* argv[]) {
static constexpr const char* OPTSTR = "h:d:s:t:";
static const struct option OPTIONS[] = {
- { "help", no_argument, 0, 'h' },
- { "dimension", required_argument, 0, 'd' },
- { "source", required_argument, 0, 's' },
- { "target", required_argument, 0, 't' },
- { 0, 0, 0, 0 } // termination of the option list
+ { "help", no_argument, nullptr, 'h' },
+ { "dimension", required_argument, nullptr, 'd' },
+ { "source", required_argument, nullptr, 's' },
+ { "target", required_argument, nullptr, 't' },
+ { nullptr, 0, nullptr, 0 } // termination of the option list
};
int opt;
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..6d202ae 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,11 +52,11 @@
"-Wall",
"-Werror",
],
- export_include_dirs: localIncludeFiles,
shared_libs: sharedLibraries,
header_libs: headerLibraries,
name: "libbufferhub",
export_header_lib_headers: [
+ "libbufferhub_headers",
"libnativebase_headers",
],
}
@@ -65,4 +68,3 @@
header_libs: headerLibraries,
name: "buffer_hub-test",
}
-
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/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..3260447 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",
@@ -52,3 +50,55 @@
],
name: "dvr_api-test",
}
+
+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..7b3717e 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>
@@ -27,6 +28,8 @@
namespace {
+using ::testing::Test;
+
DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
DvrSurfaceAttribute attribute;
attribute.key = key;
@@ -140,6 +143,23 @@
return {UniqueDvrWriteBufferQueue(queue)};
}
+Status<std::vector<uint8_t>> GetConfigData(int config_type) {
+ uint8_t* data = nullptr;
+ size_t data_size = 0;
+ int error = dvrConfigurationDataGet(config_type, &data, &data_size);
+ if (error < 0) {
+ return ErrorStatus(-error);
+ }
+
+ if (!data || data_size == 0) {
+ return ErrorStatus(EINVAL);
+ }
+ std::vector<uint8_t> data_result(data, data + data_size);
+ dvrConfigurationDataDestroy(data);
+ std::string s(data, data + data_size);
+ return {std::move(data_result)};
+}
+
class TestDisplayManager {
public:
TestDisplayManager(UniqueDvrDisplayManager display_manager,
@@ -274,23 +294,6 @@
return {std::move(queue_ids)};
}
- Status<std::vector<uint8_t>> GetConfigData(int config_type) {
- uint8_t* data = nullptr;
- size_t data_size = 0;
- int error = dvrConfigurationDataGet(config_type, &data, &data_size);
- if (error < 0) {
- return ErrorStatus(-error);
- }
-
- if (!data || data_size == 0) {
- return ErrorStatus(EINVAL);
- }
- std::vector<uint8_t> data_result(data, data + data_size);
- dvrConfigurationDataDestroy(data);
- std::string s(data, data + data_size);
- return {std::move(data_result)};
- }
-
private:
UniqueDvrDisplayManager display_manager_;
UniqueDvrSurfaceState surface_state_;
@@ -302,9 +305,17 @@
void operator=(const TestDisplayManager&) = delete;
};
-class DvrDisplayManagerTest : public ::testing::Test {
+class DvrDisplayManagerTest : public Test {
protected:
void SetUp() override {
+ // dvr display manager test doesn't apply to standalone vr devices because
+ // tests cannot create display manager client on these devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ GTEST_SKIP()
+ << "All tests in DvrDisplayManagerTest test case are skipped "
+ "because the device boot to VR.";
+ }
+
int ret;
DvrDisplayManager* display_manager;
DvrSurfaceState* surface_state;
@@ -428,7 +439,7 @@
#if 0
// Verify utility predicate/macro functionality. This section is commented out
// because it is designed to fail in some cases to validate the helpers.
-TEST_F(DvrDisplayManagerTest, ExpectVoid) {
+TEST_F(Test, ExpectVoid) {
Status<void> status_error{ErrorStatus{EINVAL}};
Status<void> status_ok{};
@@ -443,7 +454,7 @@
EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
}
-TEST_F(DvrDisplayManagerTest, ExpectInt) {
+TEST_F(Test, ExpectInt) {
Status<int> status_error{ErrorStatus{EINVAL}};
Status<int> status_ok{10};
@@ -863,16 +874,17 @@
dvrWriteBufferDestroy(buffer);
}
-TEST_F(DvrDisplayManagerTest, ConfigurationData) {
- // TODO(hendrikw): Move this out of the display manager tests.
- auto data1 = manager_->GetConfigData(-1);
+TEST_F(Test, ConfigurationData) {
+ // TODO(hendrikw): Move this test and GetConfigData helper function out of the
+ // display manager tests.
+ auto data1 = GetConfigData(-1);
ASSERT_STATUS_ERROR(data1);
const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
// This should be run on devices with and without built in metrics.
bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty();
- auto data2 = manager_->GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
+ auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
if (has_metric) {
ASSERT_STATUS_OK(data2);
ASSERT_NE(0u, data2.get().size());
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..410e234
--- /dev/null
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -0,0 +1,36 @@
+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",
+}
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 75dda6d..abc7a72 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/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md
new file mode 100644
index 0000000..bfc44db
--- /dev/null
+++ b/opengl/libs/EGL/GLES_layers.md
@@ -0,0 +1,258 @@
+# GLES Layers
+
+## EGL Loader Initialization
+After standard entrypoints have all been populated unmodified, a GLES LayerLoader will be instantiated. If debug layers are enabled, the LayerLoader will scan specified directories for layers, just like the Vulkan loader does.
+
+If layering is enabled, the loader will search for and enumerate a specified layer list. The layer list will be specified by colon separated filenames (see [Enabling layers](#Enabling-layers) below).
+
+The layers will be traversed in the order they are specified, so the first layer will be directly below the application. For each layer, it will track two entrypoints from the layer. `AndroidGLESLayer_Initialize` and `AndroidGLESLayer_GetProcAddress`.
+```cpp
+typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
+```
+
+`AndroidGLESLayer_Initialize` is a new function that provides an identifier for the layer to use (layer_id) and an entrypoint that can be called to look up functions below the layer. The entrypoint can be used like so:
+```cpp
+const char* func = "eglFoo";
+void* gpa = get_next_layer_proc_address(layer_id, func);
+```
+
+Note that only GLES2+ entrypoints will be provided. If a layer tries to make independent GLES 1.x calls, they will be routed to GLES2+ libraries, which may not behave as expected. Application calls to 1.x will not be affected.
+
+AndroidGLESLayer_GetProcAddress is a new function designed for this layering system. It takes the address of the next call in the chain that the layer should call when finished. If there is only one layer, next will point directly to the driver for most functions.
+```cpp
+void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
+```
+
+For each layer found, the GLES LayerLoader will call `AndroidGLESLayer_Initialize`, and then walk libEGL’s function lists and call `AndroidGLESLayer_GetProcAddress` for all known functions. The layer can track that next address with any means it wants. If the layer does not intercept the function, `AndroidGLESLayer_GetProcAddress` must return the same function address it was passed. The LayerLoader will then update the function hook list to point to the layer’s entrypoint.
+
+The layers are not required to do anything with the info provided by `AndroidGLESLayer_Initialize` or get_next_layer_proc_address, but providing them makes it easier for existing layers (like GAPID and RenderDoc) to support Android. That way a layer can look up functions independently (i.e. not wait for calls to `AndroidGLESLayer_GetProcAddress`). Layers must be sure to use gen_next_layer_proc_address if they look up function calls instead of eglGetProcAddress or they will not get an accurate answer. eglGetProcAddress must be passed down the chain to the platform.
+
+## Placing layers
+
+Where layers can be found, in order of priority
+ 1. System location for root
+ This requires root access
+ ```bash
+ adb root
+ adb disable-verity
+ adb reboot
+ adb root
+ adb shell setenforce 0
+ adb shell mkdir -p /data/local/debug/gles
+ adb push <layer>.so /data/local/debug/gles/
+ ```
+
+ 2. Application's base directory
+ Target application must be debuggable, or you must have root access:
+ ```bash
+ adb push libGLTrace.so /data/local/tmp
+ adb shell run-as com.android.gl2jni cp /data/local/tmp/libGLTrace.so .
+ adb shell run-as com.android.gl2jni ls | grep libGLTrace
+ libGLTrace.so
+ ```
+
+ 3. External APK
+ Determine the ABI of your target application, then install an APK containing the layers you wish to load:
+ ```bash
+ adb install --abi armeabi-v7a layers.apk
+ ```
+
+ 4. In the target application's APK
+
+## Enabling layers
+
+### Per application
+Note these settings will persist across reboots:
+```bash
+# Enable layers
+adb shell settings put global enable_gpu_debug_layers 1
+
+# Specify target application
+adb shell settings put global gpu_debug_app <package_name>
+
+# Specify layer list (from top to bottom)
+adb shell settings put global gpu_debug_layers_gles <layer1:layer2:layerN>
+
+# Specify a package to search for layers
+adb shell settings put global gpu_debug_layer_app <layer_package>
+```
+To disable the per-app layers:
+```
+adb shell settings delete global enable_gpu_debug_layers
+adb shell settings delete global gpu_debug_app
+adb shell settings delete global gpu_debug_layers_gles
+adb shell settings delete global gpu_debug_layer_app
+```
+
+### Globally
+These will be cleared on reboot:
+```bash
+# This will attempt to load layers for all applications, including native executables
+adb shell setprop debug.gles.layers <layer1:layer2:layerN>
+```
+
+
+## Creating a layer
+
+Layers must expose the following two functions described above:
+```cpp
+AndroidGLESLayer_Initialize
+AndroidGLESLayer_GetProcAddress
+```
+
+For a simple layer that just wants to intercept a handful of functions, a passively initialized layer is the way to go. It can simply wait for the EGL Loader to initialize the function it cares about. See below for an example of creating a passive layer.
+
+For more formalized layers that need to fully initialize up front, or layers that needs to look up extensions not known to the EGL loader, active layer initialization is the way to go. The layer can utilize get_next_layer_proc_address provided by `AndroidGLESLayer_Initialize` to look up a function at any time. The layer must still respond to `AndroidGLESLayer_GetProcAddress` requests from the loader so the platform knows where to route calls. See below for an example of creating an active layer.
+
+### Example Passive Layer Initialization
+```cpp
+namespace {
+
+std::unordered_map<std::string, EGLFuncPointer> funcMap;
+
+EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig (
+ EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+
+ EGLFuncPointer entry = funcMap["eglChooseConfig"];
+
+ typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+ EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
+
+ PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry);
+
+ return next(dpy, attrib_list, configs, config_size, num_config);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) {
+
+ #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \
+ return (EGLFuncPointer)glesLayer_##func; }
+
+ GETPROCADDR(eglChooseConfig);
+
+ // Don't return anything for unrecognized functions
+ return nullptr;
+}
+
+EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ // This function is purposefully empty, since this layer does not proactively
+ // look up any entrypoints
+ }
+
+EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress(
+ const char* funcName, EGLFuncPointer next) {
+ EGLFuncPointer entry = eglGPA(funcName);
+ if (entry != nullptr) {
+ funcMap[std::string(funcName)] = next;
+ return entry;
+ }
+ return next;
+}
+
+} // namespace
+
+extern "C" {
+ __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address);
+ }
+ __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres(
+ const char *funcName, EGLFuncPointer next) {
+ return (void*)glesLayer_GetLayerProcAddress(funcName, next);
+ }
+}
+```
+
+### Example Active Layer Initialization
+```cpp
+namespace {
+
+std::unordered_map<std::string, EGLFuncPointer> funcMap;
+
+EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig (
+ EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+
+ EGLFuncPointer entry = funcMap["eglChooseConfig"];
+
+ typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+ EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
+
+ PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry);
+
+ return next(dpy, attrib_list, configs, config_size, num_config);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) {
+
+ #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \
+ return (EGLFuncPointer)glesLayer_##func; }
+
+ GETPROCADDR(eglChooseConfig);
+
+ // Don't return anything for unrecognized functions
+ return nullptr;
+}
+
+EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+
+ // Note: This is where the layer would populate its function map with all the
+ // functions it cares about
+ const char* func = “eglChooseConfig”;
+ funcMap[func] = get_next_layer_proc_address(layer_id, func);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress(
+ const char* funcName, EGLFuncPointer next) {
+ EGLFuncPointer entry = eglGPA(funcName);
+ if (entry != nullptr) {
+ return entry;
+ }
+
+ return next;
+}
+
+} // namespace
+
+extern "C" {
+ __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address);
+ }
+ __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres(
+ const char *funcName, EGLFuncPointer next) {
+ return (void*)glesLayer_GetLayerProcAddress(funcName, next);
+ }
+}
+```
+
+## Caveats
+Only supports GLES 2.0+.
+
+When layering is enabled, GLES 1.x exclusive functions will continue to route to GLES 1.x drivers. But functions shared with GLES 2.0+ (like glGetString) will be routed to 2.0+ drivers, which can cause confusion.
+
+## FAQ
+ - Who can use layers?
+ - GLES Layers can be loaded by any debuggable application, or for any application if you have root access
+ - How do we know if layers are working on a device?
+ - This feature is backed by Android CTS, so you can run `atest CtsGpuToolsHostTestCases`
+ - How does a app determine if this feature is supported?
+ - There are two ways. First you can check against the version of Android.
+ ```bash
+ # Q is the first that will support this, so look for `Q` or 10 for release
+ adb shell getprop ro.build.version.sdk
+ # Or look for the SDK version, which should be 29 for Q
+ adb shell getprop ro.build.version.sdk
+ ```
+ - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system:
+ ```cpp
+ std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos)
+ {
+ // Layers are supported!
+ }
+ ```
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 6f050bf..038a432 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,23 +27,19 @@
#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"
+#include <EGL/eglext_angle.h>
-extern "C" {
- android_namespace_t* android_get_exported_namespace(const char*);
-}
-
-// ----------------------------------------------------------------------------
namespace android {
-// ----------------------------------------------------------------------------
-
/*
* EGL userspace drivers must be provided either:
@@ -73,41 +69,6 @@
return loader;
}
-/* This function is called to check whether we run inside the emulator,
- * and if this is the case whether GLES GPU emulation is supported.
- *
- * Returned values are:
- * -1 -> not running inside the emulator
- * 0 -> running inside the emulator, but GPU emulation not supported
- * 1 -> running inside the emulator, GPU emulation is supported
- * through the "emulation" host-side OpenGL ES implementation.
- * 2 -> running inside the emulator, GPU emulation is supported
- * through a guest-side vendor driver's OpenGL ES implementation.
- */
-static int
-checkGlesEmulationStatus(void)
-{
- /* We're going to check for the following kernel parameters:
- *
- * qemu=1 -> tells us that we run inside the emulator
- * android.qemu.gles=<number> -> tells us the GLES GPU emulation status
- *
- * Note that we will return <number> if we find it. This let us support
- * more additionnal emulation modes in the future.
- */
- char prop[PROPERTY_VALUE_MAX];
- int result = -1;
-
- /* First, check for qemu=1 */
- property_get("ro.kernel.qemu",prop,"0");
- if (atoi(prop) != 1)
- return -1;
-
- /* We are in the emulator, get GPU status value */
- property_get("qemu.gles",prop,"0");
- return atoi(prop);
-}
-
static void* do_dlopen(const char* path, int mode) {
ATRACE_CALL();
return dlopen(path, mode);
@@ -123,13 +84,16 @@
return android_load_sphal_library(path, mode);
}
-// ----------------------------------------------------------------------------
+static int do_android_unload_sphal_library(void* dso) {
+ ATRACE_CALL();
+ return android_unload_sphal_library(dso);
+}
Loader::driver_t::driver_t(void* gles)
{
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 +101,7 @@
for (size_t i=0 ; i<NELEM(dso) ; i++) {
if (dso[i]) {
dlclose(dso[i]);
- dso[i] = 0;
+ dso[i] = nullptr;
}
}
}
@@ -160,10 +124,8 @@
return 0;
}
-// ----------------------------------------------------------------------------
-
Loader::Loader()
- : getProcAddress(NULL)
+ : getProcAddress(nullptr)
{
}
@@ -216,33 +178,161 @@
}
}
+static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
+
+static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
+ DRIVER_SUFFIX_PROPERTY,
+ "ro.board.platform",
+};
+
+static bool should_unload_system_driver(egl_connection_t* cnx) {
+ // Return false if the system driver has been unloaded once.
+ if (cnx->systemDriverUnloaded) {
+ return false;
+ }
+
+ // Return true if Angle namespace is set.
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
+ if (ns) {
+ return true;
+ }
+
+#ifndef __ANDROID_VNDK__
+ // Return true if updated driver namespace is set.
+ ns = android::GraphicsEnv::getInstance().getDriverNamespace();
+ if (ns) {
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+static void uninit_api(char const* const* api, __eglMustCastToProperFunctionPointerType* curr) {
+ while (*api) {
+ *curr++ = nullptr;
+ api++;
+ }
+}
+
+void Loader::unload_system_driver(egl_connection_t* cnx) {
+ ATRACE_CALL();
+
+ uninit_api(gl_names,
+ (__eglMustCastToProperFunctionPointerType*)&cnx
+ ->hooks[egl_connection_t::GLESv2_INDEX]
+ ->gl);
+ uninit_api(gl_names,
+ (__eglMustCastToProperFunctionPointerType*)&cnx
+ ->hooks[egl_connection_t::GLESv1_INDEX]
+ ->gl);
+ uninit_api(egl_names, (__eglMustCastToProperFunctionPointerType*)&cnx->egl);
+
+ if (cnx->dso) {
+ ALOGD("Unload system gl driver.");
+ driver_t* hnd = (driver_t*)cnx->dso;
+ if (hnd->dso[2]) {
+ do_android_unload_sphal_library(hnd->dso[2]);
+ }
+ if (hnd->dso[1]) {
+ do_android_unload_sphal_library(hnd->dso[1]);
+ }
+ if (hnd->dso[0]) {
+ do_android_unload_sphal_library(hnd->dso[0]);
+ }
+ cnx->dso = nullptr;
+ }
+
+ cnx->systemDriverUnloaded = true;
+}
+
void* Loader::open(egl_connection_t* cnx)
{
ATRACE_CALL();
+ const nsecs_t openTime = systemTime();
- void* dso;
- driver_t* hnd = 0;
+ if (should_unload_system_driver(cnx)) {
+ unload_system_driver(cnx);
+ }
+
+ // If a driver has been loaded, return the driver directly.
+ if (cnx->dso) {
+ return cnx->dso;
+ }
setEmulatorGlesValue();
- dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);
- if (dso) {
- hnd = new driver_t(dso);
+ // 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 {
- // Always load EGL first
- dso = load_driver("EGL", cnx, EGL);
- if (dso) {
- hnd = new driver_t(dso);
- hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM );
- hnd->set( load_driver("GLESv2", cnx, GLESv2), GLESv2 );
+ cnx->shouldUseAngle = false;
+ }
+
+ // Firstly, try to load ANGLE driver.
+ driver_t* hnd = attempt_to_load_angle(cnx);
+ if (!hnd) {
+ // Secondly, try to load from driver apk.
+ hnd = attempt_to_load_updated_driver(cnx);
+ }
+
+ bool failToLoadFromDriverSuffixProperty = false;
+ if (!hnd) {
+ // Finally, try to load system driver, start by searching for the library name appended by
+ // the system properties of the GLES userspace driver in both locations.
+ // i.e.:
+ // libGLES_${prop}.so, or:
+ // libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
+ char prop[PROPERTY_VALUE_MAX + 1];
+ for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
+ if (property_get(key, prop, nullptr) <= 0) {
+ continue;
+ }
+ hnd = attempt_to_load_system_driver(cnx, prop, true);
+ if (hnd) {
+ break;
+ } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
+ failToLoadFromDriverSuffixProperty = true;
+ }
}
}
- LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation");
+ if (!hnd) {
+ // Can't find graphics driver by appending system properties, now search for the exact name
+ // without any suffix of the GLES userspace driver in both locations.
+ // i.e.:
+ // libGLES.so, or:
+ // libEGL.so, libGLESv1_CM.so, libGLESv2.so
+ hnd = attempt_to_load_system_driver(cnx, nullptr, true);
+ }
- 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 (!hnd && !failToLoadFromDriverSuffixProperty) {
+ hnd = attempt_to_load_system_driver(cnx, nullptr, false);
+ }
+
+ 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, make sure you set %s or %s",
+ HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1]);
+
+ if (!cnx->libEgl) {
+ cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
+ }
+ if (!cnx->libGles1) {
+ cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
+ }
+ if (!cnx->libGles2) {
+ cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.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");
@@ -250,13 +340,26 @@
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,
@@ -282,11 +385,11 @@
__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"))) {
@@ -296,7 +399,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")) {
@@ -305,7 +408,7 @@
//ALOGD_IF(f, "found <%s> instead", scrap);
}
}
- if (f == NULL) {
+ if (f == nullptr) {
//ALOGD("%s", name);
f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
@@ -328,43 +431,11 @@
}
}
-static void* load_system_driver(const char* kind) {
+static void* load_system_driver(const char* kind, const char* suffix, const bool exact) {
ATRACE_CALL();
class MatchFile {
public:
- static std::string find(const char* kind) {
- std::string result;
- int emulationStatus = checkGlesEmulationStatus();
- switch (emulationStatus) {
- case 0:
-#if defined(__LP64__)
- result = "/vendor/lib64/egl/libGLES_android.so";
-#else
- result = "/vendor/lib/egl/libGLES_android.so";
-#endif
- return result;
- case 1:
- // Use host-side OpenGL through the "emulation" library
-#if defined(__LP64__)
- result = std::string("/vendor/lib64/egl/lib") + kind + "_emulation.so";
-#else
- result = std::string("/vendor/lib/egl/lib") + kind + "_emulation.so";
-#endif
- return result;
- case 2:
- // Use guest side swiftshader library
-#if defined(__LP64__)
- result = std::string("/vendor/lib64/egl/lib") + kind + "_swiftshader.so";
-#else
- result = std::string("/vendor/lib/egl/lib") + kind + "_swiftshader.so";
-#endif
- return result;
- default:
- // Not in emulator, or use other guest-side implementation
- break;
- }
-
- std::string pattern = std::string("lib") + kind;
+ static std::string find(const char* libraryName, const bool exact) {
const char* const searchPaths[] = {
#if defined(__LP64__)
"/vendor/lib64/egl",
@@ -375,35 +446,16 @@
#endif
};
- // first, we search for the exact name of the GLES userspace
- // driver in both locations.
- // i.e.:
- // libGLES.so, or:
- // libEGL.so, libGLESv1_CM.so, libGLESv2.so
-
- for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {
- if (find(result, pattern, searchPaths[i], true)) {
- return result;
+ for (auto dir : searchPaths) {
+ std::string absolutePath;
+ if (find(absolutePath, libraryName, dir, exact)) {
+ return absolutePath;
}
}
- // for compatibility with the old "egl.cfg" naming convention
- // we look for files that match:
- // libGLES_*.so, or:
- // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so
-
- pattern.append("_");
- for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {
- if (find(result, pattern, searchPaths[i], false)) {
- return result;
- }
- }
-
- // we didn't find the driver. gah.
- result.clear();
- return result;
+ // Driver not found. gah.
+ return std::string();
}
-
private:
static bool find(std::string& result,
const std::string& pattern, const char* const search, bool exact) {
@@ -417,9 +469,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;
}
@@ -441,11 +493,19 @@
}
};
-
- std::string absolutePath = MatchFile::find(kind);
+ std::string libraryName = std::string("lib") + kind;
+ if (suffix) {
+ libraryName += std::string("_") + suffix;
+ } else if (!exact) {
+ // Deprecated: we look for files that match
+ // libGLES_*.so, or:
+ // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so
+ libraryName += std::string("_");
+ }
+ std::string absolutePath = MatchFile::find(libraryName.c_str(), exact);
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();
@@ -455,10 +515,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);
@@ -466,10 +526,88 @@
return dso;
}
-static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
- "ro.hardware.egl",
- "ro.board.platform",
-};
+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) {
+ 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.
+ char prop[PROPERTY_VALUE_MAX + 1];
+ for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
+ if (property_get(key, prop, nullptr) <= 0) {
+ continue;
+ }
+ void* dso = load_system_driver("EGL", prop, true);
+ if (dso) {
+ cnx->vendorEGL = dso;
+ break;
+ }
+ }
+ if (!cnx->vendorEGL) {
+ cnx->vendorEGL = load_system_driver("EGL", nullptr, true);
+ }
+ }
+ } 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 void* load_updated_driver(const char* kind, android_namespace_t* ns) {
ATRACE_CALL();
@@ -480,35 +618,110 @@
void* so = nullptr;
char prop[PROPERTY_VALUE_MAX + 1];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
- if (property_get(key, prop, nullptr) > 0) {
- std::string name = std::string("lib") + kind + "_" + prop + ".so";
- so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
- if (so) {
- return so;
- }
+ if (property_get(key, prop, nullptr) <= 0) {
+ continue;
+ }
+ std::string name = std::string("lib") + kind + "_" + prop + ".so";
+ so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+ if (so) {
+ return so;
}
}
return nullptr;
}
-void *Loader::load_driver(const char* kind,
- egl_connection_t* cnx, uint32_t mask)
-{
+Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) {
ATRACE_CALL();
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
+ if (!ns) {
+ return nullptr;
+ }
- void* dso = nullptr;
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::ANGLE);
+ driver_t* hnd = nullptr;
+
+ // ANGLE doesn't ship with GLES library, and thus we skip GLES driver.
+ void* dso = load_angle("EGL", ns, cnx);
+ if (dso) {
+ initialize_api(dso, cnx, EGL);
+ hnd = new driver_t(dso);
+
+ dso = load_angle("GLESv1_CM", ns, cnx);
+ initialize_api(dso, cnx, GLESv1_CM);
+ hnd->set(dso, GLESv1_CM);
+
+ dso = load_angle("GLESv2", ns, cnx);
+ initialize_api(dso, cnx, GLESv2);
+ hnd->set(dso, GLESv2);
+ }
+ return hnd;
+}
+
+Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
+ ATRACE_CALL();
#ifndef __ANDROID_VNDK__
- android_namespace_t* ns = android_getDriverNamespace();
- if (ns) {
- dso = load_updated_driver(kind, ns);
- }
-#endif
- if (!dso) {
- dso = load_system_driver(kind);
- if (!dso)
- return NULL;
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
+ if (!ns) {
+ return nullptr;
}
+ ALOGD("Load updated gl driver.");
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL_UPDATED);
+ driver_t* hnd = nullptr;
+ void* dso = load_updated_driver("GLES", ns);
+ if (dso) {
+ initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2);
+ hnd = new driver_t(dso);
+ return hnd;
+ }
+
+ dso = load_updated_driver("EGL", ns);
+ if (dso) {
+ initialize_api(dso, cnx, EGL);
+ hnd = new driver_t(dso);
+
+ dso = load_updated_driver("GLESv1_CM", ns);
+ initialize_api(dso, cnx, GLESv1_CM);
+ hnd->set(dso, GLESv1_CM);
+
+ dso = load_updated_driver("GLESv2", ns);
+ initialize_api(dso, cnx, GLESv2);
+ hnd->set(dso, GLESv2);
+ }
+ return hnd;
+#else
+ return nullptr;
+#endif
+}
+
+Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
+ const bool exact) {
+ ATRACE_CALL();
+ android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL);
+ driver_t* hnd = nullptr;
+ void* dso = load_system_driver("GLES", suffix, exact);
+ if (dso) {
+ initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2);
+ hnd = new driver_t(dso);
+ return hnd;
+ }
+ dso = load_system_driver("EGL", suffix, exact);
+ if (dso) {
+ initialize_api(dso, cnx, EGL);
+ hnd = new driver_t(dso);
+
+ dso = load_system_driver("GLESv1_CM", suffix, exact);
+ initialize_api(dso, cnx, GLESv1_CM);
+ hnd->set(dso, GLESv1_CM);
+
+ dso = load_system_driver("GLESv2", suffix, exact);
+ initialize_api(dso, cnx, GLESv2);
+ hnd->set(dso, GLESv2);
+ }
+ return hnd;
+}
+
+void Loader::initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask) {
if (mask & EGL) {
getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");
@@ -523,11 +736,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;
@@ -548,10 +761,6 @@
&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
getProcAddress);
}
-
- return dso;
}
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index e88d1a2..6f31ab4 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -33,7 +33,8 @@
enum {
EGL = 0x01,
GLESv1_CM = 0x02,
- GLESv2 = 0x04
+ GLESv2 = 0x04,
+ PLATFORM = 0x08
};
struct driver_t {
explicit driver_t(void* gles);
@@ -50,11 +51,15 @@
~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);
+ driver_t* attempt_to_load_angle(egl_connection_t* cnx);
+ driver_t* attempt_to_load_updated_driver(egl_connection_t* cnx);
+ driver_t* attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact);
+ void unload_system_driver(egl_connection_t* cnx);
+ void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
static __attribute__((noinline))
void init_api(void* dso,
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 213c802..25b1009 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,12 +187,16 @@
// dynamically load our EGL implementation
egl_connection_t* cnx = &gEGLImpl;
- if (cnx->dso == 0) {
- cnx->hooks[egl_connection_t::GLESv1_INDEX] =
- &gHooks[egl_connection_t::GLESv1_INDEX];
- cnx->hooks[egl_connection_t::GLESv2_INDEX] =
- &gHooks[egl_connection_t::GLESv2_INDEX];
- cnx->dso = loader.open(cnx);
+ cnx->hooks[egl_connection_t::GLESv1_INDEX] = &gHooks[egl_connection_t::GLESv1_INDEX];
+ cnx->hooks[egl_connection_t::GLESv2_INDEX] = &gHooks[egl_connection_t::GLESv2_INDEX];
+ 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,7 +256,7 @@
char const * const gl_names[] = {
#include "../entries.in"
- NULL
+ nullptr
};
char const * const gl_names_1[] = {
@@ -259,7 +266,12 @@
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..67d69b4 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;
@@ -65,6 +71,11 @@
return false;
}
+bool needsAndroidPEglMitigation() {
+ static const int32_t vndk_version = property_get_int32("ro.vndk.version", -1);
+ return vndk_version <= 28;
+}
+
int egl_get_init_count(EGLDisplay dpy) {
egl_display_t* eglDisplay = egl_display_t::get(dpy);
return eglDisplay ? eglDisplay->getRefsCount() : 0;
@@ -114,15 +125,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();
@@ -130,12 +217,38 @@
Loader& loader(Loader::getInstance());
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {
- EGLDisplay dpy = cnx->egl.eglGetDisplay(display);
+ if (cnx->dso) {
+ 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 +261,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) {
@@ -199,9 +319,37 @@
}
}
+ if (cnx->minor == 5) {
+ // full list in egl_entries.in
+ if (!cnx->egl.eglCreateImage ||
+ !cnx->egl.eglDestroyImage ||
+ !cnx->egl.eglGetPlatformDisplay ||
+ !cnx->egl.eglCreatePlatformWindowSurface ||
+ !cnx->egl.eglCreatePlatformPixmapSurface ||
+ !cnx->egl.eglCreateSync ||
+ !cnx->egl.eglDestroySync ||
+ !cnx->egl.eglClientWaitSync ||
+ !cnx->egl.eglGetSyncAttrib ||
+ !cnx->egl.eglWaitSync) {
+ ALOGE("Driver indicates EGL 1.5 support, but does not have "
+ "a critical API");
+ cnx->minor = 4;
+ }
+ }
+
// the query strings are per-display
mVendorString = sVendorString;
- mVersionString = sVersionString;
+ mVersionString.clear();
+ cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0);
+ mVersionString = sVersionString14;
+ if ((cnx->major == 1) && (cnx->minor == 5)) {
+ mVersionString = sVersionString15;
+ cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
+ }
+ 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 +366,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,9 +389,10 @@
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" &&
+ // Mitigation for Android P vendor partitions: Adreno 530 driver shipped on
+ // some Android P vendor partitions this extension under the draft KHR name,
+ // but during Khronos review it was decided to demote it to EXT.
+ if (needsAndroidPEglMitigation() && ext == "EGL_EXT_image_gl_colorspace" &&
findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
mExtensionString.append("EGL_EXT_image_gl_colorspace ");
}
@@ -268,10 +418,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 +463,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 +517,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 +543,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..e117314 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -43,12 +43,14 @@
struct egl_connection_t;
bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
+bool needsAndroidPEglMitigation();
// ----------------------------------------------------------------------------
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 +74,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 +162,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..ac01dc8
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -0,0 +1,444 @@
+/*
+ ** 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 -
+// - AndroidGLESLayer_Initialize (provided by layer, called by loader)
+// - AndroidGLESLayer_GetProcAddress (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 AndroidGLESLayer_Initialize
+ auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
+ EGLFuncPointer val;
+
+ ALOGV("getNextLayerProcAddress servicing %s", name);
+
+ 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 - name(%s) no func_indices entry found", name);
+
+ // Look up which GPA we should use
+ int gpaIndex = func_indices["eglGetProcAddress"];
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
+ EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
+
+
+ // Call it for the requested function
+ typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
+ PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
+
+ val = reinterpret_cast<EGLFuncPointer>(next(name));
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
+
+ // 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];
+ ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+ return reinterpret_cast<void*>(val);
+}
+
+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()) {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
+ func_names[func_idx] = name;
+ func_indices[name] = func_idx;
+ } else {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for 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) {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
+ functions[func_idx] = *curr;
+ } else {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
+ }
+
+ 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 = "AndroidGLESLayer_Initialize";
+ 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 = "AndroidGLESLayer_GetProcAddress";
+ 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..e996be6
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -0,0 +1,2736 @@
+/*
+ ** 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 "
+ "EGL_ANDROID_GLES_layers";
+// 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;
+
+ std::vector<AttrType> strippedAttribs;
+ if (needsAndroidPEglMitigation()) {
+ // Mitigation for Android P vendor partitions: 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.
+ for (const AttrType *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;
+ }
+ }
+ strippedAttribs.push_back(attr[0]);
+ strippedAttribs.push_back(attr[1]);
+ }
+ strippedAttribs.push_back(EGL_NONE);
+ }
+
+ 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,
+ needsAndroidPEglMitigation() ? strippedAttribs.data() : 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 acc205a..7bb9b59 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,59 @@
// ----------------------------------------------------------------------------
+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),
+ libEgl(nullptr),
+ libGles1(nullptr),
+ libGles2(nullptr),
+ systemDriverUnloaded(false) {
+
+ 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 systemDriverUnloaded;
+ 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
// ----------------------------------------------------------------------------
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/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/EGL_ANDROID_GLES_layers.txt b/opengl/specs/EGL_ANDROID_GLES_layers.txt
new file mode 100644
index 0000000..eb2a7d9
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_GLES_layers.txt
@@ -0,0 +1,64 @@
+Name
+
+ ANDROID_GLES_layers
+
+Name Strings
+
+ EGL_ANDROID_GLES_layers
+
+Contributors
+
+ Cody Northrop
+
+Contact
+
+ Cody Northrop, Google LLC (cnorthrop 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, March 3, 2019
+
+Number
+
+ EGL Extension #132
+
+Extension Type
+
+ EGL client extension
+
+Dependencies
+
+ Requires EGL 1.5 or EGL_EXT_client_extensions
+
+Overview
+
+ This extension indicates the EGL loader supports GLES layering on Android.
+ It does not add any requirements to drivers or hardware.
+
+ See frameworks/native/opengl/libs/EGL/GLES_layers.md in Android for
+ more information.
+
+New Types
+
+ None
+
+New Procedures and Functions
+
+ None
+
+New Tokens
+
+ None
+
+Issues
+
+ None
+
+Revision History
+
+#1 (Cody Northrop, March 3, 2019)
+ - Initial draft.
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..5c48a15
--- /dev/null
+++ b/opengl/tools/glgen/specs/egl/EGL15.spec
@@ -0,0 +1,15 @@
+EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+// eglGetSyncAttrib pulled in with eglCreateSync stubs
+// EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+// 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..34cb3e1
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -0,0 +1,194 @@
+/*
+** 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 <vector>
+#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 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 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;
+
+ pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
+ *remaining = (limit - position) << elementSizeShift;
+ if (pointer != 0L) {
+ *array = nullptr;
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
+ }
+
+ *array = jniGetNioBufferBaseArray(_env, buffer);
+ *offset = jniGetNioBufferBaseArrayOffset(_env, buffer);
+ return nullptr;
+}
+
+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));
+}
+
+struct WrappedEGLAttribs {
+private:
+ std::vector<EGLAttrib> backing; // only for 32-bit
+public:
+ EGLAttrib *attribs;
+ WrappedEGLAttribs(): attribs(nullptr) { };
+ void init(jlong *array, jint size) {
+ if (sizeof(EGLAttrib) != sizeof(jlong)) {
+ for (jint i = 0; i < size; ++i) {
+ backing.push_back(array[i]);
+ }
+ attribs = backing.data();
+ } else {
+ attribs = (EGLAttrib*)array;
+ }
+ }
+};
+
+// --------------------------------------------------------------------------
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/eglCreateImage.cpp b/opengl/tools/glgen/stubs/egl/eglCreateImage.cpp
new file mode 100644
index 0000000..f93815c
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreateImage.cpp
@@ -0,0 +1,50 @@
+/* EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateImage
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject context, jint target, jlong buffer, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLImage _returnValue = (EGLImage) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLContext context_native = (EGLContext) fromEGLHandle(_env, eglcontextGetHandleID, context);
+ jlong *attrib_list_base = (jlong *) 0;
+ jint _remaining;
+ WrappedEGLAttribs attrib_list;
+
+ 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 = (jlong *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list.init(attrib_list_base + offset, _remaining);
+
+ _returnValue = eglCreateImage(
+ (EGLDisplay)dpy_native,
+ (EGLContext)context_native,
+ (EGLenum)target,
+ (EGLClientBuffer)buffer,
+ attrib_list.attribs
+ );
+
+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, eglimageClass, eglimageConstructor, _returnValue);
+}
diff --git a/opengl/tools/glgen/stubs/egl/eglCreateImage.java b/opengl/tools/glgen/stubs/egl/eglCreateImage.java
new file mode 100644
index 0000000..06a04bb
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreateImage.java
@@ -0,0 +1,11 @@
+ // C function EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list )
+
+ public static native EGLImage eglCreateImage(
+ EGLDisplay dpy,
+ EGLContext context,
+ int target,
+ long buffer,
+ long[] attrib_list,
+ int offset
+ );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreateImage.nativeReg b/opengl/tools/glgen/stubs/egl/eglCreateImage.nativeReg
new file mode 100644
index 0000000..da5687d
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreateImage.nativeReg
@@ -0,0 +1 @@
+{"eglCreateImage", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLContext;IJ[JI)Landroid/opengl/EGLImage;", (void *) android_eglCreateImage },
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/eglCreatePlatformWindowSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.cpp
new file mode 100644
index 0000000..48dbd35
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.cpp
@@ -0,0 +1,68 @@
+/* EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformWindowSurface
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_window_buf, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ jarray _array = (jarray) 0;
+ jint _bufferOffset = (jint) 0;
+ EGLSurface _returnValue = (EGLSurface) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+ jint _native_windowRemaining;
+ void *native_window = (void *) 0;
+ jlong *attrib_list_base = (jlong *) 0;
+ jint _attrib_listRemaining;
+ WrappedEGLAttribs attrib_list;
+
+ if (!native_window_buf) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "native_window == null";
+ goto exit;
+ }
+ native_window = (void *)getPointer(_env, native_window_buf, (jarray*)&_array, &_native_windowRemaining, &_bufferOffset);
+ 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;
+ }
+ _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (jlong *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list.init(attrib_list_base + offset, _attrib_listRemaining);
+
+ if (native_window == NULL) {
+ char * _native_windowBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+ native_window = (void *) (_native_windowBase + _bufferOffset);
+ }
+ _returnValue = eglCreatePlatformWindowSurface(
+ (EGLDisplay)dpy_native,
+ (EGLConfig)config_native,
+ (void *)native_window,
+ attrib_list.attribs
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_array) {
+ releasePointer(_env, _array, native_window, _exception ? JNI_FALSE : JNI_TRUE);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return nullptr;
+ }
+ return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.java b/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.java
new file mode 100644
index 0000000..dda37f8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.java
@@ -0,0 +1,10 @@
+ // C function EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+
+ public static native EGLSurface eglCreatePlatformWindowSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ java.nio.Buffer native_window,
+ long[] attrib_list,
+ int offset
+ );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.nativeReg b/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.nativeReg
new file mode 100644
index 0000000..ce464e8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePlatformWindowSurface.nativeReg
@@ -0,0 +1 @@
+{"eglCreatePlatformWindowSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformWindowSurface },
diff --git a/opengl/tools/glgen/stubs/egl/eglCreateSync.cpp b/opengl/tools/glgen/stubs/egl/eglCreateSync.cpp
new file mode 100644
index 0000000..c53afea
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreateSync.cpp
@@ -0,0 +1,101 @@
+/* EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jint type, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLSync _returnValue = (EGLSync) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ jlong *attrib_list_base = (jlong *) 0;
+ jint _remaining;
+ WrappedEGLAttribs attrib_list;
+
+ 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 = (jlong *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list.init(attrib_list_base + offset, _remaining);
+
+ _returnValue = eglCreateSync(
+ (EGLDisplay)dpy_native,
+ (EGLenum)type,
+ attrib_list.attribs
+ );
+
+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, eglsyncClass, eglsyncConstructor, _returnValue);
+}
+
+/* EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) */
+static jboolean
+android_eglGetSyncAttrib
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint attribute, jlongArray value_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+ jlong *value_base = (jlong *) 0;
+ jint _remaining;
+ EGLAttrib value;
+
+ if (!value_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "value == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(value_ref) - offset;
+ value_base = (jlong *)
+ _env->GetLongArrayElements(value_ref, (jboolean *)0);
+
+ _returnValue = eglGetSyncAttrib(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)attribute,
+ &value
+ );
+
+ if (value_base && _returnValue == EGL_TRUE) {
+ *(value_base + offset) = (jlong) value;
+ }
+
+exit:
+ if (value_base) {
+ _env->ReleaseLongArrayElements(value_ref, (jlong*)value_base,
+ _exception ? JNI_ABORT: 0);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ return JNI_FALSE;
+ }
+ return (jboolean)_returnValue;
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreateSync.java b/opengl/tools/glgen/stubs/egl/eglCreateSync.java
new file mode 100644
index 0000000..db8f728
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreateSync.java
@@ -0,0 +1,22 @@
+ // C function EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+
+ public static native EGLSync eglCreateSync(
+ EGLDisplay dpy,
+ int type,
+ long[] attrib_list,
+ int offset
+ );
+
+ /**
+ * C function EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute,
+ * EGLAttrib *value )
+ */
+
+ public static native boolean eglGetSyncAttrib(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int attribute,
+ long[] value,
+ int offset
+ );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreateSync.nativeReg b/opengl/tools/glgen/stubs/egl/eglCreateSync.nativeReg
new file mode 100644
index 0000000..c99e7fe
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreateSync.nativeReg
@@ -0,0 +1,2 @@
+{"eglCreateSync", "(Landroid/opengl/EGLDisplay;I[JI)Landroid/opengl/EGLSync;", (void *) android_eglCreateSync },
+{"eglGetSyncAttrib", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I[JI)Z", (void *) android_eglGetSyncAttrib },
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..6acb32a
--- /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;
+ jlong *attrib_list_base = (jlong *) 0;
+ jint _remaining;
+ WrappedEGLAttribs attrib_list;
+
+ 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 = (jlong *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list.init(attrib_list_base + offset, _remaining);
+
+ _returnValue = eglGetPlatformDisplay(
+ (EGLenum)platform,
+ (void *)native_display,
+ attrib_list.attribs
+ );
+
+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..e763b4e 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -4,17 +4,6 @@
#include <utils/misc.h>
#include <assert.h>
-static int initialized = 0;
-
-static jclass nioAccessClass;
-static jclass bufferClass;
-static jmethodID getBasePointerID;
-static jmethodID getBaseArrayID;
-static jmethodID getBaseArrayOffsetID;
-static jfieldID positionID;
-static jfieldID limitID;
-static jfieldID elementSizeShiftID;
-
/* special calls implemented in Android's GLES wrapper used to more
* efficiently bound-check passed arrays */
@@ -49,28 +38,9 @@
#endif
}
-/* Cache method IDs each time the class is loaded. */
-
static void
nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
- 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");
}
static void *
@@ -81,23 +51,17 @@
jint elementSizeShift;
jlong pointer;
- position = _env->GetIntField(buffer, positionID);
- limit = _env->GetIntField(buffer, limitID);
- elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+ pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
*remaining = (limit - position) << elementSizeShift;
- pointer = _env->CallStaticLongMethod(nioAccessClass,
- getBasePointerID, buffer);
if (pointer != 0L) {
- *array = NULL;
+ *array = nullptr;
+ pointer += position << elementSizeShift;
return reinterpret_cast<void*>(pointer);
}
- *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
- getBaseArrayID, buffer);
- *offset = _env->CallStaticIntMethod(nioAccessClass,
- getBaseArrayOffsetID, buffer);
-
- return NULL;
+ *array = jniGetNioBufferBaseArray(_env, buffer);
+ *offset = jniGetNioBufferBaseArrayOffset(_env, buffer);
+ return nullptr;
}
class ByteArrayGetter {
@@ -219,16 +183,18 @@
static void *
getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
- char* buf = (char*) _env->GetDirectBufferAddress(buffer);
- if (buf) {
- jint position = _env->GetIntField(buffer, positionID);
- jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
- buf += position << elementSizeShift;
- } else {
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+ pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
+ if (pointer == 0) {
jniThrowException(_env, "java/lang/IllegalArgumentException",
"Must use a native order direct Buffer");
+ return nullptr;
}
- return (void*) buf;
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
}
// --------------------------------------------------------------------------
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..c12efc3 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -64,18 +64,7 @@
GLsizei stride, const GLvoid *pointer, GLsizei count);
}
-static int initialized = 0;
-
-static jclass nioAccessClass;
-static jclass bufferClass;
static jclass G11ImplClass;
-static jmethodID getBasePointerID;
-static jmethodID getBaseArrayID;
-static jmethodID getBaseArrayOffsetID;
-static jmethodID allowIndirectBuffersID;
-static jfieldID positionID;
-static jfieldID limitID;
-static jfieldID elementSizeShiftID;
static jfieldID haveCheckedExtensionsID;
static jfieldID have_OES_blend_equation_separateID;
static jfieldID have_OES_blend_subtractID;
@@ -87,12 +76,6 @@
static void
nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
- jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
- nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
-
- jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
- bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
-
jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl");
G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal);
haveCheckedExtensionsID = _env->GetFieldID(G11ImplClass, "haveCheckedExtensions", "Z");
@@ -100,19 +83,6 @@
have_OES_blend_subtractID = _env->GetFieldID(G11ImplClass, "have_OES_blend_subtract", "Z");
have_OES_framebuffer_objectID = _env->GetFieldID(G11ImplClass, "have_OES_framebuffer_object", "Z");
have_OES_texture_cube_mapID = _env->GetFieldID(G11ImplClass, "have_OES_texture_cube_map", "Z");
-
- 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");
- allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal,
- "allowIndirectBuffers", "(Ljava/lang/String;)Z");
- positionID = _env->GetFieldID(bufferClass, "position", "I");
- limitID = _env->GetFieldID(bufferClass, "limit", "I");
- elementSizeShiftID =
- _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
}
static void *
@@ -123,28 +93,17 @@
jint elementSizeShift;
jlong pointer;
- position = _env->GetIntField(buffer, positionID);
- limit = _env->GetIntField(buffer, limitID);
- elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+ pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
*remaining = (limit - position) << elementSizeShift;
- pointer = _env->CallStaticLongMethod(nioAccessClass,
- getBasePointerID, buffer);
if (pointer != 0L) {
- *offset = 0;
- *array = NULL;
- return reinterpret_cast<void *>(pointer);
+ *array = nullptr;
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
}
- *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
- getBaseArrayID, buffer);
- if (*array == NULL) {
- *offset = 0;
- return (void*) NULL;
- }
- *offset = _env->CallStaticIntMethod(nioAccessClass,
- getBaseArrayOffsetID, buffer);
-
- return NULL;
+ *array = jniGetNioBufferBaseArray(_env, buffer);
+ *offset = jniGetNioBufferBaseArrayOffset(_env, buffer);
+ return nullptr;
}
static void
@@ -158,42 +117,24 @@
extern char* __progname;
}
-static bool
-allowIndirectBuffers(JNIEnv *_env) {
- static jint sIndirectBufferCompatability;
- if (sIndirectBufferCompatability == 0) {
- jobject appName = _env->NewStringUTF(::__progname);
- sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1;
- }
- return sIndirectBufferCompatability == 2;
-}
-
static void *
getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
- if (!buffer) {
- return NULL;
+ if (buffer == nullptr) {
+ return nullptr;
}
- void* buf = _env->GetDirectBufferAddress(buffer);
- if (buf) {
- jint position = _env->GetIntField(buffer, positionID);
- jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
- buf = ((char*) buf) + (position << elementSizeShift);
- } else {
- if (allowIndirectBuffers(_env)) {
- jarray array = 0;
- jint remaining;
- jint offset;
- buf = getPointer(_env, buffer, &array, &remaining, &offset);
- if (array) {
- releasePointer(_env, array, buf, 0);
- }
- buf = (char*)buf + offset;
- } else {
- jniThrowException(_env, "java/lang/IllegalArgumentException",
- "Must use a native order direct Buffer");
- }
+
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+ pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift);
+ if (pointer == 0) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
+ return nullptr;
}
- return buf;
+ pointer += position << elementSizeShift;
+ return reinterpret_cast<void*>(pointer);
}
static int
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/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index b9b0706..6235f06 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -98,6 +98,37 @@
data.writeInt32((int32_t) piid);
return remote()->transact(RELEASE_PLAYER, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeStrongBinder(recorder);
+ // get new RIId in reply
+ const status_t res = remote()->transact(TRACK_RECORDER, data, &reply, 0);
+ if (res != OK || reply.readExceptionCode() != 0) {
+ ALOGE("trackRecorder() failed, riid is %d", RECORD_RIID_INVALID);
+ return RECORD_RIID_INVALID;
+ } else {
+ const audio_unique_id_t riid = (audio_unique_id_t) reply.readInt32();
+ ALOGV("trackRecorder() returned riid %d", riid);
+ return riid;
+ }
+ }
+
+ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32((int32_t) riid);
+ data.writeInt32((int32_t) event);
+ return remote()->transact(RECORDER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t releaseRecorder(audio_unique_id_t riid) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
+ data.writeInt32((int32_t) riid);
+ return remote()->transact(RELEASE_RECORDER, data, &reply, IBinder::FLAG_ONEWAY);
+ }
};
IMPLEMENT_META_INTERFACE(AudioManager, "android.media.IAudioService");
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
new file mode 100644
index 0000000..cd03bc2
--- /dev/null
+++ b/services/bufferhub/Android.bp
@@ -0,0 +1,81 @@
+//
+// 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",
+ "libhidlbase",
+ "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..8accf9d 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -14,87 +14,136 @@
* 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,
+ const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+ bool isDriverLoaded, int64_t driverLoadingTime) {
+ ATRACE_CALL();
+
+ mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
+ appPackageName, vulkanVersion, 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;
+}
+
+void GpuService::setCpuVulkanInUse(const std::string& appPackageName,
+ const uint64_t driverVersionCode) {
+ ATRACE_CALL();
+
+ mGpuStats->setCpuVulkanInUse(appPackageName, driverVersionCode);
+}
+
+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 +154,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 +166,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..8226901 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -17,40 +17,64 @@
#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, const int32_t vulkanVersion,
+ 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;
+ void setCpuVulkanInUse(const std::string& appPackageName,
+ const uint64_t driverVersionCode) 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..37c6abc
--- /dev/null
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -0,0 +1,250 @@
+/*
+ * 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 <cutils/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <unordered_set>
+
+namespace android {
+
+static void 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;
+ case GraphicsEnv::Driver::ANGLE:
+ outGlobalInfo->angleLoadingCount++;
+ if (!isDriverLoaded) outGlobalInfo->angleLoadingFailureCount++;
+ break;
+ default:
+ break;
+ }
+}
+
+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) {
+ outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime);
+ }
+ break;
+ case GraphicsEnv::Driver::VULKAN:
+ case GraphicsEnv::Driver::VULKAN_UPDATED:
+ if (outAppInfo->vkDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
+ outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime);
+ }
+ break;
+ case GraphicsEnv::Driver::ANGLE:
+ if (outAppInfo->angleDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
+ outAppInfo->angleDriverLoadingTime.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, const int32_t vulkanVersion,
+ 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"
+ "\tvulkanVersion[%d]\n"
+ "\tdriver[%d]\n"
+ "\tisDriverLoaded[%d]\n"
+ "\tdriverLoadingTime[%" PRId64 "]",
+ driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime,
+ appPackageName.c_str(), vulkanVersion, static_cast<int32_t>(driver), isDriverLoaded,
+ driverLoadingTime);
+
+ if (!mGlobalStats.count(driverVersionCode)) {
+ GpuStatsGlobalInfo globalInfo;
+ addLoadingCount(driver, isDriverLoaded, &globalInfo);
+ globalInfo.driverPackageName = driverPackageName;
+ globalInfo.driverVersionName = driverVersionName;
+ globalInfo.driverVersionCode = driverVersionCode;
+ globalInfo.driverBuildTime = driverBuildTime;
+ globalInfo.vulkanVersion = vulkanVersion;
+ mGlobalStats.insert({driverVersionCode, globalInfo});
+ } else {
+ addLoadingCount(driver, isDriverLoaded, &mGlobalStats[driverVersionCode]);
+ }
+
+ const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
+ if (!mAppStats.count(appStatsKey)) {
+ if (mAppStats.size() >= MAX_NUM_APP_RECORDS) {
+ ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats.");
+ return;
+ }
+
+ GpuStatsAppInfo appInfo;
+ addLoadingTime(driver, driverLoadingTime, &appInfo);
+ appInfo.appPackageName = appPackageName;
+ appInfo.driverVersionCode = driverVersionCode;
+ mAppStats.insert({appStatsKey, appInfo});
+ return;
+ }
+
+ addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
+}
+
+void GpuStats::setCpuVulkanInUse(const std::string& appPackageName,
+ const uint64_t driverVersionCode) {
+ const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
+ if (!mAppStats.count(appStatsKey)) {
+ return;
+ }
+
+ mAppStats[appStatsKey].cpuVulkanInUse = true;
+}
+
+void GpuStats::interceptSystemDriverStatsLocked() {
+ // Append cpuVulkanVersion and glesVersion to system driver stats
+ if (!mGlobalStats.count(0) || mGlobalStats[0].glesVersion) {
+ return;
+ }
+
+ mGlobalStats[0].cpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
+ mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
+}
+
+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) {
+ interceptSystemDriverStatsLocked();
+
+ 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());
+
+ interceptSystemDriverStatsLocked();
+
+ 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..b293f59
--- /dev/null
+++ b/services/gpuservice/gpustats/GpuStats.h
@@ -0,0 +1,71 @@
+/*
+ * 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, const int32_t vulkanVersion,
+ GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime);
+ // Set CPU Vulkan in use signal into app stats.
+ void setCpuVulkanInUse(const std::string& appPackageName, const uint64_t driverVersionCode);
+ // 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);
+ // Append cpuVulkanVersion and glesVersion to system driver stats
+ void interceptSystemDriverStatsLocked();
+
+ // 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..8dd4d1d 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -12,43 +12,139 @@
// 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",
+ "InputClassifierConverter.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..ce56272 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,51 @@
}
}
+/**
+ * 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.
+ *
+ * Setting this to "false" would prevent any video devices from being discovered and
+ * associated with input devices.
+ *
+ * This property can be used as follows:
+ * 1. To turn off features that are dependent on video device presence.
+ * 2. During testing and development, to allow other clients to read video devices
+ * directly from /dev.
+ */
+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 +190,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 +209,6 @@
EventHub::Device::~Device() {
close();
delete configuration;
- delete virtualKeyMap;
}
void EventHub::Device::close() {
@@ -172,9 +219,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 +240,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 +288,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 +319,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 +359,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 +422,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 +468,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 +485,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 +517,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 +526,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 +563,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 +616,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 +634,7 @@
if (device) {
return device->getKeyCharacterMap();
}
- return NULL;
+ return nullptr;
}
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
@@ -599,16 +652,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 +669,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 +690,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 +717,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 +730,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 +752,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 +785,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 +840,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 +860,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 +889,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 +898,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 +914,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 +964,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 +981,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 +1056,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 +1075,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 +1117,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 +1201,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 +1242,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 +1250,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 +1258,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 +1266,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 +1372,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 +1422,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 +1443,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 +1465,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 +1483,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 +1507,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 +1549,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 +1571,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 +1589,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 +1608,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 +1661,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 +1689,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 +1717,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 +1778,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 +1792,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 +1809,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 +1822,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 +1864,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 +1881,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 +1926,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..ef1a224
--- /dev/null
+++ b/services/inputflinger/InputClassifier.cpp
@@ -0,0 +1,468 @@
+/*
+ * 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 "InputClassifierConverter.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 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::hidl_death_recipient> deathRecipient) :
+ mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
+ mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+/**
+ * This function may block for some time to initialize the HAL, so it should only be called
+ * from the "InputClassifier HAL" thread.
+ */
+bool MotionClassifier::init() {
+ ensureHalThread(__func__);
+ 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 false;
+ }
+
+ sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
+ if (recipient != nullptr) {
+ const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link MotionClassifier to the HAL death");
+ return false;
+ }
+ }
+
+ // 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());
+
+ {
+ std::scoped_lock lock(mLock);
+ if (mService) {
+ ALOGE("MotionClassifier::%s should only be called once", __func__);
+ }
+ mService = service;
+ }
+ return true;
+}
+
+MotionClassifier::~MotionClassifier() {
+ requestExit();
+ mHalThread.join();
+}
+
+void MotionClassifier::ensureHalThread(const char* function) {
+ if (DEBUG) {
+ if (std::this_thread::get_id() != mHalThread.get_id()) {
+ LOG_FATAL("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__);
+ const bool initialized = init();
+ if (!initialized) {
+ // MotionClassifier no longer useful.
+ // Deliver death notification from a separate thread
+ // because ~MotionClassifier may be invoked, which calls mHalThread.join()
+ std::thread([deathRecipient = mDeathRecipient](){
+ sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
+ if (recipient != nullptr) {
+ recipient->serviceDied(0 /*cookie*/, nullptr);
+ }
+ }).detach();
+ return;
+ }
+ // From this point on, mService is guaranteed to be non-null.
+
+ 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 =
+ notifyMotionArgsToHalMotionEvent(*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));
+}
+
+const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
+ if (!mService) {
+ return "null";
+ }
+ if (mService->ping().isOk()) {
+ return "running";
+ }
+ return "not responding";
+}
+
+void MotionClassifier::dump(std::string& dump) {
+ std::scoped_lock lock(mLock);
+ dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
+ 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() {
+ if (!deepPressEnabled()) {
+ // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
+ // When MotionClassifier is null, InputClassifier will forward all events
+ // to the next InputListener, unmodified.
+ return;
+ }
+ std::scoped_lock lock(mLock);
+ mMotionClassifier = std::make_unique<MotionClassifier>(this);
+}
+
+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
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
new file mode 100644
index 0000000..47e20db
--- /dev/null
+++ b/services/inputflinger/InputClassifier.h
@@ -0,0 +1,260 @@
+/*
+ * 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 deathRecipient will be subscribed to the HAL death. If the death recipient
+ * owns MotionClassifier and receives HAL death, it should delete its copy of it.
+ * The callback serviceDied will also be sent if the MotionClassifier itself fails
+ * to initialize. If the MotionClassifier fails to initialize, it is not useful, and
+ * should be deleted.
+ * If no death recipient is supplied, then the registration step will be skipped, so there will
+ * be no listeners registered for the HAL death. This is useful for testing
+ * MotionClassifier in isolation.
+ */
+ explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr);
+ ~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:
+ /**
+ * Initialize MotionClassifier.
+ * Return true if initializaion is successful.
+ */
+ bool init();
+ /**
+ * Entity that will be notified of the HAL death (most likely InputClassifier).
+ */
+ wp<android::hardware::hidl_death_recipient> mDeathRecipient;
+
+ // 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 __func__
+ */
+ void ensureHalThread(const char* function);
+ /**
+ * Call the InputClassifier HAL
+ */
+ void callInputClassifierHal();
+ /**
+ * Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
+ * When init() successfully completes, mService is guaranteed to remain non-null and to not
+ * change its value until MotionClassifier is destroyed.
+ * This variable is *not* guarded by mLock in the InputClassifier thread, because
+ * that thread knows exactly when this variable is initialized.
+ * When accessed in any other thread, mService is checked for nullness with a lock.
+ */
+ 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();
+ /**
+ * Return string status of mService
+ */
+ const char* getServiceStatus() REQUIRES(mLock);
+};
+
+
+/**
+ * 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/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp
new file mode 100644
index 0000000..f82c8ef
--- /dev/null
+++ b/services/inputflinger/InputClassifierConverter.cpp
@@ -0,0 +1,384 @@
+/*
+ * 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 "InputClassifierConverter.h"
+
+using android::hardware::hidl_bitfield;
+using namespace android::hardware::input;
+
+namespace android {
+
+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);
+}
+
+// MotionEvent axes asserts
+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);
+
+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;
+ // OK to copy bits because we have static_assert for pointerCoords axes
+ coords.bits = args.pointerCoords[i].bits;
+ coords.values = std::vector<float>(
+ args.pointerCoords[i].values,
+ args.pointerCoords[i].values + BitSet64::count(args.pointerCoords[i].bits));
+ outPointerCoords->push_back(coords);
+ }
+}
+
+common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(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;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputClassifierConverter.h b/services/inputflinger/InputClassifierConverter.h
new file mode 100644
index 0000000..5154b0b
--- /dev/null
+++ b/services/inputflinger/InputClassifierConverter.h
@@ -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.
+ */
+
+#ifndef _UI_INPUT_CLASSIFIER_CONVERTER_H
+#define _UI_INPUT_CLASSIFIER_CONVERTER_H
+
+#include "InputListener.h"
+#include <android/hardware/input/common/1.0/types.h>
+
+
+namespace android {
+
+/**
+ * Convert from framework's NotifyMotionArgs to hidl's common::V1_0::MotionEvent
+ */
+::android::hardware::input::common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(
+ const NotifyMotionArgs& args);
+
+} // namespace android
+
+#endif // _UI_INPUT_CLASSIFIER_CONVERTER_H
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9a449fa..c2ff4c9 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "InputDispatcher"
#define ATRACE_TAG ATRACE_TAG_INPUT
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
// Log detailed debug messages about each inbound event notification to the dispatcher.
#define DEBUG_INBOUND_EVENT_DETAILS 0
@@ -46,6 +46,7 @@
#include "InputDispatcher.h"
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <sstream>
#include <stddef.h>
@@ -57,7 +58,7 @@
#include <log/log.h>
#include <utils/Trace.h>
#include <powermanager/PowerManager.h>
-#include <ui/Region.h>
+#include <binder/Binder.h>
#define INDENT " "
#define INDENT2 " "
@@ -96,6 +97,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);
@@ -124,7 +128,7 @@
static std::string keyActionToString(int32_t action) {
// Convert KeyEvent action to string
- switch(action) {
+ switch (action) {
case AKEY_EVENT_ACTION_DOWN:
return "DOWN";
case AKEY_EVENT_ACTION_UP:
@@ -135,6 +139,24 @@
return StringPrintf("%" PRId32, action);
}
+static std::string dispatchModeToString(int32_t dispatchMode) {
+ switch (dispatchMode) {
+ case InputTarget::FLAG_DISPATCH_AS_IS:
+ return "DISPATCH_AS_IS";
+ case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
+ return "DISPATCH_AS_OUTSIDE";
+ case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
+ return "DISPATCH_AS_HOVER_ENTER";
+ case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
+ return "DISPATCH_AS_HOVER_EXIT";
+ case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
+ return "DISPATCH_AS_SLIPPERY_EXIT";
+ case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
+ return "DISPATCH_AS_SLIPPERY_ENTER";
+ }
+ return StringPrintf("%" PRId32, dispatchMode);
+}
+
static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -211,10 +233,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 +253,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 +295,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 +386,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 +396,7 @@
}
if (mNextUnblockedEvent == mPendingEvent) {
- mNextUnblockedEvent = NULL;
+ mNextUnblockedEvent = nullptr;
}
switch (mPendingEvent->type) {
@@ -393,7 +419,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 +427,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 +443,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 +481,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 +507,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 +539,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 +552,55 @@
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;
+}
+
+std::vector<InputDispatcher::TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+ std::vector<TouchedMonitor> touchedMonitors;
+
+ std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
+ addGestureMonitors(monitors, touchedMonitors);
+ for (const sp<InputWindowHandle>& portalWindow : portalWindows) {
+ const InputWindowInfo* windowInfo = portalWindow->getInfo();
+ monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
+ addGestureMonitors(monitors, touchedMonitors,
+ -windowInfo->frameLeft, -windowInfo->frameTop);
+ }
+ return touchedMonitors;
+}
+
+void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset, float yOffset) {
+ if (monitors.empty()) {
+ return;
+ }
+ outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+ for (const Monitor& monitor : monitors) {
+ outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+ }
}
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
@@ -591,13 +657,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 +686,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 +729,7 @@
if (mPendingEvent) {
resetANRTimeoutsLocked();
releaseInboundEventLocked(mPendingEvent);
- mPendingEvent = NULL;
+ mPendingEvent = nullptr;
}
}
@@ -673,10 +739,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 +751,7 @@
void InputDispatcher::resetKeyRepeatLocked() {
if (mKeyRepeatState.lastKeyEntry) {
mKeyRepeatState.lastKeyEntry->release();
- mKeyRepeatState.lastKeyEntry = NULL;
+ mKeyRepeatState.lastKeyEntry = nullptr;
}
}
@@ -701,8 +767,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 +798,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 +853,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 +873,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 +893,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.
+ addGlobalMonitoringTargetsLocked(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
@@ -863,16 +934,17 @@
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+ ATRACE_CALL();
// Preprocessing.
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 +952,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 +969,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 +981,24 @@
return true;
}
- addMonitoringTargetsLocked(inputTargets);
+ // Add monitor channels from event's or focused display.
+ addGlobalMonitoringTargetsLocked(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();
+ addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
+ -windowInfo->frameLeft, -windowInfo->frameTop);
+ }
+ }
+ }
+ }
// Dispatch the motion.
if (conflictingPointerActions) {
@@ -922,14 +1011,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 +1046,8 @@
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
+ EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) {
+ ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
ALOGD("dispatchEventToCurrentInputTargets");
#endif
@@ -965,9 +1056,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 +1076,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 +1085,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 +1108,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 +1140,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 +1161,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 +1192,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 +1275,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,8 +1285,9 @@
}
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) {
+ ATRACE_CALL();
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
INJECTION_PERMISSION_GRANTED,
@@ -1186,7 +1308,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 +1331,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 +1348,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,84 +1366,67 @@
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> newTouchedWindowHandle;
- bool isTouchModal = false;
+ bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+ sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
+ displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
- // 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));
- }
- }
- }
+ std::vector<TouchedMonitor> newGestureMonitors = isDown
+ ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+ : std::vector<TouchedMonitor>{};
// 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);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- goto Failed;
+ }
+
+ if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
+ ALOGI("Dropping event because there is no touchable window or gesture monitor at "
+ "(%d, %d) in display %" PRId32 ".", x, y, displayId);
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ if (newTouchedWindowHandle != nullptr) {
+ // Set target flags.
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
+ if (isSplit) {
+ targetFlags |= InputTarget::FLAG_SPLIT;
}
+ if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ }
+
+ // Update hover state.
+ if (isHoverAction) {
+ newHoverWindowHandle = newTouchedWindowHandle;
+ } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
+ newHoverWindowHandle = mLastHoverWindowHandle;
+ }
+
+ // Update the temporary touch state.
+ BitSet32 pointerIds;
+ if (isSplit) {
+ uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+ pointerIds.markBit(pointerId);
+ }
+ mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
- // Set target flags.
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
- if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
- }
- if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- }
-
- // Update hover state.
- if (isHoverAction) {
- newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
- }
-
- // Update the temporary touch state.
- BitSet32 pointerIds;
- if (isSplit) {
- uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
- pointerIds.markBit(pointerId);
- }
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ mTempTouchState.addGestureMonitors(newGestureMonitors);
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
@@ -1327,7 +1434,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 +1452,13 @@
sp<InputWindowHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y);
if (oldTouchedWindowHandle != newTouchedWindowHandle
- && newTouchedWindowHandle != NULL) {
+ && oldTouchedWindowHandle != nullptr
+ && 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 +1489,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 +1499,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 +1513,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,
@@ -1416,9 +1524,11 @@
}
}
}
- if (! haveForegroundWindow) {
+ bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+ if (!haveForegroundWindow && !hasGestureMonitor) {
#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 " or gesture monitor to receive it.", displayId);
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
@@ -1433,29 +1543,29 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
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];
- if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
- sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
- if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
- mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+ if (foregroundWindowHandle) {
+ const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
+ for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
+ if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
+ mTempTouchState.addOrUpdateWindow(inputWindowHandle,
+ InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+ }
}
}
}
}
// 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;
}
}
@@ -1470,9 +1580,10 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
- if (foregroundWindowHandle->getInfo()->hasWallpaper) {
- for (size_t i = 0; i < mWindowHandles.size(); i++) {
- sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+ if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
+ const std::vector<sp<InputWindowHandle>> windowHandles =
+ getWindowHandlesLocked(displayId);
+ for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* info = windowHandle->getInfo();
if (info->displayId == displayId
&& windowHandle->getInfo()->layoutParamsType
@@ -1490,12 +1601,16 @@
// 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);
}
+ for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+ addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
+ touchedMonitor.yOffset, inputTargets);
+ }
+
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
mTempTouchState.filterNonAsIsTouchWindows();
@@ -1503,7 +1618,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 +1669,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 +1710,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 +1720,59 @@
}
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::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
+ int32_t displayId, float xOffset, float yOffset) {
- 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;
+ std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
+ mGlobalMonitorsByDisplay.find(displayId);
+
+ if (it != mGlobalMonitorsByDisplay.end()) {
+ const std::vector<Monitor>& monitors = it->second;
+ for (const Monitor& monitor : monitors) {
+ addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets);
+ }
}
}
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor,
+ float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) {
+ InputTarget target;
+ target.inputChannel = monitor.inputChannel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.xOffset = xOffset;
+ target.yOffset = yOffset;
+ target.pointerIds.clear();
+ target.globalScaleFactor = 1.0f;
+ inputTargets.push_back(target);
+}
+
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 +1790,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 +1809,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 +1835,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 +1907,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 +1919,7 @@
} else {
return applicationHandle->getName();
}
- } else if (windowHandle != NULL) {
+ } else if (windowHandle != nullptr) {
return windowHandle->getName();
} else {
return "<unknown application or window>";
@@ -1795,8 +1927,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());
@@ -1836,13 +1971,21 @@
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+ ATRACE_NAME(message.c_str());
+ }
#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 +2012,7 @@
#if DEBUG_FOCUS
ALOGD("channel '%s' ~ Split motion event.",
connection->getInputChannelName().c_str());
- logOutboundMotionDetailsLocked(" ", splitMotionEntry);
+ logOutboundMotionDetails(" ", splitMotionEntry);
#endif
enqueueDispatchEntriesLocked(currentTime, connection,
splitMotionEntry, inputTarget);
@@ -1884,6 +2027,13 @@
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+ ATRACE_NAME(message.c_str());
+ }
+
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
@@ -1909,6 +2059,13 @@
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf(
+ "enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
+ connection->getInputChannelName().c_str(),
+ dispatchModeToString(dispatchMode).c_str());
+ ATRACE_NAME(message.c_str());
+ }
int32_t inputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) {
return;
@@ -1919,7 +2076,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) {
@@ -1982,22 +2140,59 @@
delete dispatchEntry;
return; // skip the inconsistent event
}
+
+ dispatchPointerDownOutsideFocus(motionEntry->source,
+ dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
+
break;
}
}
// 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::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) {
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
+ if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
+ return;
+ }
+
+ sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
+ if (inputWindowHandle == nullptr) {
+ return;
+ }
+
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
+
+ bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
+
+ if (!hasFocusChanged) {
+ return;
+ }
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+ commandEntry->newToken = newToken;
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
+ connection->getInputChannelName().c_str());
+ ATRACE_NAME(message.c_str());
+ }
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ startDispatchCycle",
connection->getInputChannelName().c_str());
@@ -2017,7 +2212,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 +2230,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 +2260,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 +2303,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 +2335,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 +2352,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 +2370,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 +2430,7 @@
} // release lock
}
-void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
+void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked (
const CancelationOptions& options) {
for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
synthesizeCancelationEventsForConnectionLocked(
@@ -2241,10 +2438,20 @@
}
}
-void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked (
const CancelationOptions& options) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options);
+ synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay);
+ synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay);
+}
+
+void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
+ const CancelationOptions& options,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
+ for (const auto& it : monitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ for (const Monitor& monitor : monitors) {
+ synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options);
+ }
}
}
@@ -2265,11 +2472,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,29 +2484,32 @@
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;
@@ -2349,7 +2559,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 +2591,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 +2624,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 +2636,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 +2689,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 +2703,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 +2732,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 +2752,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 +2786,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 +2800,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 +2816,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 +2833,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 +2857,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 +2869,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 +2889,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 +2919,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 +2934,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 +2942,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 +2952,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 +2997,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 +3011,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 +3032,7 @@
break;
}
- mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
@@ -2813,7 +3052,7 @@
break;
}
- mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
}
}
@@ -2835,7 +3074,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 +3103,223 @@
}
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()) {
+ // handle no longer valid
+ continue;
+ }
+ const InputWindowInfo* info = handle->getInfo();
+
+ if ((getInputChannelLocked(handle->getToken()) == nullptr &&
+ info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+ const bool noInputChannel =
+ info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
+ const bool canReceiveInput =
+ !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
+ !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+ if (canReceiveInput && !noInputChannel) {
+ ALOGE("Window handle %s has no registered input channel",
+ handle->getName().c_str());
+ }
+ continue;
+ }
+
+ if (info->displayId != displayId) {
+ ALOGE("Window %s updated by wrong display %d, should belong to display %d",
+ handle->getName().c_str(), displayId, info->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 +3330,45 @@
// 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();
}
- mFocusedApplicationHandle = inputApplicationHandle;
+ mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
- } else if (mFocusedApplicationHandle != NULL) {
+ } else if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- mFocusedApplicationHandle->releaseInfo();
- mFocusedApplicationHandle.clear();
+ oldFocusedApplicationHandle.clear();
+ mFocusedApplicationHandlesByDisplay.erase(displayId);
}
#if DEBUG_FOCUS
@@ -3036,6 +3380,66 @@
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_NON_POINTER_EVENTS,
+ "The display which contains this window no longer has focus.");
+ options.displayId = ADISPLAY_ID_NONE;
+ 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 +3447,7 @@
bool changed;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
@@ -3062,7 +3466,7 @@
}
#if DEBUG_FOCUS
- //logDispatchStateLocked();
+ logDispatchStateLocked();
#endif
} // release lock
@@ -3078,7 +3482,7 @@
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mInputFilterEnabled == enabled) {
return;
@@ -3092,29 +3496,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 +3533,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 +3554,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) {
@@ -3205,19 +3610,38 @@
}
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
- dump += StringPrintf(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
- dump += StringPrintf(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
+ dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
+ dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
+ dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled));
+ 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 +3650,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,51 +3662,74 @@
} 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 (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) {
+ for (auto& it : mGlobalMonitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first);
+ dumpMonitors(dump, monitors);
+ }
+ for (auto& it : mGestureMonitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first);
+ dumpMonitors(dump, monitors);
+ }
} else {
- dump += INDENT "MonitoringChannels: <none>\n";
+ dump += INDENT "Monitors: <none>\n";
}
nsecs_t currentTime = now();
@@ -3397,15 +3844,25 @@
mConfig.keyRepeatTimeout * 0.000001f);
}
+void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
+ const size_t numMonitors = monitors.size();
+ for (size_t i = 0; i < numMonitors; i++) {
+ const Monitor& monitor = monitors[i];
+ const sp<InputChannel>& channel = monitor.inputChannel;
+ dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str());
+ dump += "\n";
+ }
+}
+
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+ 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 (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
@@ -3413,14 +3870,11 @@
return BAD_VALUE;
}
- sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
+ sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
-
- if (monitor) {
- mMonitoringChannels.push(inputChannel);
- }
+ mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
@@ -3430,13 +3884,47 @@
return OK;
}
+status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ if (displayId < 0) {
+ ALOGW("Attempted to register input monitor without a specified display.");
+ return BAD_VALUE;
+ }
+
+ if (inputChannel->getToken() == nullptr) {
+ ALOGW("Attempted to register input monitor without an identifying token.");
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = new Connection(inputChannel, true /*monitor*/);
+
+ const int fd = inputChannel->getFd();
+ mConnectionsByFd.add(fd, connection);
+ mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+
+ auto& monitorsByDisplay = isGestureMonitor
+ ? mGestureMonitorsByDisplay
+ : mGlobalMonitorsByDisplay;
+ monitorsByDisplay[displayId].emplace_back(inputChannel);
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+
+ }
+ // Wake the looper because some connections have changed.
+ mLooper->wake();
+ return OK;
+}
+
status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str());
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
if (status) {
@@ -3462,6 +3950,8 @@
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
mConnectionsByFd.removeItemsAt(connectionIndex);
+ mInputChannelsByToken.erase(inputChannel->getToken());
+
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
}
@@ -3476,20 +3966,98 @@
}
void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- if (mMonitoringChannels[i] == inputChannel) {
- mMonitoringChannels.removeAt(i);
- break;
- }
+ removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
+ removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
+}
+
+void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
+ for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end(); ) {
+ std::vector<Monitor>& monitors = it->second;
+ const size_t numMonitors = monitors.size();
+ for (size_t i = 0; i < numMonitors; i++) {
+ if (monitors[i].inputChannel == inputChannel) {
+ monitors.erase(monitors.begin() + i);
+ break;
+ }
+ }
+ if (monitors.empty()) {
+ it = monitorsByDisplay.erase(it);
+ } else {
+ ++it;
+ }
}
}
+status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ std::optional<int32_t> foundDisplayId = findGestureMonitorDisplayByTokenLocked(token);
+
+ if (!foundDisplayId) {
+ ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token");
+ return BAD_VALUE;
+ }
+ int32_t displayId = foundDisplayId.value();
+
+ ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
+ if (stateIndex < 0) {
+ ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
+ return BAD_VALUE;
+ }
+
+ TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ std::optional<int32_t> foundDeviceId;
+ for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
+ if (touchedMonitor.monitor.inputChannel->getToken() == token) {
+ foundDeviceId = state.deviceId;
+ }
+ }
+ if (!foundDeviceId || !state.down) {
+ ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
+ " Ignoring.");
+ return BAD_VALUE;
+ }
+ int32_t deviceId = foundDeviceId.value();
+
+ // Send cancel events to all the input channels we're stealing from.
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "gesture monitor stole pointer stream");
+ options.deviceId = deviceId;
+ options.displayId = displayId;
+ for (const TouchedWindow& window : state.windows) {
+ sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
+ synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ }
+ // Then clear the current touch state so we stop dispatching to them as well.
+ state.filterNonMonitors();
+ }
+ return OK;
+}
+
+
+std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
+ const sp<IBinder>& token) {
+ for (const auto& it : mGestureMonitorsByDisplay) {
+ const std::vector<Monitor>& monitors = it.second;
+ for (const Monitor& monitor : monitors) {
+ if (monitor.inputChannel->getToken() == token) {
+ return it.first;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
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 +4084,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 +4102,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 +4115,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 +4124,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 +4145,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 +4185,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",
@@ -3617,6 +4207,12 @@
entry->release();
}
+void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+ mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
+ mLock.lock();
+}
+
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
@@ -3655,12 +4251,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 +4267,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 +4461,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 +4477,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 +4485,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 +4494,7 @@
}
void InputDispatcher::dump(std::string& dump) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
dump += "Input Dispatcher State:\n";
dumpDispatchStateLocked(dump);
@@ -3901,10 +4507,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 +4537,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 +4559,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 +4581,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 +4598,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 +4614,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 +4633,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 +4661,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 +4684,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 +4717,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 +4748,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 +4773,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 +4792,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 +4806,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 +4831,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 +4844,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 +4859,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 +4874,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 +4887,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 +4902,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 +4914,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 +4952,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 +4985,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);
}
}
}
@@ -4410,11 +5023,15 @@
bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento,
const CancelationOptions& options) {
- if (options.keyCode != -1 && memento.keyCode != options.keyCode) {
+ if (options.keyCode && memento.keyCode != options.keyCode.value()) {
return false;
}
- if (options.deviceId != -1 && memento.deviceId != options.deviceId) {
+ if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+ return false;
+ }
+
+ if (options.displayId && memento.displayId != options.displayId.value()) {
return false;
}
@@ -4431,7 +5048,11 @@
bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento,
const CancelationOptions& options) {
- if (options.deviceId != -1 && memento.deviceId != options.deviceId) {
+ if (options.deviceId && memento.deviceId != options.deviceId.value()) {
+ return false;
+ }
+
+ if (options.displayId && memento.displayId != options.displayId.value()) {
return false;
}
@@ -4450,9 +5071,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 +5081,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,30 +5107,39 @@
}
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::Monitor
+InputDispatcher::Monitor::Monitor(const sp<InputChannel>& inputChannel) :
+ inputChannel(inputChannel) {
}
// --- 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) {
}
InputDispatcher::CommandEntry::~CommandEntry() {
}
+// --- InputDispatcher::TouchedMonitor ---
+InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset,
+ float yOffset) : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {
+}
// --- 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 +5150,10 @@
split = false;
deviceId = -1;
source = 0;
- displayId = -1;
+ displayId = ADISPLAY_ID_NONE;
windows.clear();
+ portalWindows.clear();
+ gestureMonitors.clear();
}
void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4532,6 +5163,8 @@
source = other.source;
displayId = other.displayId;
windows = other.windows;
+ portalWindows = other.portalWindows;
+ gestureMonitors = other.gestureMonitors;
}
void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4541,7 +5174,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 +5185,44 @@
}
}
- 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::addGestureMonitors(
+ const std::vector<TouchedMonitor>& newMonitors) {
+ const size_t newSize = gestureMonitors.size() + newMonitors.size();
+ gestureMonitors.reserve(newSize);
+ gestureMonitors.insert(std::end(gestureMonitors),
+ std::begin(newMonitors), std::end(newMonitors));
}
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 +5230,37 @@
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);
}
}
}
+void InputDispatcher::TouchState::filterNonMonitors() {
+ windows.clear();
+ portalWindows.clear();
+}
+
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..753b748 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -17,25 +17,29 @@
#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 <optional>
+#include <ui/Region.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 +162,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 +213,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 +246,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.
@@ -266,6 +274,13 @@
*/
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid) = 0;
+
+ /* Notifies the policy that a pointer down event has occurred outside the current focused
+ * window.
+ *
+ * The touchedToken passed as an argument is the window that received the input event.
+ */
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
};
@@ -299,7 +314,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 +322,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,22 +353,42 @@
*/
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.
*
- * 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;
+
+ /* Registers input channels to be used to monitor input events.
+ *
+ * Each monitor must target a specific display and will only receive input events sent to that
+ * display. If the monitor is a gesture monitor, it will only receive pointer events on the
+ * targeted display.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t registerInputMonitor(
+ const sp<InputChannel>& inputChannel, int32_t displayId, bool gestureMonitor) = 0;
+
+ /* 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;
+
+ /* Allows an input monitor steal the current pointer stream away from normal input windows.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
+
};
/* Dispatches events to input targets. Some functions of the input dispatcher, such as
@@ -372,32 +415,39 @@
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- virtual void dump(std::string& dump);
- virtual void monitor();
+ virtual void dump(std::string& dump) override;
+ virtual void monitor() override;
- virtual void dispatchOnce();
+ virtual void dispatchOnce() override;
- virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
- virtual void notifyKey(const NotifyKeyArgs* args);
- virtual void notifyMotion(const NotifyMotionArgs* args);
- virtual void notifySwitch(const NotifySwitchArgs* args);
- virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+ 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 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);
+ uint32_t policyFlags) override;
- virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles);
- virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle);
- virtual void setInputDispatchMode(bool enabled, bool frozen);
- virtual void setInputFilterEnabled(bool enabled);
+ virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles,
+ int32_t displayId,
+ const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
+ virtual void setFocusedApplication(int32_t displayId,
+ const sp<InputApplicationHandle>& inputApplicationHandle) override;
+ virtual void setFocusedDisplay(int32_t displayId) override;
+ virtual void setInputDispatchMode(bool enabled, bool frozen) override;
+ virtual void setInputFilterEnabled(bool enabled) override;
- virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
- const sp<InputChannel>& toChannel);
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken)
+ override;
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
+ int32_t displayId) override;
+ virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel,
+ int32_t displayId, bool isGestureMonitor) override;
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
+ virtual status_t pilferPointers(const sp<IBinder>& token) override;
private:
template <typename T>
@@ -406,7 +456,7 @@
T* prev;
protected:
- inline Link() : next(NULL), prev(NULL) { }
+ inline Link() : next(nullptr), prev(nullptr) { }
};
struct InjectionState {
@@ -433,6 +483,7 @@
TYPE_MOTION
};
+ uint32_t sequenceNum;
mutable int32_t refCount;
int32_t type;
nsecs_t eventTime;
@@ -441,20 +492,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 +515,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 +525,7 @@
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
int32_t action;
int32_t flags;
int32_t keyCode;
@@ -493,9 +545,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 +560,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 +597,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 +607,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 +656,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 +672,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 +687,7 @@
} else {
head = entry;
}
- entry->next = NULL;
+ entry->next = nullptr;
tail = entry;
}
@@ -641,7 +699,7 @@
} else {
tail = entry;
}
- entry->prev = NULL;
+ entry->prev = nullptr;
head = entry;
}
@@ -664,9 +722,9 @@
T* entry = head;
head = entry->next;
if (head) {
- head->prev = NULL;
+ head->prev = nullptr;
} else {
- tail = NULL;
+ tail = nullptr;
}
return entry;
}
@@ -691,14 +749,16 @@
// Descriptive reason for the cancelation.
const char* reason;
- // The specific keycode of the key event to cancel, or -1 to cancel any key event.
- int32_t keyCode;
+ // The specific keycode of the key event to cancel, or nullopt to cancel any key event.
+ std::optional<int32_t> keyCode = std::nullopt;
- // The specific device id of events to cancel, or -1 to cancel events from any device.
- int32_t deviceId;
+ // The specific device id of events to cancel, or nullopt to cancel events from any device.
+ std::optional<int32_t> deviceId = std::nullopt;
- CancelationOptions(Mode mode, const char* reason) :
- mode(mode), reason(reason), keyCode(-1), deviceId(-1) { }
+ // The specific display id of events to cancel, or nullopt to cancel events on any display.
+ std::optional<int32_t> displayId = std::nullopt;
+
+ CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) { }
};
/* Tracks dispatched key and motion event state so that cancelation events can be
@@ -727,7 +787,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 +814,7 @@
struct KeyMemento {
int32_t deviceId;
uint32_t source;
+ int32_t displayId;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
@@ -765,11 +826,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 +840,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 +873,6 @@
Status status;
sp<InputChannel> inputChannel; // never null
- sp<InputWindowHandle> inputWindowHandle; // may be null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
@@ -828,8 +888,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(); }
@@ -839,6 +898,12 @@
DispatchEntry* findWaitQueueEntry(uint32_t seq);
};
+ struct Monitor {
+ sp<InputChannel> inputChannel; // never null
+
+ explicit Monitor(const sp<InputChannel>& inputChannel);
+ };
+
enum DropReason {
DROP_REASON_NOT_DROPPED = 0,
DROP_REASON_POLICY = 1,
@@ -851,73 +916,94 @@
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;
+ // Finds the display ID of the gesture monitor identified by the provided token.
+ std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
+ REQUIRES(mLock);
+
+ 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<Monitor>> mGlobalMonitorsByDisplay
+ GUARDED_BY(mLock);
+
+ // Input channels that will receive pointer events that start within the corresponding display.
+ // These are a bit special when compared to global monitors since they'll cause gesture streams
+ // to continue even when there isn't a touched window,and have the ability to steal the rest of
+ // the pointer stream in order to claim it for a system gesture.
+ std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay
+ 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 +1017,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 {
@@ -966,13 +1061,30 @@
int32_t targetFlags;
BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
};
+
+ // For tracking the offsets we need to apply when adding gesture monitor targets.
+ struct TouchedMonitor {
+ Monitor monitor;
+ float xOffset = 0.f;
+ float yOffset = 0.f;
+
+ explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
+ };
+
struct TouchState {
bool down;
bool split;
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;
+
+ std::vector<TouchedMonitor> gestureMonitors;
TouchState();
~TouchState();
@@ -980,37 +1092,45 @@
void copyFrom(const TouchState& other);
void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
+ void addPortalWindow(const sp<InputWindowHandle>& windowHandle);
+ void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
void removeWindow(const sp<InputWindowHandle>& windowHandle);
+ void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
+ void filterNonMonitors();
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 +1139,153 @@
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);
+ std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId,
+ const std::vector<sp<InputWindowHandle>>& portalWindows) REQUIRES(mLock);
+ void addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, float yOffset = 0);
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 addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+ std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ void addGlobalMonitoringTargetsLocked(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);
+ EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntryLocked(const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode)
+ REQUIRES(mLock);
+ 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);
+ // The action sent should only be of type AMOTION_EVENT_*
+ void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) REQUIRES(mLock);
void synthesizeCancelationEventsForAllConnectionsLocked(
- const CancelationOptions& options);
- void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options);
+ const CancelationOptions& options) REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(
+ const CancelationOptions& options) REQUIRES(mLock);
+ void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel,
- const CancelationOptions& options);
+ 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 dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
+ 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);
+ void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel,
+ std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay)
+ 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);
+ void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
+ REQUIRES(mLock);
// 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..a45b8a5 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,38 @@
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.count);
+ 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 +4405,7 @@
cookAndDispatch(mCurrentRawState.when);
}
if (count != 0) {
- mRawStatesPending.removeItemsAt(0, count);
+ mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
}
if (mExternalStylusDataPending) {
@@ -4448,7 +4464,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 +4512,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 +4538,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 +4754,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 +5284,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 +5449,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 +5496,7 @@
mPointerVelocityControl.reset();
// Remove any current spots.
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->clearSpots();
}
@@ -6335,8 +6358,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 +6369,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 +6390,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 +6406,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 +6433,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 +6468,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 +6531,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 +6573,7 @@
}
void TouchInputMapper::fadePointer() {
- if (mPointerController != NULL) {
+ if (mPointerController != nullptr) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
}
}
@@ -6554,12 +6592,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 +6609,7 @@
}
}
- return NULL;
+ return nullptr;
}
void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
@@ -6769,9 +6804,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 +6818,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 +6829,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 +6840,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 +6961,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 +7052,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 +7295,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 +7317,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 +7346,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 +7461,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..9054316 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -3,16 +3,23 @@
cc_test {
name: "inputflinger_tests",
srcs: [
- "InputReader_test.cpp",
+ "BlockingQueue_test.cpp",
+ "TestInputListener.cpp",
+ "InputClassifier_test.cpp",
+ "InputClassifierConverter_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 +28,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/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
new file mode 100644
index 0000000..813b69e
--- /dev/null
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "../InputClassifierConverter.h"
+
+#include <gtest/gtest.h>
+#include <utils/BitSet.h>
+
+
+using namespace android::hardware::input;
+
+namespace android {
+
+// --- InputClassifierConverterTest ---
+
+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, 2);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
+ 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;
+}
+
+static float getMotionEventAxis(common::V1_0::PointerCoords coords,
+ common::V1_0::Axis axis) {
+ uint32_t index = BitSet64::getIndexOfBit(static_cast<uint64_t>(coords.bits),
+ static_cast<uint64_t>(axis));
+ return coords.values[index];
+}
+
+/**
+ * Check that coordinates get converted properly from the framework's PointerCoords
+ * to the hidl PointerCoords in input::common.
+ */
+TEST(InputClassifierConverterTest, PointerCoordsAxes) {
+ const NotifyMotionArgs motionArgs = generateBasicMotionArgs();
+ ASSERT_EQ(1, motionArgs.pointerCoords[0].getX());
+ ASSERT_EQ(2, motionArgs.pointerCoords[0].getY());
+ ASSERT_EQ(0.5, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE));
+ ASSERT_EQ(3U, BitSet64::count(motionArgs.pointerCoords[0].bits));
+
+ common::V1_0::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs);
+
+ ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::X),
+ motionArgs.pointerCoords[0].getX());
+ ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::Y),
+ motionArgs.pointerCoords[0].getY());
+ ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::SIZE),
+ motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE));
+ ASSERT_EQ(BitSet64::count(motionArgs.pointerCoords[0].bits),
+ BitSet64::count(motionEvent.pointerCoords[0].bits));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
new file mode 100644
index 0000000..7cc17a2
--- /dev/null
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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 {
+ mMotionClassifier = std::make_unique<MotionClassifier>();
+ }
+};
+
+/**
+ * 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
+ 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
+ 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
+ ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
+}
+
+/**
+ * Make sure MotionClassifier does not crash when it is reset.
+ */
+TEST_F(MotionClassifierTest, Reset_DoesNotCrash) {
+ 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*/);
+ 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..9fe6481 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,113 @@
public:
FakeInputDispatcherPolicy() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ mOnPointerDownToken.clear();
+ }
+
+ 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.";
+ }
+
+ void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+ ASSERT_EQ(mOnPointerDownToken, touchedToken)
+ << "Expected token from onPointerDownOutsideFocus was not matched";
+ reset();
}
private:
+ bool mInputEventFiltered;
+ nsecs_t mTime;
+ int32_t mAction;
+ int32_t mDisplayId;
+ sp<IBinder> mOnPointerDownToken;
+
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 +168,18 @@
virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
return false;
}
+
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+ mOnPointerDownToken = newToken;
+ }
+
+ void reset() {
+ mInputEventFiltered = false;
+ mTime = -1;
+ mAction = -1;
+ mDisplayId = -1;
+ mOnPointerDownToken.clear();
+ }
};
@@ -103,13 +189,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 +213,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 +241,788 @@
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() {
+ 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;
+ 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), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
+ 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 = mLayoutParamFlags;
+ mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.frameLeft = mFrame.left;
+ mInfo.frameTop = mFrame.top;
+ mInfo.frameRight = mFrame.right;
+ mInfo.frameBottom = mFrame.bottom;
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.addTouchableRegion(mFrame);
+ 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 setFrame(const Rect& frame) {
+ mFrame.set(frame);
+ }
+
+ void setLayoutParamFlags(int32_t flags) {
+ mLayoutParamFlags = flags;
+ }
+
+ void releaseChannel() {
+ mServerChannel.clear();
+ InputWindowHandle::releaseChannel();
+ }
+protected:
+ virtual bool handled() {
+ return true;
+ }
+
+ bool mFocused;
+ Rect mFrame;
+ int32_t mLayoutParamFlags;
+};
+
+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, int32_t x = 100, int32_t y = 200) {
+ 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, x);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+
+ 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, bool isGestureMonitor = false)
+ : FakeInputReceiver(dispatcher, name, displayId) {
+ mServerChannel->setToken(new BBinder());
+ mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
+ }
+};
+
+// 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);
+}
+
+class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
+ virtual void SetUp() {
+ InputDispatcherTest::SetUp();
+
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+ mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
+ mWindowFocused->setFrame(Rect(50, 50, 100, 100));
+ mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mWindowFocusedTouchPoint = 60;
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mWindowFocused->setFocus();
+
+ // Expect one focus window exist in display.
+ std::vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push_back(mUnfocusedWindow);
+ inputWindowHandles.push_back(mWindowFocused);
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ }
+
+ virtual void TearDown() {
+ InputDispatcherTest::TearDown();
+
+ mUnfocusedWindow.clear();
+ mWindowFocused.clear();
+ }
+
+protected:
+ sp<FakeWindowHandle> mUnfocusedWindow;
+ sp<FakeWindowHandle> mWindowFocused;
+ int32_t mWindowFocusedTouchPoint;
+};
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
+// the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TRACKBALL and action
+// DOWN on the window that doesn't have focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
+// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that already has focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus,
+ OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
+ mWindowFocusedTouchPoint))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ // Call monitor to wait for the command queue to get flushed.
+ mDispatcher->monitor();
+
+ mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
} // 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..717f317 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 {
@@ -36,7 +45,9 @@
ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
-static status_t StatusFromResult(Result result) {
+namespace {
+
+status_t statusFromResult(Result result) {
switch (result) {
case Result::OK:
return OK;
@@ -51,12 +62,57 @@
}
}
+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,
+};
+
+} // anonymous namespace
+
+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 =
+ (checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION);
+}
+
+void SensorDevice::initializeSensorList() {
float minPowerMa = 0.001; // 1 microAmp
checkReturn(mSensors->getSensorsList(
@@ -70,7 +126,7 @@
convertToSensor(list[i], &sensor);
// Sanity check and clamp power if it is 0 (or close)
if (sensor.power < minPowerMa) {
- ALOGE("Reported power %f not deemed sane, clamping to %f",
+ ALOGI("Reported power %f not deemed sane, clamping to %f",
sensor.power, minPowerMa);
sensor.power = minPowerMa;
}
@@ -81,37 +137,212 @@
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 = checkReturnAndGetStatus(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()) {
+ ALOGI("Sensor list size changed from %zu to %zu", 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)) {
+ ALOGI("Sensor %s not equivalent to previous version", newSensor.name);
+ didChange = true;
+ }
+ }
+ }
+
+ if (!found) {
+ // Could not find the new sensor in the old list of sensors, the lists must
+ // have changed.
+ ALOGI("Sensor %s (handle %d) did not exist before", newSensor.name, newSensor.handle);
+ 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) {
@@ -136,7 +367,7 @@
for (const auto & s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
- if (info.batchParams.isEmpty()) continue;
+ if (info.numActiveClients() == 0) continue;
result.appendFormat("0x%08x) active-count = %zu; ", handle, info.batchParams.size());
@@ -173,6 +404,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;
@@ -187,7 +431,7 @@
convertToSensorEvents(events, dynamicSensorsAdded, buffer);
err = (ssize_t)events.size();
} else {
- err = StatusFromResult(result);
+ err = statusFromResult(result);
}
});
@@ -208,7 +452,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 +460,84 @@
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(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);
@@ -225,15 +547,23 @@
}
Info& info(mActivationCount.editValueAt(activationIndex));
info.removeBatchParamsForIdent(ident);
+ if (info.numActiveClients() == 0) {
+ info.isActive = false;
+ }
}
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);
@@ -255,10 +585,9 @@
}
if (info.batchParams.indexOfKey(ident) >= 0) {
- if (info.numActiveClients() == 1) {
- // This is the first connection, we need to activate the underlying h/w sensor.
- actuateHardware = true;
- }
+ if (info.numActiveClients() > 0 && !info.isActive) {
+ actuateHardware = true;
+ }
} else {
// Log error. Every activate call should be preceded by a batch() call.
ALOGE("\t >>>ERROR: activate called without batch");
@@ -300,13 +629,18 @@
if (actuateHardware) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
enabled);
- err = StatusFromResult(checkReturn(mSensors->activate(handle, enabled)));
+ err = checkReturnAndGetStatus(mSensors->activate(handle, enabled));
ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
strerror(-err));
if (err != NO_ERROR && enabled) {
// Failure when enabling the sensor. Clean up on failure.
info.removeBatchParamsForIdent(ident);
+ } else {
+ // Update the isActive flag if there is no error. If there is an error when disabling a
+ // sensor, still set the flag to false since the batch parameters have already been
+ // removed. This ensures that everything remains in-sync.
+ info.isActive = enabled;
}
}
@@ -333,6 +667,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);
@@ -363,9 +702,8 @@
if (prevBestBatchParams != info.bestBatchParams) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
- err = StatusFromResult(
- checkReturn(mSensors->batch(
- handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch)));
+ err = checkReturnAndGetStatus(mSensors->batch(
+ handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
if (err != NO_ERROR) {
ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
mSensors.get(), handle, info.bestBatchParams.mTSample,
@@ -389,7 +727,7 @@
if (mSensors == nullptr) return NO_INIT;
if (isClientDisabled(ident)) return INVALID_OPERATION;
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
- return StatusFromResult(checkReturn(mSensors->flush(handle)));
+ return checkReturnAndGetStatus(mSensors->flush(handle));
}
bool SensorDevice::isClientDisabled(void* ident) {
@@ -401,6 +739,15 @@
return mDisabledClients.indexOf(ident) >= 0;
}
+bool SensorDevice::isSensorActive(int handle) const {
+ Mutex::Autolock _l(mLock);
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ return false;
+ }
+ return mActivationCount.valueAt(activationIndex).numActiveClients() > 0;
+}
+
void SensorDevice::enableAllSensors() {
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
@@ -413,18 +760,20 @@
const int sensor_handle = mActivationCount.keyAt(i);
ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
sensor_handle);
- status_t err = StatusFromResult(
- checkReturn(mSensors->batch(
- sensor_handle,
- info.bestBatchParams.mTSample,
- info.bestBatchParams.mTBatch)));
+ status_t err = checkReturnAndGetStatus(mSensors->batch(
+ sensor_handle,
+ info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch));
ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
if (err == NO_ERROR) {
- err = StatusFromResult(
- checkReturn(mSensors->activate(sensor_handle, 1 /* enabled */)));
+ err = checkReturnAndGetStatus(mSensors->activate(sensor_handle, 1 /* enabled */));
ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
}
+
+ if (err == NO_ERROR) {
+ info.isActive = true;
+ }
}
}
@@ -432,7 +781,7 @@
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
for (size_t i = 0; i< mActivationCount.size(); ++i) {
- const Info& info = mActivationCount.valueAt(i);
+ Info& info = mActivationCount.editValueAt(i);
// Check if this sensor has been activated previously and disable it.
if (info.batchParams.size() > 0) {
const int sensor_handle = mActivationCount.keyAt(i);
@@ -446,6 +795,8 @@
mDisabledClients.add(info.batchParams.keyAt(j));
ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
}
+
+ info.isActive = false;
}
}
}
@@ -464,14 +815,13 @@
Event ev;
convertFromSensorEvent(*injected_sensor_event, &ev);
- return StatusFromResult(checkReturn(mSensors->injectSensorData(ev)));
+ return checkReturnAndGetStatus(mSensors->injectSensorData(ev));
}
status_t SensorDevice::setMode(uint32_t mode) {
if (mSensors == nullptr) return NO_INIT;
- return StatusFromResult(
- checkReturn(mSensors->setOperationMode(
- static_cast<hardware::sensors::V1_0::OperationMode>(mode))));
+ return checkReturnAndGetStatus(mSensors->setOperationMode(
+ static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
}
int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
@@ -509,7 +859,7 @@
if (result == Result::OK) {
ret = channelHandle;
} else {
- ret = StatusFromResult(result);
+ ret = statusFromResult(result);
}
}));
return ret;
@@ -548,12 +898,12 @@
checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
[&ret, rate] (auto result, auto token) {
if (rate == RateLevel::STOP) {
- ret = StatusFromResult(result);
+ ret = statusFromResult(result);
} else {
if (result == Result::OK) {
ret = token;
} else {
- ret = StatusFromResult(result);
+ ret = statusFromResult(result);
}
}
}));
@@ -563,7 +913,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 +1001,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 +1012,17 @@
}
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 (!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");
+ }
+}
+
+status_t SensorDevice::checkReturnAndGetStatus(const Return<Result>& ret) {
+ checkReturn(ret);
+ return (!ret.isOk()) ? DEAD_OBJECT : statusFromResult(ret);
}
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 6d75051..d2c6994 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,24 @@
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;
+ }
+
+ bool isSensorActive(int handle) const;
+
// 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;
@@ -141,6 +165,9 @@
// requested by the client.
KeyedVector<void*, BatchParams> batchParams;
+ // Flag to track if the sensor is active
+ bool isActive = false;
+
// Sets batch parameters for this ident. Returns error if this ident is not already present
// in the KeyedVector above.
status_t setBatchParamsForIdent(void* ident, int flags, int64_t samplingPeriodNs,
@@ -151,7 +178,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,15 +190,35 @@
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);
- static void handleHidlDeath(const std::string &detail);
+ 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);
+
+ void handleHidlDeath(const std::string &detail);
template<typename T>
- static Return<T> checkReturn(Return<T> &&ret) {
+ void checkReturn(const Return<T>& ret) {
if (!ret.isOk()) {
handleHidlDeath(ret.description());
}
- return std::move(ret);
}
+ status_t checkReturnAndGetStatus(const Return<Result>& ret);
//TODO(b/67425500): remove waiter after bug is resolved.
sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
@@ -189,6 +236,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..0e40940 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;
}
@@ -77,7 +78,14 @@
void SensorService::SensorEventConnection::dump(String8& result) {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tOperating Mode: %s\n",mDataInjectionMode ? "DATA_INJECTION" : "NORMAL");
+ result.appendFormat("\tOperating Mode: ");
+ if (!mService->isWhiteListedPackage(getPackageName())) {
+ result.append("RESTRICTED\n");
+ } else if (mDataInjectionMode) {
+ result.append("DATA_INJECTION\n");
+ } else {
+ result.append("NORMAL\n");
+ }
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
"max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
mMaxCacheSize);
@@ -200,7 +208,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 +232,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);
@@ -277,8 +285,9 @@
scratch[count++] = buffer[i];
}
} else {
- // Regular sensor event, just copy it to the scratch buffer.
- if (mHasSensorAccess) {
+ // Regular sensor event, just copy it to the scratch buffer after checking
+ // the AppOp.
+ if (hasSensorAccess() && noteOpIfRequired(buffer[i])) {
scratch[count++] = buffer[i];
}
}
@@ -289,11 +298,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 +315,6 @@
sendPendingFlushEventsLocked();
// Early return if there are no events for this connection.
if (count == 0) {
- delete sanitizedBuffer;
return status_t(NO_ERROR);
}
@@ -315,39 +324,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 +355,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 +375,6 @@
}
#endif
- delete sanitizedBuffer;
return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
@@ -403,6 +383,20 @@
mHasSensorAccess = hasAccess;
}
+bool SensorService::SensorEventConnection::hasSensorAccess() {
+ return mHasSensorAccess && !mService->mSensorPrivacyPolicy->isSensorPrivacyEnabled();
+}
+
+bool SensorService::SensorEventConnection::noteOpIfRequired(const sensors_event_t& event) {
+ bool success = true;
+ const auto iter = mHandleToAppOp.find(event.sensor);
+ if (iter != mHandleToAppOp.end()) {
+ int32_t appOpMode = mService->sAppOpsManager.noteOp((*iter).second, mUid, mOpPackageName);
+ success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ }
+ return success;
+}
+
void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t const* scratch,
int count) {
sensors_event_t *eventCache_new;
@@ -415,12 +409,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 +513,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..fd881cb 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <unordered_map>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
@@ -53,7 +54,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 +109,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 +131,13 @@
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();
+
+ // Call noteOp for the sensor if the sensor requires a permission
+ bool noteOpIfRequired(const sensors_event_t& event);
+
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
@@ -161,6 +173,8 @@
sensors_event_t *mEventCache;
int mCacheSize, mMaxCacheSize;
+ int64_t mTimeOfLastEventDrop;
+ int mEventsDropped;
String8 mPackageName;
const String16 mOpPackageName;
#if DEBUG_CONNECTIONS
@@ -171,6 +185,10 @@
mutable Mutex mDestroyLock;
bool mDestroyed;
bool mHasSensorAccess;
+
+ // Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
+ // valid mapping for sensors that require a permission in order to reduce the lookup time.
+ std::unordered_map<int32_t, int32_t> mHandleToAppOp;
};
} // namepsace android
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..c11b88e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <android/content/pm/IPackageManagerNative.h>
#include <binder/ActivityManager.h>
-#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -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>
@@ -73,6 +75,9 @@
const char* SensorService::WAKE_LOCK_NAME = "SensorService_wakelock";
uint8_t SensorService::sHmacGlobalKey[128] = {};
bool SensorService::sHmacGlobalKeyIsValid = false;
+std::map<String16, int> SensorService::sPackageTargetVersion;
+Mutex SensorService::sPackageTargetVersionLock;
+AppOpsManager SensorService::sAppOpsManager;
#define SENSOR_SERVICE_DIR "/data/system/sensor_service"
#define SENSOR_SERVICE_HMAC_KEY_FILE SENSOR_SERVICE_DIR "/hmac_key"
@@ -87,6 +92,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 +256,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 +291,9 @@
// Start watching UID changes to apply policy.
mUidPolicy->registerSelf();
+
+ // Start watching sensor privacy changes
+ mSensorPrivacyPolicy->registerSelf();
}
}
}
@@ -295,7 +304,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 +315,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 +346,7 @@
delete entry.second;
}
mUidPolicy->unregisterSelf();
+ mSensorPrivacyPolicy->unregisterSelf();
}
status_t SensorService::dump(int fd, const Vector<String16>& args) {
@@ -363,35 +373,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 +414,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());
@@ -448,12 +444,15 @@
}
result.append("Active sensors:\n");
+ SensorDevice& dev = SensorDevice::getInstance();
for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
int handle = mActiveSensors.keyAt(i);
- result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
- getSensorName(handle).string(),
- handle,
- mActiveSensors.valueAt(i)->getNumConnections());
+ if (dev.isSensorActive(handle)) {
+ result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
+ getSensorName(handle).string(),
+ handle,
+ mActiveSensors.valueAt(i)->getNumConnections());
+ }
}
result.appendFormat("Socket Buffer size = %zd events\n",
@@ -471,11 +470,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 +514,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)) {
@@ -521,11 +568,11 @@
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
- if (args.size() == 3 && args[0] == String16("set-uid-state")) {
+ if (args[0] == String16("set-uid-state")) {
return handleSetUidState(args, err);
- } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
+ } else if (args[0] == String16("reset-uid-state")) {
return handleResetUidState(args, err);
- } else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
+ } else if (args[0] == String16("get-uid-state")) {
return handleGetUidState(args, out, err);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
@@ -535,14 +582,32 @@
return BAD_VALUE;
}
-status_t SensorService::handleSetUidState(Vector<String16>& args, int err) {
+static status_t getUidForPackage(String16 packageName, int userId, /*inout*/uid_t& uid, int err) {
PermissionController pc;
- int uid = pc.getPackageUid(args[1], 0);
+ uid = pc.getPackageUid(packageName, 0);
if (uid <= 0) {
- ALOGE("Unknown package: '%s'", String8(args[1]).string());
- dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+ ALOGE("Unknown package: '%s'", String8(packageName).string());
+ dprintf(err, "Unknown package: '%s'\n", String8(packageName).string());
return BAD_VALUE;
}
+
+ if (userId < 0) {
+ ALOGE("Invalid user: %d", userId);
+ dprintf(err, "Invalid user: %d\n", userId);
+ return BAD_VALUE;
+ }
+
+ uid = multiuser_get_uid(userId, uid);
+ return NO_ERROR;
+}
+
+status_t SensorService::handleSetUidState(Vector<String16>& args, int err) {
+ // Valid arg.size() is 3 or 5, args.size() is 5 with --user option.
+ if (!(args.size() == 3 || args.size() == 5)) {
+ printHelp(err);
+ return BAD_VALUE;
+ }
+
bool active = false;
if (args[2] == String16("active")) {
active = true;
@@ -550,30 +615,59 @@
ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
return BAD_VALUE;
}
+
+ int userId = 0;
+ if (args.size() == 5 && args[3] == String16("--user")) {
+ userId = atoi(String8(args[4]));
+ }
+
+ uid_t uid;
+ if (getUidForPackage(args[1], userId, uid, err) != NO_ERROR) {
+ return BAD_VALUE;
+ }
+
mUidPolicy->addOverrideUid(uid, active);
return NO_ERROR;
}
status_t SensorService::handleResetUidState(Vector<String16>& args, int err) {
- PermissionController pc;
- int uid = pc.getPackageUid(args[1], 0);
- if (uid < 0) {
- ALOGE("Unknown package: '%s'", String8(args[1]).string());
- dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+ // Valid arg.size() is 2 or 4, args.size() is 4 with --user option.
+ if (!(args.size() == 2 || args.size() == 4)) {
+ printHelp(err);
return BAD_VALUE;
}
+
+ int userId = 0;
+ if (args.size() == 4 && args[2] == String16("--user")) {
+ userId = atoi(String8(args[3]));
+ }
+
+ uid_t uid;
+ if (getUidForPackage(args[1], userId, uid, err) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+
mUidPolicy->removeOverrideUid(uid);
return NO_ERROR;
}
status_t SensorService::handleGetUidState(Vector<String16>& args, int out, int err) {
- PermissionController pc;
- int uid = pc.getPackageUid(args[1], 0);
- if (uid < 0) {
- ALOGE("Unknown package: '%s'", String8(args[1]).string());
- dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+ // Valid arg.size() is 2 or 4, args.size() is 4 with --user option.
+ if (!(args.size() == 2 || args.size() == 4)) {
+ printHelp(err);
return BAD_VALUE;
}
+
+ int userId = 0;
+ if (args.size() == 4 && args[2] == String16("--user")) {
+ userId = atoi(String8(args[3]));
+ }
+
+ uid_t uid;
+ if (getUidForPackage(args[1], userId, uid, err) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+
if (mUidPolicy->isUidActive(uid)) {
return dprintf(out, "active\n");
} else {
@@ -583,9 +677,9 @@
status_t SensorService::printHelp(int out) {
return dprintf(out, "Sensor service commands:\n"
- " get-uid-state <PACKAGE> gets the uid state\n"
- " set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
- " reset-uid-state <PACKAGE> clears the uid state override\n"
+ " get-uid-state <PACKAGE> [--user USER_ID] gets the uid state\n"
+ " set-uid-state <PACKAGE> <active|idle> [--user USER_ID] overrides the uid state\n"
+ " reset-uid-state <PACKAGE> [--user USER_ID] clears the uid state override\n"
" help print this message\n");
}
@@ -627,8 +721,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 +750,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 +823,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 +871,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 +884,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 +917,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 +1122,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 +1164,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,
@@ -1290,6 +1397,14 @@
checkWakeLockStateLocked();
}
+ {
+ Mutex::Autolock packageLock(sPackageTargetVersionLock);
+ auto iter = sPackageTargetVersion.find(c->mOpPackageName);
+ if (iter != sPackageTargetVersion.end()) {
+ sPackageTargetVersion.erase(iter);
+ }
+ }
+
SensorDevice& dev(SensorDevice::getInstance());
dev.notifyConnectionDestroyed(c);
}
@@ -1325,12 +1440,26 @@
}
SensorRecord* rec = mActiveSensors.valueFor(handle);
- if (rec == 0) {
+ if (rec == nullptr) {
rec = new SensorRecord(connection);
mActiveSensors.add(handle, rec);
if (sensor->isVirtual()) {
mActiveVirtualSensors.emplace(handle);
}
+
+ // There was no SensorRecord for this sensor which means it was previously disabled. Mark
+ // the recent event as stale to ensure that the previous event is not sent to a client. This
+ // ensures on-change events that were generated during a previous sensor activation are not
+ // erroneously sent to newly connected clients, especially if a second client registers for
+ // an on-change sensor before the first client receives the updated event. Once an updated
+ // event is received, the recent events will be marked as current, and any new clients will
+ // immediately receive the most recent event.
+ if (sensor->getSensor().getReportingMode() == AREPORTING_MODE_ON_CHANGE) {
+ auto logger = mRecentEvent.find(handle);
+ if (logger != mRecentEvent.end()) {
+ logger->second->setLastEventStale();
+ }
+ }
} else {
if (rec->addConnection(connection)) {
// this sensor is already activated, but we are adding a connection that uses it.
@@ -1343,16 +1472,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();
}
@@ -1420,6 +1550,11 @@
if (err == NO_ERROR) {
connection->updateLooperRegistration(mLooper);
+ if (sensor->getSensor().getRequiredPermission().size() > 0 &&
+ sensor->getSensor().getRequiredAppOp() >= 0) {
+ connection->mHandleToAppOp[handle] = sensor->getSensor().getRequiredAppOp();
+ }
+
mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
SensorRegistrationInfo(handle, connection->getPackageName(),
samplingPeriodNs, maxBatchReportLatencyNs, true);
@@ -1534,7 +1669,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;
}
@@ -1544,13 +1679,53 @@
bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation,
const String16& opPackageName) {
- const String8& requiredPermission = sensor.getRequiredPermission();
-
- if (requiredPermission.length() <= 0) {
+ // Check if a permission is required for this sensor
+ if (sensor.getRequiredPermission().length() <= 0) {
return true;
}
+ const int32_t opCode = sensor.getRequiredAppOp();
+ const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
+ IPCThreadState::self()->getCallingUid(), opPackageName);
+ bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
+
+ bool canAccess = false;
+ if (hasPermissionForSensor(sensor)) {
+ // Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
+ if (opCode < 0 || appOpAllowed) {
+ canAccess = true;
+ }
+ } else if (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
+ sensor.getType() == SENSOR_TYPE_STEP_DETECTOR) {
+ int targetSdkVersion = getTargetSdkVersion(opPackageName);
+ // Allow access to the sensor if the application targets pre-Q, which is before the
+ // requirement to hold the AR permission to access Step Counter and Step Detector events
+ // was introduced, and the user hasn't revoked the app op.
+ //
+ // Verifying the app op is required to ensure that the user hasn't revoked the necessary
+ // permissions to access the Step Detector and Step Counter when the application targets
+ // pre-Q. Without this check, if the user revokes the pre-Q install-time GMS Core AR
+ // permission, the app would still be able to receive Step Counter and Step Detector events.
+ if (appOpAllowed &&
+ targetSdkVersion > 0 &&
+ targetSdkVersion <= __ANDROID_API_P__) {
+ canAccess = true;
+ }
+ }
+
+ if (canAccess) {
+ sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
+ } else {
+ ALOGE("%s a sensor (%s) without holding its required permission: %s",
+ operation, sensor.getName().string(), sensor.getRequiredPermission().string());
+ }
+
+ return canAccess;
+}
+
+bool SensorService::hasPermissionForSensor(const Sensor& sensor) {
bool hasPermission = false;
+ const String8& requiredPermission = sensor.getRequiredPermission();
// Runtime permissions can't use the cache as they may change.
if (sensor.isRequiredPermissionRuntime()) {
@@ -1559,25 +1734,31 @@
} else {
hasPermission = PermissionCache::checkCallingPermission(String16(requiredPermission));
}
+ return hasPermission;
+}
- if (!hasPermission) {
- ALOGE("%s a sensor (%s) without holding its required permission: %s",
- operation, sensor.getName().string(), sensor.getRequiredPermission().string());
- return false;
- }
-
- const int32_t opCode = sensor.getRequiredAppOp();
- if (opCode >= 0) {
- AppOpsManager appOps;
- if (appOps.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName)
- != AppOpsManager::MODE_ALLOWED) {
- ALOGE("%s a sensor (%s) without enabled required app op: %d",
- operation, sensor.getName().string(), opCode);
- return false;
+int SensorService::getTargetSdkVersion(const String16& opPackageName) {
+ Mutex::Autolock packageLock(sPackageTargetVersionLock);
+ int targetSdkVersion = -1;
+ auto entry = sPackageTargetVersion.find(opPackageName);
+ if (entry != sPackageTargetVersion.end()) {
+ targetSdkVersion = entry->second;
+ } else {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("package_native"));
+ if (binder != nullptr) {
+ sp<content::pm::IPackageManagerNative> packageManager =
+ interface_cast<content::pm::IPackageManagerNative>(binder);
+ if (packageManager != nullptr) {
+ binder::Status status = packageManager->getTargetSdkVersionForPackage(
+ opPackageName, &targetSdkVersion);
+ if (!status.isOk()) {
+ targetSdkVersion = -1;
+ }
+ }
}
+ sPackageTargetVersion[opPackageName] = targetSdkVersion;
}
-
- return true;
+ return targetSdkVersion;
}
void SensorService::checkWakeLockState() {
@@ -1592,7 +1773,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 +1798,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 +1808,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 +1911,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..e6ec96d 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -20,12 +20,15 @@
#include "SensorList.h"
#include "RecentEventLogger.h"
+#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IUidObserver.h>
#include <cutils/compiler.h>
+#include <cutils/multiuser.h>
#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 +62,6 @@
namespace android {
// ---------------------------------------------------------------------------
class SensorInterface;
-using namespace SensorServiceUtil;
class SensorService :
public BinderService<SensorService>,
@@ -118,6 +120,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 +135,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.
@@ -216,6 +244,8 @@
sensors_event_t const* buffer, const int count);
static bool canAccessSensor(const Sensor& sensor, const char* operation,
const String16& opPackageName);
+ static bool hasPermissionForSensor(const Sensor& sensor);
+ static int getTargetSdkVersion(const String16& opPackageName);
// SensorService acquires a partial wakelock for delivering events from wake up sensors. This
// method checks whether all the events from these wake up sensors have been delivered to the
// corresponding applications, if yes the wakelock is released.
@@ -246,7 +276,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 +304,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 +331,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 +345,11 @@
Vector<SensorRegistrationInfo> mLastNSensorRegistrations;
sp<UidPolicy> mUidPolicy;
+ sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
+
+ static AppOpsManager sAppOpsManager;
+ static std::map<String16, int> sPackageTargetVersion;
+ static Mutex sPackageTargetVersionLock;
};
} // 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/Android.bp b/services/surfaceflinger/Android.bp
index c8b2d91..9aa4e85 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",
+ "libSurfaceFlingerProp",
],
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,22 @@
],
}
+cc_defaults {
+ name: "libsurfaceflinger_production_defaults",
+ defaults: ["libsurfaceflinger_defaults"],
+ cflags: [
+ "-fvisibility=hidden",
+ "-fwhole-program-vtables", // requires ThinLTO
+ ],
+ lto: {
+ thin: true,
+ },
+ // TODO(b/131771163): Fix broken fuzzer support with LTO.
+ sanitize: {
+ fuzzer: false,
+ },
+}
+
cc_library_headers {
name: "libsurfaceflinger_headers",
export_include_dirs: ["."],
@@ -87,82 +118,74 @@
srcs: [
"BufferLayer.cpp",
"BufferLayerConsumer.cpp",
+ "BufferQueueLayer.cpp",
+ "BufferStateLayer.cpp",
+ "ClientCache.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",
+ "RefreshRateOverlay.cpp",
+ "RegionSamplingThread.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",
+ "Scheduler/DispSync.cpp",
+ "Scheduler/DispSyncSource.cpp",
+ "Scheduler/EventControlThread.cpp",
+ "Scheduler/EventThread.cpp",
+ "Scheduler/IdleTimer.cpp",
+ "Scheduler/LayerHistory.cpp",
+ "Scheduler/LayerInfo.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,
- },
- // TODO(b/131771163): Fix broken fuzzer support with LTO.
- sanitize: {
- fuzzer: false,
- },
}
-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",
],
@@ -171,15 +194,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",
],
@@ -188,19 +213,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",
+ "libSurfaceFlingerProp",
+ ],
}
subdirs = [
@@ -208,3 +236,34 @@
"TimeStats/timestatsproto",
"tests",
]
+
+cc_library_shared {
+ name: "libSurfaceFlingerProp",
+ srcs: [
+ "SurfaceFlingerProperties.cpp",
+ ],
+ 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",
+ "liblog",
+ ],
+ static_libs: [
+ "SurfaceFlingerProperties",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.graphics.common@1.2",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ ],
+ export_static_lib_headers: [
+ "SurfaceFlingerProperties",
+ ],
+}
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..f51fbb4 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -20,76 +20,64 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "BufferLayer.h"
-#include "Colorizer.h"
-#include "DisplayDevice.h"
-#include "LayerRejecter.h"
-#include "clz.h"
-#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 <math.h>
-#include <stdlib.h>
+#include <cmath>
+#include <cstdlib>
#include <mutex>
+#include <sstream>
+
+#include "Colorizer.h"
+#include "DisplayDevice.h"
+#include "LayerRejecter.h"
+#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 +85,32 @@
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);
+ bool visible = !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
+ (mActiveBuffer != nullptr || mSidebandStream != nullptr);
+ mFlinger->mScheduler->setLayerVisibility(mSchedulerLayerHandle, visible);
+
+ return visible;
}
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 +131,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 +155,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 +211,145 @@
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::Unsupported) {
+ // If per layer color transform is not supported, we use GPU composition.
+ setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CLIENT);
+ } else 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 +359,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 +380,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 +395,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 +414,42 @@
// 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()) {
+ ATRACE_NAME("!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;
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ 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 +460,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;
}
@@ -595,6 +526,9 @@
}
if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
+ std::stringstream ss;
+ ss << "Dropping sync point " << (*point)->getFrameNumber();
+ ATRACE_NAME(ss.str().c_str());
point = mLocalSyncPoints.erase(point);
} else {
++point;
@@ -602,307 +536,44 @@
}
}
- // 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() {
+ const auto headFrameNumber = getHeadFrameNumber();
+ const bool headFenceSignaled = fenceHasSignaled();
+ const bool presentTimeIsCurrent = framePresentTimeIsCurrent();
+ Mutex::Autolock lock(mLocalSyncPointMutex);
+ for (auto& point : mLocalSyncPoints) {
+ if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled &&
+ presentTimeIsCurrent) {
+ point->setFrameAvailable();
+ sp<Layer> requestedSyncLayer = point->getRequestedSyncLayer();
+ if (requestedSyncLayer) {
+ // Need to update the transaction flag to ensure the layer's pending transaction
+ // gets applied.
+ requestedSyncLayer->setTransactionFlags(eTransactionNeeded);
+ }
}
}
-
- 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 +590,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 +617,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..b679380 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -16,183 +16,181 @@
#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 bool framePresentTimeIsCurrent() 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 ae8ebf0..6709fb4 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,45 @@
}
// 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);
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
+ mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
+ 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;
+ {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ 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 +262,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 +290,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,27 +313,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& currentCrop = getCurrentCropLocked();
- const Rect& cropRect = canUseImageCrop(currentCrop) ? Rect::EMPTY_RECT : currentCrop;
- GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform,
+ GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
+ mCurrentTextureBuffer == nullptr
+ ? nullptr
+ : mCurrentTextureBuffer->graphicBuffer(),
+ getCurrentCropLocked(), mCurrentTransform,
mFilteringEnabled);
}
@@ -480,14 +374,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 {
@@ -522,13 +420,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);
@@ -552,10 +445,11 @@
void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ std::lock_guard<std::mutex> lock(mImagesMutex);
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
- mImages[slotIndex].clear();
+ mImages[slotIndex] = nullptr;
ConsumerBase::freeBufferLocked(slotIndex);
}
@@ -584,6 +478,18 @@
}
}
+void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
+ if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ const std::shared_ptr<Image>& oldImage = mImages[item.mSlot];
+ if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
+ oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
+ mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+ mRE.cacheExternalTextureBuffer(item.mGraphicBuffer);
+ }
+ }
+}
+
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
sp<Layer> l = mLayer.promote();
@@ -594,7 +500,11 @@
void BufferLayerConsumer::abandonLocked() {
BLC_LOGV("abandonLocked");
- mCurrentTextureImage.clear();
+ mCurrentTextureBuffer = nullptr;
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ mImages[i] = nullptr;
+ }
ConsumerBase::abandonLocked();
}
@@ -612,39 +522,10 @@
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 84404c7..e3f6100 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_BUFFERLAYERCONSUMER_H
#define ANDROID_BUFFERLAYERCONSUMER_H
+#include <android-base/thread_annotations.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
#include <gui/HdrMetadata.h>
@@ -37,10 +38,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 +74,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 +92,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 +152,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;
@@ -176,20 +176,21 @@
// setConsumerUsageBits overrides the ConsumerBase method to OR
// DEFAULT_USAGE_FLAGS to usage.
status_t setConsumerUsageBits(uint64_t usage);
+ void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex);
protected:
// abandonLocked overrides the ConsumerBase method to clear
// mCurrentTextureImage in addition to the ConsumerBase behavior.
- virtual void abandonLocked();
+ virtual void abandonLocked() EXCLUDES(mImagesMutex);
// dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
// 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;
+ uint64_t maxFrameNumber = 0) override
+ EXCLUDES(mImagesMutex);
bool canUseImageCrop(const Rect& crop) const;
@@ -208,59 +209,36 @@
// completion of the method will instead be returned to the caller, so that
// it may call releaseBufferLocked itself later.
status_t updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease = nullptr);
+ PendingRelease* pendingRelease = nullptr)
+ EXCLUDES(mImagesMutex);
- // 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);
+ virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
// IConsumerListener interface
void onDisconnect() override;
@@ -274,19 +252,13 @@
// mCurrentTextureImage must not be nullptr.
void computeCurrentTransformMatrixLocked();
- // See getCurrentCrop, but with mMutex already held.
- Rect getCurrentCropLocked() const;
-
// doFenceWaitLocked inserts a wait command into the RenderEngine command
// stream to ensure that it is safe for future RenderEngine commands to
// 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();
+ // getCurrentCropLocked returns the cropping rectangle of the current buffer.
+ Rect getCurrentCropLocked() const;
// The default consumer usage flags that BufferLayerConsumer always sets on its
// BufferQueue instance; these will be OR:d with any additional flags passed
@@ -294,10 +266,10 @@
// 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.
@@ -355,7 +327,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
@@ -367,15 +339,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,
@@ -384,6 +347,14 @@
// 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] GUARDED_BY(mImagesMutex);
+
+ // Separate mutex guarding the shadow buffer cache.
+ // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
+ // which is contentious enough that we can't just use mMutex.
+ mutable std::mutex mImagesMutex;
+
// 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..bd0b55f
--- /dev/null
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -0,0 +1,585 @@
+/*
+ * 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 "BufferQueueLayer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <gui/BufferQueueConsumer.h>
+#include <system/window.h>
+
+#include "BufferQueueLayer.h"
+#include "LayerRejecter.h"
+#include "SurfaceInterceptor.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;
+}
+
+bool BufferQueueLayer::framePresentTimeIsCurrent() const {
+ if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
+ return true;
+ }
+
+ Mutex::Autolock lock(mQueueItemLock);
+ return mQueueItems[0].mTimestamp <= mFlinger->mScheduler->expectedPresentTime();
+}
+
+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);
+ uint64_t frameNumber = mQueueItems[0].mFrameNumber;
+
+ // The head of the queue will be dropped if there are signaled and timely frames behind it
+ nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime();
+
+ if (isRemovedFromCurrentState()) {
+ expectedPresentTime = 0;
+ }
+
+ for (int i = 1; i < mQueueItems.size(); i++) {
+ const bool fenceSignaled =
+ mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ if (!fenceSignaled) {
+ break;
+ }
+
+ // We don't drop frames without explicit timestamps
+ if (mQueueItems[i].mIsAutoTimestamp) {
+ break;
+ }
+
+ const nsecs_t desiredPresent = mQueueItems[i].mTimestamp;
+ if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
+ desiredPresent > expectedPresentTime) {
+ break;
+ }
+
+ frameNumber = mQueueItems[i].mFrameNumber;
+ }
+
+ return frameNumber;
+}
+
+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;
+
+ // INVALID_BUFFER_SLOT is used to identify BufferStateLayers. Default to 0
+ // for BufferQueueLayers
+ int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
+ (*outputLayer->editState().hwc)
+ .hwcBufferCache.getHwcBuffer(slot, 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) {
+ ATRACE_CALL();
+ // Add this buffer from our internal queue tracker
+ { // Autolock scope
+ if (mFlinger->mUseSmart90ForVideo) {
+ const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+ mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
+ item.mHdrMetadata.validTypes != 0);
+ }
+
+ 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();
+ }
+ mConsumer->onBufferAvailable(item);
+}
+
+void BufferQueueLayer::onFrameReplaced(const BufferItem& item) {
+ ATRACE_CALL();
+ { // 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();
+ }
+ mConsumer->onBufferAvailable(item);
+}
+
+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..7def33a
--- /dev/null
+++ b/services/surfaceflinger/BufferQueueLayer.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.
+ */
+
+#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;
+ bool framePresentTimeIsCurrent() 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..05c721f
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -0,0 +1,696 @@
+/*
+ * 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 <gui/BufferQueue.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), mHwcSlotGenerator(new HwcSlotGenerator()) {
+ 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.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.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, nsecs_t postTime,
+ nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) {
+ if (mCurrentState.buffer) {
+ mReleasePreviousBuffer = true;
+ }
+
+ mCurrentState.buffer = buffer;
+ mCurrentState.clientCacheId = clientCacheId;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+
+ mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
+ mDesiredPresentTime = desiredPresentTime;
+
+ if (mFlinger->mUseSmart90ForVideo) {
+ const nsecs_t presentTime = (mDesiredPresentTime == -1) ? 0 : mDesiredPresentTime;
+ mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
+ mCurrentState.hdrMetadata.validTypes != 0);
+ }
+
+ 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.hdrMetadata = hdrMetadata;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+ mCurrentState.surfaceDamageRegion = surfaceDamage;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setApi(int32_t api) {
+ if (mCurrentState.api == api) return false;
+ mCurrentState.api = api;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+ if (mCurrentState.sidebandStream == sidebandStream) return false;
+ 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;
+}
+
+// -----------------------------------------------------------------------
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayer
+// -----------------------------------------------------------------------
+bool BufferStateLayer::fenceHasSignaled() const {
+ if (latchUnsignaledBuffers()) {
+ return true;
+ }
+
+ return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+}
+
+bool BufferStateLayer::framePresentTimeIsCurrent() const {
+ if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
+ return true;
+ }
+
+ return mDesiredPresentTime <= mFlinger->mScheduler->expectedPresentTime();
+}
+
+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);
+
+ mCurrentStateModified = false;
+
+ 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());
+
+ uint32_t hwcSlot;
+ sp<GraphicBuffer> buffer;
+ hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId),
+ 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));
+ }
+
+ mFrameNumber++;
+}
+
+void BufferStateLayer::onFirstRef() {
+ BufferLayer::onFirstRef();
+
+ if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+ updateTransformHint(display);
+ }
+}
+
+void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
+ std::lock_guard lock(mMutex);
+ if (!clientCacheId.isValid()) {
+ ALOGE("invalid process, failed to erase buffer");
+ return;
+ }
+ eraseBufferLocked(clientCacheId);
+}
+
+uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto itr = mCachedBuffers.find(clientCacheId);
+ if (itr == mCachedBuffers.end()) {
+ return addCachedBuffer(clientCacheId);
+ }
+ auto& [hwcCacheSlot, counter] = itr->second;
+ counter = mCounter++;
+ return hwcCacheSlot;
+}
+
+uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
+ REQUIRES(mMutex) {
+ if (!clientCacheId.isValid()) {
+ ALOGE("invalid process, returning invalid slot");
+ return BufferQueue::INVALID_BUFFER_SLOT;
+ }
+
+ ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
+
+ uint32_t hwcCacheSlot = getFreeHwcCacheSlot();
+ mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
+ return hwcCacheSlot;
+}
+
+uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
+ if (mFreeHwcCacheSlots.empty()) {
+ evictLeastRecentlyUsed();
+ }
+
+ uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top();
+ mFreeHwcCacheSlots.pop();
+ return hwcCacheSlot;
+}
+
+void BufferStateLayer::HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
+ uint64_t minCounter = UINT_MAX;
+ client_cache_t minClientCacheId = {};
+ for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
+ const auto& [hwcCacheSlot, counter] = slotCounter;
+ if (counter < minCounter) {
+ minCounter = counter;
+ minClientCacheId = clientCacheId;
+ }
+ }
+ eraseBufferLocked(minClientCacheId);
+
+ ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this);
+}
+
+void BufferStateLayer::HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId)
+ REQUIRES(mMutex) {
+ auto itr = mCachedBuffers.find(clientCacheId);
+ if (itr == mCachedBuffers.end()) {
+ return;
+ }
+ auto& [hwcCacheSlot, counter] = itr->second;
+
+ // TODO send to hwc cache and resources
+
+ mFreeHwcCacheSlots.push(hwcCacheSlot);
+ mCachedBuffers.erase(clientCacheId);
+}
+} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
new file mode 100644
index 0000000..4e2bc45
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -0,0 +1,205 @@
+/*
+ * 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>
+
+#include <stack>
+
+namespace android {
+
+class SlotGenerationTest;
+
+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, nsecs_t postTime, nsecs_t desiredPresentTime,
+ const client_cache_t& clientCacheId) 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;
+
+ // -----------------------------------------------------------------------
+
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayer
+ // -----------------------------------------------------------------------
+ bool fenceHasSignaled() const override;
+ bool framePresentTimeIsCurrent() 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:
+ friend class SlotGenerationTest;
+ 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
+
+ class HwcSlotGenerator : public ClientCache::ErasedRecipient {
+ public:
+ HwcSlotGenerator() {
+ for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ mFreeHwcCacheSlots.push(i);
+ }
+ }
+
+ void bufferErased(const client_cache_t& clientCacheId);
+
+ uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId);
+
+ private:
+ friend class SlotGenerationTest;
+ uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+ uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex);
+ void evictLeastRecentlyUsed() REQUIRES(mMutex);
+ void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+
+ struct CachedBufferHash {
+ std::size_t operator()(const client_cache_t& clientCacheId) const {
+ return std::hash<uint64_t>{}(clientCacheId.id);
+ }
+ };
+
+ std::mutex mMutex;
+
+ std::unordered_map<client_cache_t,
+ std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>,
+ CachedBufferHash>
+ mCachedBuffers GUARDED_BY(mMutex);
+ std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
+
+ // The cache increments this counter value when a slot is updated or used.
+ // Used to track the least recently-used buffer
+ uint64_t mCounter = 0;
+ };
+
+ sp<HwcSlotGenerator> mHwcSlotGenerator;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 0b59147..6bfd302 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,64 +72,38 @@
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,
+ LayerMetadata metadata, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) {
+ // We rely on createLayer to check permissions.
+ return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
+ parentHandle);
}
-
-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)
-{
- sp<Layer> parent = nullptr;
- if (parentHandle != nullptr) {
- auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get());
- parent = layerHandle->owner.promote();
- if (parent == nullptr) {
- return NAME_NOT_FOUND;
+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) {
+ ALOGE("failed to authenticate surface texture");
+ // The extra parent layer check below before returning is to help with debugging
+ // b/134888387. Once the bug is fixed the check can be deleted.
+ if ((static_cast<MonitoredProducer*>(parent.get()))->getLayer() == nullptr) {
+ ALOGE("failed to find parent layer");
}
- }
- 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 BAD_VALUE;
}
- return mFlinger->createLayer(name, this, w, h, format, flags, windowType,
- ownerUid, handle, gbp, &parent);
-}
+ const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("failed to find parent layer");
+ return BAD_VALUE;
+ }
-status_t Client::destroySurface(const sp<IBinder>& handle) {
- return mFlinger->onLayerRemoved(this, handle);
+ return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
+ nullptr, layer);
}
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/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
new file mode 100644
index 0000000..77f2f57
--- /dev/null
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 "ClientCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cinttypes>
+
+#include "ClientCache.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
+
+ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
+
+bool ClientCache::getBuffer(const client_cache_t& cacheId,
+ ClientCacheBuffer** outClientCacheBuffer) {
+ auto& [processToken, id] = cacheId;
+ if (processToken == nullptr) {
+ ALOGE("failed to get buffer, invalid (nullptr) process token");
+ return false;
+ }
+ auto it = mBuffers.find(processToken);
+ if (it == mBuffers.end()) {
+ ALOGE("failed to get buffer, invalid process token");
+ return false;
+ }
+
+ auto& processBuffers = it->second;
+
+ auto bufItr = processBuffers.find(id);
+ if (bufItr == processBuffers.end()) {
+ ALOGE("failed to get buffer, invalid buffer id");
+ return false;
+ }
+
+ ClientCacheBuffer& buf = bufItr->second;
+ *outClientCacheBuffer = &buf;
+ return true;
+}
+
+void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
+ auto& [processToken, id] = cacheId;
+ if (processToken == nullptr) {
+ ALOGE("failed to cache buffer: invalid process token");
+ return;
+ }
+
+ if (!buffer) {
+ ALOGE("failed to cache buffer: invalid buffer");
+ return;
+ }
+
+ std::lock_guard lock(mMutex);
+ sp<IBinder> token;
+
+ // If this is a new process token, set a death recipient. If the client process dies, we will
+ // get a callback through binderDied.
+ auto it = mBuffers.find(processToken);
+ if (it == mBuffers.end()) {
+ token = processToken.promote();
+ if (!token) {
+ ALOGE("failed to cache buffer: invalid token");
+ return;
+ }
+
+ status_t err = token->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("failed to cache buffer: could not link to death");
+ return;
+ }
+ auto [itr, success] =
+ mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>());
+ LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
+ it = itr;
+ }
+
+ auto& processBuffers = it->second;
+
+ if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
+ ALOGE("failed to cache buffer: cache is full");
+ return;
+ }
+
+ processBuffers[id].buffer = buffer;
+}
+
+void ClientCache::erase(const client_cache_t& cacheId) {
+ auto& [processToken, id] = cacheId;
+ std::vector<sp<ErasedRecipient>> pendingErase;
+ {
+ std::lock_guard lock(mMutex);
+ ClientCacheBuffer* buf = nullptr;
+ if (!getBuffer(cacheId, &buf)) {
+ ALOGE("failed to erase buffer, could not retrieve buffer");
+ return;
+ }
+
+ for (auto& recipient : buf->recipients) {
+ sp<ErasedRecipient> erasedRecipient = recipient.promote();
+ if (erasedRecipient) {
+ pendingErase.push_back(erasedRecipient);
+ }
+ }
+
+ mBuffers[processToken].erase(id);
+ }
+
+ for (auto& recipient : pendingErase) {
+ recipient->bufferErased(cacheId);
+ }
+}
+
+sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) {
+ std::lock_guard lock(mMutex);
+
+ ClientCacheBuffer* buf = nullptr;
+ if (!getBuffer(cacheId, &buf)) {
+ ALOGE("failed to get buffer, could not retrieve buffer");
+ return nullptr;
+ }
+
+ return buf->buffer;
+}
+
+void ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
+ const wp<ErasedRecipient>& recipient) {
+ std::lock_guard lock(mMutex);
+
+ ClientCacheBuffer* buf = nullptr;
+ if (!getBuffer(cacheId, &buf)) {
+ ALOGE("failed to register erased recipient, could not retrieve buffer");
+ return;
+ }
+ buf->recipients.insert(recipient);
+}
+
+void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId,
+ const wp<ErasedRecipient>& recipient) {
+ std::lock_guard lock(mMutex);
+
+ ClientCacheBuffer* buf = nullptr;
+ if (!getBuffer(cacheId, &buf)) {
+ ALOGE("failed to unregister erased recipient");
+ return;
+ }
+
+ buf->recipients.erase(recipient);
+}
+
+void ClientCache::removeProcess(const wp<IBinder>& processToken) {
+ std::vector<std::pair<sp<ErasedRecipient>, client_cache_t>> pendingErase;
+ {
+ if (processToken == nullptr) {
+ ALOGE("failed to remove process, invalid (nullptr) process token");
+ return;
+ }
+ std::lock_guard lock(mMutex);
+ auto itr = mBuffers.find(processToken);
+ if (itr == mBuffers.end()) {
+ ALOGE("failed to remove process, could not find process");
+ return;
+ }
+
+ for (auto& [id, clientCacheBuffer] : itr->second) {
+ client_cache_t cacheId = {processToken, id};
+ for (auto& recipient : clientCacheBuffer.recipients) {
+ sp<ErasedRecipient> erasedRecipient = recipient.promote();
+ if (erasedRecipient) {
+ pendingErase.emplace_back(erasedRecipient, cacheId);
+ }
+ }
+ }
+ mBuffers.erase(itr);
+ }
+
+ for (auto& [recipient, cacheId] : pendingErase) {
+ recipient->bufferErased(cacheId);
+ }
+}
+
+void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
+ ClientCache::getInstance().removeProcess(who);
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
new file mode 100644
index 0000000..9f057c4
--- /dev/null
+++ b/services/surfaceflinger/ClientCache.h
@@ -0,0 +1,78 @@
+/*
+ * 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 <gui/LayerState.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <map>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+
+#define BUFFER_CACHE_MAX_SIZE 64
+
+namespace android {
+
+class ClientCache : public Singleton<ClientCache> {
+public:
+ ClientCache();
+
+ void add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+ void erase(const client_cache_t& cacheId);
+
+ sp<GraphicBuffer> get(const client_cache_t& cacheId);
+
+ void removeProcess(const wp<IBinder>& processToken);
+
+ class ErasedRecipient : public virtual RefBase {
+ public:
+ virtual void bufferErased(const client_cache_t& clientCacheId) = 0;
+ };
+
+ void registerErasedRecipient(const client_cache_t& cacheId,
+ const wp<ErasedRecipient>& recipient);
+ void unregisterErasedRecipient(const client_cache_t& cacheId,
+ const wp<ErasedRecipient>& recipient);
+
+private:
+ std::mutex mMutex;
+
+ struct ClientCacheBuffer {
+ sp<GraphicBuffer> buffer;
+ std::set<wp<ErasedRecipient>> recipients;
+ };
+ std::map<wp<IBinder> /*caching process*/,
+ std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>
+ mBuffers GUARDED_BY(mMutex);
+
+ class CacheDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ void binderDied(const wp<IBinder>& who) override;
+ };
+
+ sp<CacheDeathRecipient> mDeathRecipient;
+
+ bool getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer)
+ REQUIRES(mMutex);
+};
+
+}; // namespace android
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..6f076ad
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -0,0 +1,123 @@
+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/NativeWindow.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..e21128c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -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.
+ */
+
+#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;
+
+ // Returns whether the surface is protected.
+ virtual bool isProtected() 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/DisplayHardware/HWComposerBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
similarity index 68%
rename from services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index a008ca9..8eec035 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
-#define ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+#pragma once
-#include <stdint.h>
-
-#include <utils/StrongPointer.h>
-
+#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
@@ -37,25 +37,23 @@
//
// 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 {
+class HwcBufferCache {
public:
- HWComposerBufferCache();
-
- // Given a buffer queue slot and buffer, return the HWC cache slot and
+ 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(int slot, const sp<GraphicBuffer>& buffer,
- uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
+ 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;
+ // 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.
+ wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
};
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSERBUFFERCACHE_H
+} // 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..ab01c20
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+ // 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..0c47eb5
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.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 <math/mat4.h>
+#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};
+
+ // The color transform matrix to apply, corresponding with colorTransform.
+ mat4 colorTransformMat;
+
+ // 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..0f57315
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -0,0 +1,92 @@
+/*
+ * 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;
+ bool isProtected() const override { return mProtected; }
+
+ 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;
+ bool mProtected{false};
+ 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/NativeWindow.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/NativeWindow.h
new file mode 100644
index 0000000..714d2f7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/NativeWindow.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 <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+
+namespace android::compositionengine::mock {
+
+/* ------------------------------------------------------------------------
+ * Mock NativeWindow
+ *
+ * An intentionally simplified Mock which implements a minimal subset of the full
+ * ANativeWindow interface.
+ */
+class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow, RefBase> {
+public:
+ NativeWindow();
+ ~NativeWindow();
+
+ 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(disconnect, 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));
+};
+
+} // 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..ca2299a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -0,0 +1,50 @@
+/*
+ * 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(isProtected, bool());
+ 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/CompositionEngine/mock/NativeWindow.cpp b/services/surfaceflinger/CompositionEngine/mock/NativeWindow.cpp
new file mode 100644
index 0000000..d0cc8ef
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/NativeWindow.cpp
@@ -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.
+ */
+
+#include "compositionengine/mock/NativeWindow.h"
+#include <log/log.h>
+
+namespace android::compositionengine::mock {
+
+static int forwardSetSwapInterval(ANativeWindow* window, int interval) {
+ return static_cast<NativeWindow*>(window)->setSwapInterval(interval);
+}
+
+static int forwardDequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {
+ return static_cast<NativeWindow*>(window)->dequeueBuffer(buffer, fenceFd);
+}
+
+static int forwardCancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return static_cast<NativeWindow*>(window)->cancelBuffer(buffer, fenceFd);
+}
+
+static int forwardQueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return static_cast<NativeWindow*>(window)->queueBuffer(buffer, fenceFd);
+}
+
+static int forwardQuery(const ANativeWindow* window, int what, int* value) {
+ return static_cast<const NativeWindow*>(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 = static_cast<NativeWindow*>(window)->connect(api);
+ break;
+ }
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+ PixelFormat format = va_arg(args, PixelFormat);
+ result = static_cast<NativeWindow*>(window)->setBuffersFormat(format);
+ break;
+ }
+ case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+ ui::Dataspace dataspace = static_cast<ui::Dataspace>(va_arg(args, int));
+ result = static_cast<NativeWindow*>(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 = static_cast<NativeWindow*>(window)->setUsage(usage);
+ break;
+ }
+ case NATIVE_WINDOW_SET_USAGE64: {
+ uint64_t usage = va_arg(args, uint64_t);
+ result = static_cast<NativeWindow*>(window)->setUsage(usage);
+ break;
+ }
+ case NATIVE_WINDOW_API_DISCONNECT: {
+ int api = va_arg(args, int);
+ result = static_cast<NativeWindow*>(window)->disconnect(api);
+ 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 static_cast<NativeWindow*>(window)->dequeueBuffer(buffer, &ignoredFenceFd);
+}
+
+static int forwardCancelBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+ return static_cast<NativeWindow*>(window)->cancelBuffer(buffer, -1);
+}
+
+static int forwardLockBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+ return static_cast<NativeWindow*>(window)->lockBuffer_DEPRECATED(buffer);
+}
+
+static int forwardQueueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+ return static_cast<NativeWindow*>(window)->queueBuffer(buffer, -1);
+}
+
+NativeWindow::NativeWindow() {
+ 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;
+}
+NativeWindow::~NativeWindow() = default;
+
+} // 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..f72862b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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), wp<GraphicBuffer>(nullptr));
+}
+
+void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer) {
+ // default is 0
+ if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
+ slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+ *outSlot = 0;
+ } else {
+ *outSlot = slot;
+ }
+
+ auto& currentBuffer = mBuffers[*outSlot];
+ wp<GraphicBuffer> weakCopy(buffer);
+ if (currentBuffer == weakCopy) {
+ // already cached in HWC, skip sending the buffer
+ *outBuffer = nullptr;
+ } else {
+ *outBuffer = buffer;
+
+ // update cache
+ currentBuffer = buffer;
+ }
+}
+
+} // 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..01b5781
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -0,0 +1,244 @@
+/*
+ * 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) {
+ if (mState.colorTransformMat == transform) {
+ return;
+ }
+
+ const bool isIdentity = (transform == mat4());
+ const auto newColorTransform =
+ isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+
+ mState.colorTransform = newColorTransform;
+ mState.colorTransformMat = transform;
+
+ 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..3fcd9d1
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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) {
+ LOG_ALWAYS_FATAL_IF(!mNativeWindow);
+}
+
+RenderSurface::~RenderSurface() {
+ ANativeWindow* const window = mNativeWindow.get();
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+}
+
+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);
+ if (status == NO_ERROR) {
+ mProtected = useProtected;
+ }
+}
+
+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..33444a5
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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/NativeWindow.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.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;
+ sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+ 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_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
+ EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
+ mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, 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..00eafb1
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ sp<GraphicBuffer>* outBuffer) {
+ HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
+ }
+};
+
+class HwcBufferCacheTest : public testing::Test {
+public:
+ ~HwcBufferCacheTest() override = default;
+
+ void testSlot(const int inSlot, const uint32_t expectedSlot) {
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // The first time, the output is the same as the input
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ // The second time with the same buffer, the outBuffer is nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // With a new buffer, the outBuffer is the input.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
+
+ // Again, the second request with the same buffer sets outBuffer to nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, 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(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+ }
+
+ impl::HwcBufferCache 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, cacheWorksForSlotZero) {
+ testSlot(0, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
+ testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+}
+
+TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
+ testSlot(-123, 0);
+}
+
+} // 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..fee0c11
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -0,0 +1,381 @@
+/*
+ * 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);
+ EXPECT_EQ(identity, mOutput.getState().colorTransformMat);
+
+ // 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 nonIdentityHalf = mat4() * 0.5;
+
+ mOutput.setColorTransform(nonIdentityHalf);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+ EXPECT_EQ(nonIdentityHalf, mOutput.getState().colorTransformMat);
+
+ // Since this is a state change, the entire output should now be dirty.
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+
+ // Non-identity matrix sets a non-identity state value
+ const mat4 nonIdentityQuarter = mat4() * 0.25;
+
+ mOutput.setColorTransform(nonIdentityQuarter);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+ EXPECT_EQ(nonIdentityQuarter, mOutput.getState().colorTransformMat);
+
+ // 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..f75a4dc
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -0,0 +1,393 @@
+/*
+ * 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/NativeWindow.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+/* ------------------------------------------------------------------------
+ * 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));
+ EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
+ .WillRepeatedly(Return(NO_ERROR));
+ }
+ ~RenderSurfaceTest() override = default;
+
+ StrictMock<android::mock::HWComposer> mHwComposer;
+ StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ StrictMock<mock::Display> mDisplay;
+ sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+ 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_FALSE(mSurface.isProtected());
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ .WillOnce(Return(NO_ERROR));
+
+ mSurface.setProtected(true);
+ EXPECT_TRUE(mSurface.isProtected());
+}
+
+TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
+ EXPECT_FALSE(mSurface.isProtected());
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+ mSurface.setProtected(false);
+ EXPECT_FALSE(mSurface.isProtected());
+}
+
+TEST_F(RenderSurfaceTest, setProtectedEnableAndDisable) {
+ EXPECT_FALSE(mSurface.isProtected());
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+ mSurface.setProtected(true);
+ EXPECT_TRUE(mSurface.isProtected());
+ mSurface.setProtected(false);
+ EXPECT_FALSE(mSurface.isProtected());
+}
+
+TEST_F(RenderSurfaceTest, setProtectedEnableWithError) {
+ EXPECT_FALSE(mSurface.isProtected());
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ .WillOnce(Return(INVALID_OPERATION));
+ mSurface.setProtected(true);
+ EXPECT_FALSE(mSurface.isProtected());
+}
+
+/* ------------------------------------------------------------------------
+ * 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 725ae35..0067b50 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,30 +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,
- bool allowSecureLayers = true)
- : 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),
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 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
@@ -370,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();
@@ -383,10 +294,10 @@
}
// Recompute the device transformation for the source crop.
- Transform rotation;
- Transform translatePhysical;
- Transform translateLogical;
- Transform scale;
+ ui::Transform rotation;
+ ui::Transform translatePhysical;
+ ui::Transform translateLogical;
+ ui::Transform scale;
const Rect& viewport = mDevice->getViewport();
const Rect& scissor = mDevice->getScissor();
const Rect& frame = mDevice->getFrame();
@@ -396,13 +307,13 @@
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;
default:
break;
@@ -412,7 +323,7 @@
translatePhysical.set(scissor.left, scissor.top);
scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
frame.getHeight() / float(viewport.getHeight()));
- const Transform finalTransform =
+ const ui::Transform finalTransform =
rotation * translatePhysical * scale * translateLogical;
return finalTransform.transform(mSourceCrop);
}
@@ -420,8 +331,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;
}
@@ -432,27 +343,27 @@
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));
}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index f190b71..cc5a5b5 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,155 @@
}
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::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..c4e952b 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,29 @@
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 setDisplayBrightness(Display display, float brightness) = 0;
};
namespace impl {
@@ -374,12 +393,30 @@
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 setDisplayBrightness(Display display, float brightness) override;
+
private:
class CommandWriter : public CommandWriterBase {
public:
@@ -405,7 +442,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..7370b0c 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(mCurrentBufferSlot, 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(mCurrentBufferSlot, 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..c463c4e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -26,7 +26,6 @@
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
-#include <ui/Region.h>
#include <android/configuration.h>
@@ -124,6 +123,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 +142,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 +181,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,12 +223,40 @@
}
// 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),
@@ -250,43 +283,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 +301,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 +367,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 +403,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 +424,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 +469,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 +505,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 +529,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 +560,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 +618,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);
}
@@ -638,6 +644,29 @@
{
auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode);
auto intError = mComposer.setPowerMode(mId, intMode);
+
+ if (mode == PowerMode::On) {
+ std::call_once(mDisplayCapabilityQueryFlag, [this]() {
+ 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 (mCapabilities.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);
+ }
+ }
+ });
+ }
+
return static_cast<Error>(intError);
}
@@ -687,6 +716,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 +783,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 +816,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)
@@ -797,6 +827,11 @@
Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer,
const sp<Fence>& acquireFence)
{
+ if (buffer == nullptr && mBufferSlot == slot) {
+ return Error::None;
+ }
+ mBufferSlot = slot;
+
int32_t fenceFd = acquireFence->dup();
auto intError = mComposer.setLayerBuffer(mDisplayId, mId, slot, buffer,
fenceFd);
@@ -805,6 +840,12 @@
Error Layer::setSurfaceDamage(const Region& damage)
{
+ if (damage.isRect() && mDamageRegion.isRect() &&
+ (damage.getBounds() == mDamageRegion.getBounds())) {
+ return Error::None;
+ }
+ mDamageRegion = damage;
+
// We encode default full-screen damage as INVALID_RECT upstream, but as 0
// rects for HWC
Hwc2::Error intError = Hwc2::Error::NONE;
@@ -872,37 +913,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)
@@ -947,6 +1000,12 @@
Error Layer::setVisibleRegion(const Region& region)
{
+ if (region.isRect() && mVisibleRegion.isRect() &&
+ (region.getBounds() == mVisibleRegion.getBounds())) {
+ return Error::None;
+ }
+ mVisibleRegion = region;
+
size_t rectCount = 0;
auto rectArray = region.getArray(&rectCount);
@@ -972,4 +1031,19 @@
return static_cast<Error>(intError);
}
+// Composer HAL 2.3
+Error Layer::setColorTransform(const android::mat4& matrix) {
+ if (matrix == mColorMatrix) {
+ return Error::None;
+ }
+ auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray());
+ Error error = static_cast<Error>(intError);
+ if (error != Error::None) {
+ return error;
+ }
+ mColorMatrix = matrix;
+ return error;
+}
+
+} // namespace impl
} // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e423167..b7cdf7f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -27,6 +27,7 @@
#include <math/mat4.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
+#include <ui/Region.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
@@ -37,14 +38,11 @@
#include <unordered_set>
#include <vector>
-#include "PowerAdvisor.h"
-
namespace android {
+ struct DisplayedFrameStats;
class Fence;
class FloatRect;
class GraphicBuffer;
- class Rect;
- class Region;
namespace Hwc2 {
class Composer;
}
@@ -95,6 +93,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 +122,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 +198,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 +349,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 +356,78 @@
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::once_flag mDisplayCapabilityQueryFlag;
+ 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
@@ -349,11 +438,19 @@
hwc2_display_t mDisplayId;
hwc2_layer_t mId;
+
+ // Cached HWC2 data, to ensure the same commands aren't sent to the HWC
+ // multiple times.
+ android::Region mVisibleRegion = android::Region::INVALID_REGION;
+ android::Region mDamageRegion = android::Region::INVALID_REGION;
android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN;
android::HdrMetadata mHdrMetadata;
- std::function<void(Layer*)> mLayerDestroyedListener;
+ android::mat4 mColorMatrix;
+ uint32_t mBufferSlot;
};
+} // 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/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..4e0e4df 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(mFbProducerSlot, 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..1318bc0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -19,120 +19,110 @@
#define LOG_TAG "Layer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <math.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <algorithm>
+#include "Layer.h"
+#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 <math.h>
+#include <renderengine/RenderEngine.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.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 <algorithm>
+#include <mutex>
+#include <sstream>
#include "BufferLayer.h"
+#include "ColorLayer.h"
#include "Colorizer.h"
#include "DisplayDevice.h"
-#include "Layer.h"
+#include "DisplayHardware/HWComposer.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),
+ mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
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);
+
+ mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType);
+
+ mFlinger->onLayerCreated();
}
Layer::~Layer() {
@@ -141,13 +131,8 @@
c->detachLayer(this);
}
- for (auto& point : mRemoteSyncPoints) {
- point->setTransactionApplied();
- }
- for (auto& point : mLocalSyncPoints) {
- point->setFrameAvailable();
- }
mFrameTracker.logAndResetStats(mName);
+ mFlinger->onLayerDestroyed(this);
}
// ---------------------------------------------------------------------------
@@ -161,33 +146,73 @@
*/
void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
-void Layer::onRemovedFromCurrentState() {
- // 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;
+void Layer::removeRemoteSyncPoints() {
+ for (auto& point : mRemoteSyncPoints) {
+ point->setTransactionApplied();
}
+ mRemoteSyncPoints.clear();
- for (const auto& child : mCurrentChildren) {
- child->onRemovedFromCurrentState();
+ {
+ Mutex::Autolock pendingStateLock(mPendingStateMutex);
+ for (State pendingState : mPendingStates) {
+ pendingState.barrierLayer_legacy = nullptr;
+ }
}
}
-void Layer::onRemoved() {
- // the layer is removed from SF mLayersPendingRemoval
- abandon();
+void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
+ if (mCurrentState.zOrderRelativeOf == nullptr) {
+ return;
+ }
- destroyAllHwcLayers();
+ sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote();
+ if (strongRelative == nullptr) {
+ setZOrderRelativeOf(nullptr);
+ return;
+ }
+
+ if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) {
+ strongRelative->removeZOrderRelative(this);
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ setZOrderRelativeOf(nullptr);
+ }
+}
+
+void Layer::removeFromCurrentState() {
+ mRemovedFromCurrentState = true;
+
+ // 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.
+ removeRemoteSyncPoints();
+
+ {
+ Mutex::Autolock syncLock(mLocalSyncPointMutex);
+ for (auto& point : mLocalSyncPoints) {
+ point->setFrameAvailable();
+ }
+ mLocalSyncPoints.clear();
+ }
+
+ mFlinger->markLayerPendingRemovalLocked(this);
+}
+
+void Layer::onRemovedFromCurrentState() {
+ auto layersInTree = getLayersInTree(LayerVector::StateSet::Current);
+ std::sort(layersInTree.begin(), layersInTree.end());
+ for (const auto& layer : layersInTree) {
+ layer->removeFromCurrentState();
+ layer->removeRelativeZ(layersInTree);
+ }
+}
+
+void Layer::addToCurrentState() {
+ mRemovedFromCurrentState = false;
for (const auto& child : mCurrentChildren) {
- child->onRemoved();
+ child->addToCurrentState();
}
}
@@ -205,6 +230,11 @@
sp<IBinder> Layer::getHandle() {
Mutex::Autolock _l(mLock);
+ if (mGetHandleCalled) {
+ ALOGE("Get handle called twice" );
+ return nullptr;
+ }
+ mGetHandleCalled = true;
return new Handle(mFlinger, this);
}
@@ -212,44 +242,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 +263,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 +291,217 @@
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;
-
- 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 +513,91 @@
// 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);
-}
-
-void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
- float alpha) const {
- auto& engine(mFlinger->getRenderEngine());
- computeGeometry(renderArea, getBE().mMesh, false);
- engine.setupFillWithColor(red, green, blue, alpha);
- engine.drawMesh(getBE().mMesh);
-}
-
-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;
+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;
}
- 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) {
+
+ 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::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 +606,9 @@
// relevant frame
return false;
}
+ if (isRemovedFromCurrentState()) {
+ return false;
+ }
Mutex::Autolock lock(mLocalSyncPointMutex);
mLocalSyncPoints.push_back(point);
@@ -796,34 +619,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 +638,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);
}
@@ -882,25 +679,31 @@
if (!mCurrentState.modified) {
return;
}
+ ATRACE_CALL();
// 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, this);
if (barrierLayer->addSyncPoint(syncPoint)) {
+ std::stringstream ss;
+ ss << "Adding sync point " << mCurrentState.frameNumber_legacy;
+ ATRACE_NAME(ss.str().c_str());
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;
}
}
@@ -913,6 +716,7 @@
}
void Layer::popPendingState(State* stateToCommit) {
+ ATRACE_CALL();
*stateToCommit = mPendingStates[0];
mPendingStates.removeAt(0);
@@ -922,7 +726,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 +737,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
@@ -943,6 +748,7 @@
}
if (mRemoteSyncPoints.front()->frameIsAvailable()) {
+ ATRACE_NAME("frameIsAvailable");
// Apply the state update
popPendingState(stateToCommit);
stateUpdateAvailable = true;
@@ -951,6 +757,7 @@
mRemoteSyncPoints.front()->setTransactionApplied();
mRemoteSyncPoints.pop_front();
} else {
+ ATRACE_NAME("!frameIsAvailable");
break;
}
} else {
@@ -970,18 +777,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 +791,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 +820,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 +834,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 +846,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 +896,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 +916,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 +986,7 @@
if (strongRelative != nullptr) {
strongRelative->removeZOrderRelative(this);
}
- mCurrentState.zOrderRelativeOf = nullptr;
+ setZOrderRelativeOf(nullptr);
}
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1180,6 +1006,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 +1024,7 @@
}
if (mCurrentState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
- mCurrentState.zOrderRelativeOf == relative) {
+ mCurrentState.zOrderRelativeOf == relative) {
return false;
}
@@ -1203,7 +1036,7 @@
if (oldZOrderRelativeOf != nullptr) {
oldZOrderRelativeOf->removeZOrderRelative(this);
}
- mCurrentState.zOrderRelativeOf = relative;
+ setZOrderRelativeOf(relative);
relative->addZOrderRelative(this);
setTransactionFlags(eTransactionNeeded);
@@ -1212,11 +1045,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 +1066,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 +1115,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 +1123,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 +1146,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 +1167,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 +1184,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,34 +1203,42 @@
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) {
+ ATRACE_CALL();
+ 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;
}
+ if (usingRelativeZ(LayerVector::StateSet::Drawing)) {
+ auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
+ if (zOrderRelativeOf != nullptr) {
+ if (zOrderRelativeOf->isHiddenByPolicy()) {
+ return true;
+ }
+ }
+ }
return s.flags & layer_state_t::eLayerHidden;
}
@@ -1376,15 +1255,16 @@
return usage;
}
-void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const {
+void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const {
uint32_t orientation = 0;
+ // Disable setting transform hint if the debug flag is set.
if (!mFlinger->mDebugDisableTransformHint) {
// The transform hint is used to improve performance, but we can
// only have a single transform hint, it cannot
// apply to all displays.
- const 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 +1275,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 +1322,67 @@
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(" Window Type | ");
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, " %10d | ", mWindowType);
+ 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 +1398,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 +1408,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 +1444,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 +1472,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 +1486,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,14 +1550,62 @@
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();
+ child->removeRemoteSyncPoints();
}
}
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,23 +1616,11 @@
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;
}
-bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) {
+bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const State& state = useDrawing ? mDrawingState : mCurrentState;
return state.zOrderRelativeOf != nullptr;
@@ -1843,35 +1804,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 +1820,28 @@
return half4(color.r, color.g, color.b, getAlpha());
}
+Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ RoundedCornerState parentState = p->getRoundedCornerState();
+ 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 && getCrop(getDrawingState()).isValid()
+ ? RoundedCornerState(getCrop(getDrawingState()).toFloatRect(), radius)
+ : RoundedCornerState();
+}
+
void Layer::commitChildList() {
for (size_t i = 0; i < mCurrentChildren.size(); i++) {
const auto& child = mCurrentChildren[i];
@@ -1895,116 +1851,178 @@
mDrawingParent = mCurrentParent;
}
-void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
+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,
+ uint32_t traceFlags) {
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());
- layerInfo->set_type(String8(getTypeId()));
+ if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
+ layerInfo->set_id(sequence);
+ layerInfo->set_name(getName().c_str());
+ layerInfo->set_type(String8(getTypeId()));
- for (const auto& child : children) {
- layerInfo->add_children(child->sequence);
- }
-
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- layerInfo->add_relatives(strongRelative->sequence);
+ for (const auto& child : children) {
+ layerInfo->add_children(child->sequence);
}
- }
- LayerProtoHelper::writeToProto(state.activeTransparentRegion,
- layerInfo->mutable_transparent_region());
- LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region());
- LayerProtoHelper::writeToProto(surfaceDamageRegion, 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());
-
- PositionProto* requestedPosition = layerInfo->mutable_requested_position();
- requestedPosition->set_x(requestedTransform.tx());
- requestedPosition->set_y(requestedTransform.ty());
-
- SizeProto* size = layerInfo->mutable_size();
- size->set_w(state.active.w);
- size->set_h(state.active.h);
-
- LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop());
- LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop());
-
- layerInfo->set_is_opaque(isOpaque(state));
- layerInfo->set_invalidate(contentDirty);
-
- // XXX (b/79210409) mCurrentDataSpace is not protected
- 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());
- layerInfo->set_flags(state.flags);
-
- LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
- LayerProtoHelper::writeToProto(requestedTransform, layerInfo->mutable_requested_transform());
-
- auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent != nullptr) {
- layerInfo->set_parent(parent->sequence);
- }
-
- auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
- }
-
- // XXX getBE().compositionInfo.mBuffer is not protected
- auto buffer = getBE().compositionInfo.mBuffer;
- if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer());
- }
-
- 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);
-
- for (const auto& pendingState : mPendingStates) {
- auto barrierLayer = pendingState.barrierLayer.promote();
- if (barrierLayer != nullptr) {
- BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
- barrierLayerProto->set_id(barrierLayer->sequence);
- barrierLayerProto->set_frame_number(pendingState.frameNumber);
+ for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+ sp<Layer> strongRelative = weakRelative.promote();
+ if (strongRelative != nullptr) {
+ layerInfo->add_relatives(strongRelative->sequence);
+ }
}
+
+ 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);
+
+ LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+ [&]() { return layerInfo->mutable_position(); });
+
+ LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(),
+ [&]() {
+ return layerInfo->mutable_requested_position();
+ });
+
+ LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+ [&]() { return layerInfo->mutable_size(); });
+
+ 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);
+ layerInfo->set_is_protected(isProtected());
+
+ // XXX (b/79210409) mCurrentDataSpace is not protected
+ layerInfo->set_dataspace(
+ dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace)));
+
+ layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
+ 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());
+ LayerProtoHelper::writeToProto(requestedTransform,
+ layerInfo->mutable_requested_transform());
+
+ 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);
+ }
+
+ auto buffer = mActiveBuffer;
+ if (buffer != nullptr) {
+ 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_curr_frame(mCurrentFrameNumber);
+ layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
+
+ for (const auto& pendingState : mPendingStates) {
+ 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_legacy);
+ }
+ }
+ LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
+ }
+
+ if (traceFlags & SurfaceTracing::TRACE_INPUT) {
+ LayerProtoHelper::writeToProto(state.inputInfo, state.touchableRegionCrop,
+ [&]() { return layerInfo->mutable_input_window_info(); });
+ }
+
+ if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
+ 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(); });
}
}
-void Layer::writeToProto(LayerProto* layerInfo, int32_t hwcId) {
- if (!hasHwcLayer(hwcId)) {
+void Layer::writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice,
+ uint32_t traceFlags) {
+ auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ if (!outputLayer) {
return;
}
- writeToProto(layerInfo, LayerVector::StateSet::Drawing);
- const auto& hwcInfo = getBE().mHwcLayers.at(hwcId);
+ writeToProto(layerInfo, LayerVector::StateSet::Drawing, traceFlags);
- const Rect& frame = hwcInfo.displayFrame;
- LayerProtoHelper::writeToProto(frame, layerInfo->mutable_hwc_frame());
+ const auto& compositionState = outputLayer->getState();
- const FloatRect& crop = hwcInfo.sourceCrop;
- LayerProtoHelper::writeToProto(crop, layerInfo->mutable_hwc_crop());
+ const Rect& frame = compositionState.displayFrame;
+ LayerProtoHelper::writeToProto(frame, [&]() { return layerInfo->mutable_hwc_frame(); });
- const int32_t transform = static_cast<int32_t>(hwcInfo.transform);
+ const FloatRect& crop = compositionState.sourceCrop;
+ LayerProtoHelper::writeToProto(crop, [&]() { return layerInfo->mutable_hwc_crop(); });
+
+ 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 +2033,83 @@
}
}
+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();
+ float xSurfaceInset = info.surfaceInset;
+ float ySurfaceInset = info.surfaceInset;
+ if (xScale != 1.0f || yScale != 1.0f) {
+ info.windowXScale *= 1.0f / xScale;
+ info.windowYScale *= 1.0f / yScale;
+ info.touchableRegion.scaleSelf(xScale, yScale);
+ xSurfaceInset *= xScale;
+ ySurfaceInset *= yScale;
+ }
+
+ // Transform layer size to screen space and inset it by surface insets.
+ // If this is a portal window, set the touchableRegion to the layerBounds.
+ Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
+ ? getBufferSize(getDrawingState())
+ : info.touchableRegion.getBounds();
+ if (!layerBounds.isValid()) {
+ layerBounds = getCroppedBufferSize(getDrawingState());
+ }
+ layerBounds = t.transform(layerBounds);
+ layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset);
+
+ // 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..8a80e15 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,51 @@
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;
+ client_cache_t clientCacheId;
+ 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 +253,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 +268,67 @@
// 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*/, nsecs_t /*postTime*/,
+ nsecs_t /*desiredPresentTime*/,
+ const client_cache_t& /*clientCacheId*/) {
+ 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 +339,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 +411,104 @@
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 isRemovedFromCurrentState() const;
- bool isPendingRemoval() const { return mPendingRemoval; }
+ void writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet,
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
- void writeToProto(LayerProto* layerInfo,
- LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing);
+ void writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice,
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
- void writeToProto(LayerProto* layerInfo, int32_t hwcId);
+ 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; }
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 +517,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 +564,23 @@
* 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; }
+ /*
+ * Remove relative z for the layer if its relative parent is not part of the
+ * provided layer tree.
+ */
+ void removeRelativeZ(const std::vector<Layer*>& layersInTree);
+
+ /*
+ * Remove from current state and mark for removal.
+ */
+ void removeFromCurrentState();
+
/*
* 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 +588,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,34 +603,16 @@
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;
- }
-
- // -----------------------------------------------------------------------
-
- void clearWithOpenGL(const RenderArea& renderArea) const;
+ bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice);
+ HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice);
inline const State& getDrawingState() const { return mDrawingState; }
inline const State& getCurrentState() const { return mCurrentState; }
@@ -538,10 +621,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 +639,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 +647,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 +672,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 +681,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 +710,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,31 +725,30 @@
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;
-
- // drawing
- void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b,
- float alpha) 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;
void setParent(const sp<Layer>& layer);
-
LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
void addZOrderRelative(const wp<Layer>& relative);
void removeZOrderRelative(const wp<Layer>& relative);
class SyncPoint {
public:
- explicit SyncPoint(uint64_t frameNumber)
- : mFrameNumber(frameNumber), mFrameIsAvailable(false), mTransactionIsApplied(false) {}
+ explicit SyncPoint(uint64_t frameNumber, wp<Layer> requestedSyncLayer)
+ : mFrameNumber(frameNumber),
+ mFrameIsAvailable(false),
+ mTransactionIsApplied(false),
+ mRequestedSyncLayer(requestedSyncLayer) {}
uint64_t getFrameNumber() const { return mFrameNumber; }
@@ -653,10 +760,13 @@
void setTransactionApplied() { mTransactionIsApplied = true; }
+ sp<Layer> getRequestedSyncLayer() { return mRequestedSyncLayer.promote(); }
+
private:
const uint64_t mFrameNumber;
std::atomic<bool> mFrameIsAvailable;
std::atomic<bool> mTransactionIsApplied;
+ wp<Layer> mRequestedSyncLayer;
};
// SyncPoints which will be signaled when the correct frame is at the head
@@ -673,9 +783,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
@@ -699,17 +808,25 @@
wp<Layer> owner;
};
+ // Creates a new handle each time, so we only expect
+ // this to be called once.
sp<IBinder> getHandle();
const String8& getName() const;
virtual void notifyAvailableFrames() {}
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 usingRelativeZ(LayerVector::StateSet stateSet) const;
- bool mPremultipliedAlpha;
+ bool mPremultipliedAlpha{true};
String8 mName;
String8 mTransactionName; // A cached version of "TX - " + mName for systraces
@@ -718,16 +835,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 +851,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 +881,28 @@
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};
+
+ // Window types from WindowManager.LayoutParams
+ const int mWindowType;
+
+ // This is populated if the layer is registered with Scheduler for tracking purposes.
+ std::unique_ptr<scheduler::LayerHistory::LayerHandle> mSchedulerLayerHandle;
private:
/**
@@ -801,10 +919,45 @@
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;
+
+ // 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);
+
+ bool mGetHandleCalled = false;
+
+ void removeRemoteSyncPoints();
};
-// ---------------------------------------------------------------------------
+} // 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..c94e439 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -18,52 +18,141 @@
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);
+ }
+}
+
+void LayerProtoHelper::writeToProto(
+ const InputWindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
+ std::function<InputWindowInfoProto*()> getInputWindowInfoProto) {
+ if (inputInfo.token == nullptr) {
+ return;
+ }
+
+ InputWindowInfoProto* proto = getInputWindowInfoProto();
+ proto->set_layout_params_flags(inputInfo.layoutParamsFlags);
+ proto->set_layout_params_type(inputInfo.layoutParamsType);
+
+ LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
+ inputInfo.frameBottom},
+ [&]() { return proto->mutable_frame(); });
+ LayerProtoHelper::writeToProto(inputInfo.touchableRegion,
+ [&]() { return proto->mutable_touchable_region(); });
+
+ proto->set_surface_inset(inputInfo.surfaceInset);
+ proto->set_visible(inputInfo.visible);
+ proto->set_can_receive_keys(inputInfo.canReceiveKeys);
+ proto->set_has_focus(inputInfo.hasFocus);
+ proto->set_has_wallpaper(inputInfo.hasWallpaper);
+
+ proto->set_global_scale_factor(inputInfo.globalScaleFactor);
+ proto->set_window_x_scale(inputInfo.windowXScale);
+ proto->set_window_y_scale(inputInfo.windowYScale);
+ proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
+ auto cropLayer = touchableRegionBounds.promote();
+ if (cropLayer != nullptr) {
+ proto->set_crop_layer_id(cropLayer->sequence);
+ LayerProtoHelper::writeToProto(cropLayer->getScreenBounds(
+ false /* reduceTransparentRegion */),
+ [&]() { return proto->mutable_touchable_region_crop(); });
+ }
}
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 860da63..1754a3f 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -16,24 +16,33 @@
#include <layerproto/LayerProtoHeader.h>
+#include <Layer.h>
+#include <input/InputWindow.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);
+ static void writeToProto(const InputWindowInfo& inputInfo,
+ const wp<Layer>& touchableRegionBounds,
+ std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
};
} // 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..c60421b 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 {
@@ -131,6 +132,10 @@
return mProducer->setDequeueTimeout(timeout);
}
+status_t MonitoredProducer::setLegacyBufferDrop(bool drop) {
+ return mProducer->setLegacyBufferDrop(drop);
+}
+
status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) {
return mProducer->getLastQueuedBuffer(outBuffer, outFence,
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 1246d14..d346f82 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -61,6 +61,7 @@
virtual status_t setGenerationNumber(uint32_t generationNumber);
virtual String8 getConsumerName() const override;
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
+ virtual status_t setLegacyBufferDrop(bool drop) override;
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override;
virtual IBinder* onAsBinder();
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/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
new file mode 100644
index 0000000..5b4bec9
--- /dev/null
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -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.
+ */
+
+#include "RefreshRateOverlay.h"
+#include "Client.h"
+#include "Layer.h"
+
+namespace android {
+
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
+ : mFlinger(flinger), mClient(new Client(&mFlinger)) {
+ createLayer();
+}
+
+bool RefreshRateOverlay::createLayer() {
+ const status_t ret =
+ mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor,
+ LayerMetadata(), &mIBinder, &mGbp, nullptr);
+ if (ret) {
+ ALOGE("failed to create color layer");
+ return false;
+ }
+
+ Mutex::Autolock _l(mFlinger.mStateLock);
+ mLayer = mClient->getLayerUser(mIBinder);
+ mLayer->setCrop_legacy(Rect(50, 70, 200, 100), true);
+
+ // setting Layer's Z requires resorting layersSortedByZ
+ ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
+ if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) {
+ mFlinger.mCurrentState.layersSortedByZ.removeAt(idx);
+ mFlinger.mCurrentState.layersSortedByZ.add(mLayer);
+ }
+
+ return true;
+}
+
+void RefreshRateOverlay::changeRefreshRate(RefreshRateType type) {
+ const half3& color = (type == RefreshRateType::PERFORMANCE) ? GREEN : RED;
+ mLayer->setColor(color);
+ mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
new file mode 100644
index 0000000..ce29bc3
--- /dev/null
+++ b/services/surfaceflinger/RefreshRateOverlay.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 "SurfaceFlinger.h"
+
+namespace android {
+
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+
+class RefreshRateOverlay {
+public:
+ RefreshRateOverlay(SurfaceFlinger& flinger);
+
+ void changeRefreshRate(RefreshRateType type);
+
+private:
+ bool createLayer();
+
+ SurfaceFlinger& mFlinger;
+ sp<Client> mClient;
+ sp<Layer> mLayer;
+ sp<IBinder> mIBinder;
+ sp<IGraphicBufferProducer> mGbp;
+
+ const half3 RED = half3(1.0f, 0.0f, 0.0f);
+ const half3 GREEN = half3(0.0f, 1.0f, 0.0f);
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
new file mode 100644
index 0000000..66906e9
--- /dev/null
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -0,0 +1,481 @@
+/*
+ * 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 <compositionengine/Display.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#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) {
+ 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(mThreadControlMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ 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(mSamplingMutex);
+ mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
+}
+
+void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
+ std::lock_guard lock(mSamplingMutex);
+ mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+void RegionSamplingThread::checkForStaleLuma() {
+ std::lock_guard lock(mThreadControlMutex);
+
+ 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(mThreadControlMutex);
+ 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(mSamplingMutex);
+ 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;
+}
+} // anonymous namespace
+
+float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
+ uint32_t orientation, const Rect& sample_area) {
+ if (!sample_area.isValid() || (sample_area.getWidth() > width) ||
+ (sample_area.getHeight() > height)) {
+ ALOGE("invalid sampling region requested");
+ return 0.0f;
+ }
+
+ // (b/133849373) ROT_90 screencap images produced upside down
+ auto area = sample_area;
+ if (orientation & ui::Transform::ROT_90) {
+ area.top = height - area.top;
+ area.bottom = height - area.bottom;
+ std::swap(area.top, area.bottom);
+
+ area.left = width - area.left;
+ area.right = width - area.right;
+ std::swap(area.left, area.right);
+ }
+
+ 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;
+ for (; bucket < brightnessBuckets.size(); bucket++) {
+ accumulated += brightnessBuckets[bucket];
+ if (accumulated > majoritySampleNum) break;
+ }
+
+ return bucket / 255.0f;
+}
+
+std::vector<float> RegionSamplingThread::sampleBuffer(
+ const sp<GraphicBuffer>& buffer, const Point& leftTop,
+ const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) {
+ 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 width = buffer->getWidth();
+ const int32_t height = buffer->getHeight();
+ 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(), width, height, stride, orientation,
+ descriptor.area - leftTop);
+ });
+ return lumas;
+}
+
+void RegionSamplingThread::captureSample() {
+ ATRACE_CALL();
+ std::lock_guard lock(mSamplingMutex);
+
+ if (mDescriptors.empty()) {
+ return;
+ }
+
+ const auto device = mFlinger.getDefaultDisplayDevice();
+ const auto display = device->getCompositionDisplay();
+ const auto state = display->getState();
+ const auto orientation = static_cast<ui::Transform::orientation_flags>(state.orientation);
+
+ 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();
+
+ auto dx = 0;
+ auto dy = 0;
+ switch (orientation) {
+ case ui::Transform::ROT_90:
+ dx = device->getWidth();
+ break;
+ case ui::Transform::ROT_180:
+ dx = device->getWidth();
+ dy = device->getHeight();
+ break;
+ case ui::Transform::ROT_270:
+ dy = device->getHeight();
+ break;
+ default:
+ break;
+ }
+
+ ui::Transform t(orientation);
+ auto screencapRegion = t.transform(sampleRegion);
+ screencapRegion = screencapRegion.translate(dx, dy);
+ DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
+ sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
+
+ 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);
+ };
+
+ sp<GraphicBuffer> buffer = nullptr;
+ if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
+ mCachedBuffer->getHeight() == sampledArea.getHeight()) {
+ buffer = mCachedBuffer;
+ } else {
+ const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
+ buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
+ PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+ }
+
+ bool ignored;
+ mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false, ignored);
+
+ 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, orientation);
+ 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]);
+ }
+
+ // Extend the lifetime of mCachedBuffer from the previous frame to here to ensure that:
+ // 1) The region sampling thread is the last owner of the buffer, and the freeing of the buffer
+ // happens in this thread, as opposed to the main thread.
+ // 2) The listener(s) receive their notifications prior to freeing the buffer.
+ mCachedBuffer = buffer;
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RegionSamplingThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mThreadControlMutex);
+ while (mRunning) {
+ if (mSampleRequested) {
+ mSampleRequested = false;
+ lock.unlock();
+ captureSample();
+ lock.lock();
+ }
+ mCondition.wait(lock, [this]() REQUIRES(mThreadControlMutex) {
+ return mSampleRequested || !mRunning;
+ });
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
new file mode 100644
index 0000000..3c6fcf3
--- /dev/null
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -0,0 +1,128 @@
+/*
+ * 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/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+#include "Scheduler/IdleTimer.h"
+
+namespace android {
+
+class IRegionSamplingListener;
+class Layer;
+class Scheduler;
+class SurfaceFlinger;
+struct SamplingOffsetCallback;
+
+float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
+ uint32_t orientation, const Rect& area);
+
+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, uint32_t orientation);
+
+ void doSample();
+ void binderDied(const wp<IBinder>& who) override;
+ void checkForStaleLuma();
+
+ void captureSample();
+ void threadMain();
+
+ SurfaceFlinger& mFlinger;
+ Scheduler& mScheduler;
+ const TimingTunables mTunables;
+ scheduler::IdleTimer mIdleTimer;
+
+ std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
+
+ std::thread mThread;
+
+ std::mutex mThreadControlMutex;
+ std::condition_variable_any mCondition;
+ bool mRunning GUARDED_BY(mThreadControlMutex) = true;
+ bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
+ bool mDiscardedFrames GUARDED_BY(mThreadControlMutex) = false;
+ std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
+
+ std::mutex mSamplingMutex;
+ std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
+ sp<GraphicBuffer> mCachedBuffer GUARDED_BY(mSamplingMutex) = nullptr;
+};
+
+} // 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 61%
rename from services/surfaceflinger/DispSync.cpp
rename to services/surfaceflinger/Scheduler/DispSync.cpp
index 37dc27d..cd6fa41 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,44 @@
#define LOG_TAG "DispSyncThread"
class DispSyncThread : public Thread {
public:
- explicit DispSyncThread(const char* name)
+ DispSyncThread(const char* name, bool showTraceDetailedInfo)
: mName(name),
mStop(false),
+ mModelLocked(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;
+ if (mReferenceTime != referenceTime) {
+ for (auto& eventListener : mEventListeners) {
+ eventListener.mHasFired = false;
+ }
+ }
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,25 +106,35 @@
}
void stop() {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
mStop = true;
mCond.signal();
}
+ void lockModel() {
+ Mutex::Autolock lock(mMutex);
+ mModelLocked = true;
+ }
+
+ void unlockModel() {
+ Mutex::Autolock lock(mMutex);
+ mModelLocked = false;
+ }
+
virtual bool threadLoop() {
status_t err;
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 +158,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 +184,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 +201,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 +218,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 +272,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,29 +289,22 @@
} else if (diff < -mPeriod / 2) {
diff += mPeriod;
}
- listener.mLastEventTime -= diff;
+ eventListener.mLastEventTime -= diff;
mCond.signal();
return NO_ERROR;
}
}
-
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;
+ bool mHasFired = false;
};
struct CallbackInvocation {
@@ -259,7 +313,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 +328,45 @@
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;
+ ALOGV("[%s] [%s] Skipping event due to model error", mName,
+ eventListener.mName);
+ continue;
+ }
+ if (eventListener.mHasFired && !mModelLocked) {
+ eventListener.mLastEventTime = t;
+ ALOGV("[%s] [%s] Skipping event due to already firing", 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;
+ eventListener.mHasFired = true;
}
}
@@ -298,7 +374,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 +413,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 +424,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);
}
@@ -358,6 +434,7 @@
const char* const mName;
bool mStop;
+ bool mModelLocked;
nsecs_t mPeriod;
nsecs_t mPhase;
@@ -366,10 +443,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 +468,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,34 +496,43 @@
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;
+ mThread->unlockModel();
resetErrorLocked();
}
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;
@@ -448,25 +545,45 @@
void DispSync::beginResync() {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] beginResync", mName);
+ mThread->unlockModel();
mModelUpdated = false;
mNumResyncSamples = 0;
}
-bool DispSync::addResyncSample(nsecs_t timestamp) {
+bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
- size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
+ *periodChanged = false;
+ const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
mResyncSamples[idx] = timestamp;
if (mNumResyncSamples == 0) {
mPhase = 0;
- mReferenceTime = timestamp;
ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
"mReferenceTime = %" PRId64,
- mName, ns2us(mPeriod), ns2us(mReferenceTime));
- mThread->updateModel(mPeriod, mPhase, mReferenceTime);
+ mName, ns2us(mPeriod), ns2us(timestamp));
+ } else if (mPendingPeriod > 0) {
+ // mNumResyncSamples > 0, so priorIdx won't overflow
+ const size_t priorIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
+ const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
+
+ const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
+ if (std::abs(observedVsync - mPendingPeriod) < std::abs(observedVsync - mPeriod)) {
+ // Observed vsync is closer to the pending period, so reset the
+ // model and flush the pending period.
+ resetLocked();
+ mPeriod = mPendingPeriod;
+ mPendingPeriod = 0;
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
+ }
+ *periodChanged = true;
+ }
}
+ // Always update the reference time with the most recent timestamp.
+ mReferenceTime = timestamp;
+ mThread->updateModel(mPeriod, mPhase, mReferenceTime);
if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
mNumResyncSamples++;
@@ -481,26 +598,30 @@
}
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
// resync again
- bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
+ bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
+ if (modelLocked) {
+ mThread->lockModel();
+ }
return !modelLocked;
}
-void DispSync::endResync() {}
+void DispSync::endResync() {
+ mThread->lockModel();
+}
-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 +631,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) {
@@ -522,10 +643,10 @@
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
- mPeriod = period;
- mPhase = 0;
- mReferenceTime = 0;
- mThread->updateModel(mPeriod, mPhase, mReferenceTime);
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("DispSync:PendingPeriod", period);
+ }
+ mPendingPeriod = period;
}
nsecs_t DispSync::getPeriod() {
@@ -580,11 +701,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 +755,7 @@
"No present times for model error.");
}
- if (kTraceDetailedInfo) {
+ if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Error", mError);
}
}
@@ -657,57 +773,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 64%
rename from services/surfaceflinger/DispSync.h
rename to services/surfaceflinger/Scheduler/DispSync.h
index 077256a..8f8b8e7 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, bool* periodChanged) = 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,79 @@
// 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;
+ // Adds a vsync sample to the dispsync model. The timestamp is the time
+ // of the vsync event that fired. periodChanged will return true if the
+ // vsync period was detected to have changed to mPendingPeriod.
+ //
+ // This method will return true if more vsync samples are needed to lock
+ // down the DispSync model, and false otherwise.
+ bool addResyncSample(nsecs_t timestamp, bool* periodChanged) 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 };
@@ -144,6 +205,12 @@
// nanoseconds.
nsecs_t mPeriod;
+ // mPendingPeriod is the proposed period change in nanoseconds.
+ // If mPendingPeriod differs from mPeriod and is nonzero, it will
+ // be flushed to mPeriod when we detect that the hardware switched
+ // vsync frequency.
+ nsecs_t mPendingPeriod = 0;
+
// mPhase is the phase offset of the modeled vsync events. It is the
// number of nanoseconds from time 0 to the first vsync event.
nsecs_t mPhase;
@@ -168,7 +235,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 +262,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..00948ae
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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::onDispSyncEvent(nsecs_t when) {
+ 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..4759699
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+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;
+};
+
+} // 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..05bad4d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -0,0 +1,481 @@
+/*
+ * 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(PhysicalDisplayId 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)
+ : resyncCallback(std::move(resyncCallback)),
+ 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);
+}
+
+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);
+}
+
+sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
+ return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
+}
+
+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) {
+ 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..61530c6
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -0,0 +1,220 @@
+/*
+ * 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()>;
+
+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;
+};
+
+class EventThreadConnection : public BnDisplayEventConnection {
+public:
+ EventThreadConnection(EventThread*, ResyncCallback);
+ 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
+
+ // Called in response to requestNextVsync.
+ const ResyncCallback resyncCallback;
+
+ VSyncRequest vsyncRequest = VSyncRequest::None;
+
+private:
+ virtual void onFirstRef();
+ EventThread* const mEventThread;
+ gui::BitTube mChannel;
+};
+
+class EventThread {
+public:
+ virtual ~EventThread();
+
+ virtual sp<EventThreadConnection> createEventConnection(ResyncCallback) 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) = 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) 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) 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;
+
+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..37fdfc7
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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::RESET) {
+ triggerTime = std::chrono::steady_clock::now() + mInterval;
+ mState = TimerState::WAITING;
+ } else 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..2646688
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -0,0 +1,94 @@
+/*
+ * 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();
+
+ // Initializes and turns on the idle timer.
+ void start();
+ // Stops the idle timer and any held resources.
+ void stop();
+ // Resets the wakeup time and fires the reset callback.
+ void reset();
+
+private:
+ // Enum to track in what state is the timer.
+ enum class TimerState {
+ // The internal timer thread has been destroyed, and no state is
+ // tracked.
+ // Possible state transitions: RESET
+ STOPPED = 0,
+ // An external thread has just reset this timer.
+ // If there is a reset callback, then that callback is fired.
+ // Possible state transitions: STOPPED, WAITING
+ RESET = 1,
+ // This timer is waiting for the timeout interval to expire.
+ // Possible state transaitions: STOPPED, RESET, IDLE
+ WAITING = 2,
+ // The timeout interval has expired, so we are sleeping now.
+ // Possible state transaitions: STOPPED, RESET
+ 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;
+
+ // Current timer state
+ 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..1db43a3
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 "LayerHistory.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <limits>
+#include <numeric>
+#include <string>
+#include <unordered_map>
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+std::atomic<int64_t> LayerHistory::sNextId = 0;
+
+LayerHistory::LayerHistory() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.layer_history_trace", value, "0");
+ mTraceEnabled = bool(atoi(value));
+}
+
+LayerHistory::~LayerHistory() = default;
+
+std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
+ float maxRefreshRate) {
+ const int64_t id = sNextId++;
+
+ std::lock_guard lock(mLock);
+ mInactiveLayerInfos.emplace(id, std::make_shared<LayerInfo>(name, maxRefreshRate));
+ return std::make_unique<LayerHistory::LayerHandle>(*this, id);
+}
+
+void LayerHistory::destroyLayer(const int64_t id) {
+ std::lock_guard lock(mLock);
+ auto it = mActiveLayerInfos.find(id);
+ if (it != mActiveLayerInfos.end()) {
+ mActiveLayerInfos.erase(it);
+ }
+
+ it = mInactiveLayerInfos.find(id);
+ if (it != mInactiveLayerInfos.end()) {
+ mInactiveLayerInfos.erase(it);
+ }
+}
+
+void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime,
+ bool isHdr) {
+ std::shared_ptr<LayerInfo> layerInfo;
+ {
+ std::lock_guard lock(mLock);
+ auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
+ if (layerInfoIterator != mInactiveLayerInfos.end()) {
+ layerInfo = layerInfoIterator->second;
+ mInactiveLayerInfos.erase(layerInfoIterator);
+ mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
+ } else {
+ layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
+ if (layerInfoIterator != mActiveLayerInfos.end()) {
+ layerInfo = layerInfoIterator->second;
+ } else {
+ ALOGW("Inserting information about layer that is not registered: %" PRId64,
+ layerHandle->mId);
+ return;
+ }
+ }
+ }
+ layerInfo->setLastPresentTime(presentTime);
+ layerInfo->setHDRContent(isHdr);
+}
+
+void LayerHistory::setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible) {
+ std::shared_ptr<LayerInfo> layerInfo;
+ {
+ std::lock_guard lock(mLock);
+ auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
+ if (layerInfoIterator != mInactiveLayerInfos.end()) {
+ layerInfo = layerInfoIterator->second;
+ if (visible) {
+ mInactiveLayerInfos.erase(layerInfoIterator);
+ mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
+ }
+ } else {
+ layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
+ if (layerInfoIterator != mActiveLayerInfos.end()) {
+ layerInfo = layerInfoIterator->second;
+ } else {
+ ALOGW("Inserting information about layer that is not registered: %" PRId64,
+ layerHandle->mId);
+ return;
+ }
+ }
+ }
+ layerInfo->setVisibility(visible);
+}
+
+std::pair<float, bool> LayerHistory::getDesiredRefreshRateAndHDR() {
+ bool isHDR = false;
+ float newRefreshRate = 0.f;
+ std::lock_guard lock(mLock);
+
+ removeIrrelevantLayers();
+
+ // Iterate through all layers that have been recently updated, and find the max refresh rate.
+ for (const auto& [layerId, layerInfo] : mActiveLayerInfos) {
+ const float layerRefreshRate = layerInfo->getDesiredRefreshRate();
+ if (mTraceEnabled) {
+ // Store the refresh rate in traces for easy debugging.
+ std::string layerName = "LFPS " + layerInfo->getName();
+ ATRACE_INT(layerName.c_str(), std::round(layerRefreshRate));
+ ALOGD("%s: %f", layerName.c_str(), std::round(layerRefreshRate));
+ }
+ if (layerInfo->isRecentlyActive() && layerRefreshRate > newRefreshRate) {
+ newRefreshRate = layerRefreshRate;
+ }
+ isHDR |= layerInfo->getHDRContent();
+ }
+ if (mTraceEnabled) {
+ ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
+ }
+
+ return {newRefreshRate, isHDR};
+}
+
+void LayerHistory::removeIrrelevantLayers() {
+ const int64_t obsoleteEpsilon = systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
+ // Iterator pointing to first element in map
+ auto it = mActiveLayerInfos.begin();
+ while (it != mActiveLayerInfos.end()) {
+ // If last updated was before the obsolete time, remove it.
+ // Keep HDR layer around as long as they are visible.
+ if (!it->second->isVisible() ||
+ (!it->second->getHDRContent() && it->second->getLastUpdatedTime() < obsoleteEpsilon)) {
+ // erase() function returns the iterator of the next
+ // to last deleted element.
+ if (mTraceEnabled) {
+ ALOGD("Layer %s obsolete", it->second->getName().c_str());
+ // Make sure to update systrace to indicate that the layer was erased.
+ std::string layerName = "LFPS " + it->second->getName();
+ ATRACE_INT(layerName.c_str(), 0);
+ }
+ auto id = it->first;
+ auto layerInfo = it->second;
+ layerInfo->clearHistory();
+ mInactiveLayerInfos.insert({id, layerInfo});
+ it = mActiveLayerInfos.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace scheduler
+} // 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..adc5ce5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -0,0 +1,87 @@
+/*
+ * 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 "LayerInfo.h"
+#include "SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class represents information about layers that are considered current. We keep an
+ * unordered map between layer name and LayerInfo.
+ */
+class LayerHistory {
+public:
+ // Handle for each layer we keep track of.
+ class LayerHandle {
+ public:
+ LayerHandle(LayerHistory& lh, int64_t id) : mId(id), mLayerHistory(lh) {}
+ ~LayerHandle() { mLayerHistory.destroyLayer(mId); }
+
+ const int64_t mId;
+
+ private:
+ LayerHistory& mLayerHistory;
+ };
+
+ LayerHistory();
+ ~LayerHistory();
+
+ // When the layer is first created, register it.
+ std::unique_ptr<LayerHandle> createLayer(const std::string name, float maxRefreshRate);
+
+ // Method for inserting layers and their requested present time into the unordered map.
+ void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
+ // Method for setting layer visibility
+ void setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible);
+
+ // Returns the desired refresh rate, which is a max refresh rate of all the current
+ // layers. See go/content-fps-detection-in-scheduler for more information.
+ std::pair<float, bool> getDesiredRefreshRateAndHDR();
+
+ // Removes the handle and the object from the map.
+ void destroyLayer(const int64_t id);
+
+private:
+ // Removes the layers that have been idle for a given amount of time from mLayerInfos.
+ void removeIrrelevantLayers() REQUIRES(mLock);
+
+ // Information about currently active layers.
+ std::mutex mLock;
+ std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mActiveLayerInfos GUARDED_BY(mLock);
+ std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mInactiveLayerInfos GUARDED_BY(mLock);
+
+ // Each layer has it's own ID. This variable keeps track of the count.
+ static std::atomic<int64_t> sNextId;
+
+ // Flag whether to log layer FPS in systrace
+ bool mTraceEnabled = false;
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
new file mode 100644
index 0000000..95d7d31
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "LayerInfo.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+
+namespace android {
+namespace scheduler {
+
+LayerInfo::LayerInfo(const std::string name, float maxRefreshRate)
+ : mName(name),
+ mMinRefreshDuration(1e9f / maxRefreshRate),
+ mRefreshRateHistory(mMinRefreshDuration) {}
+
+LayerInfo::~LayerInfo() = default;
+
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) {
+ std::lock_guard lock(mLock);
+
+ // Buffers can come with a present time far in the future. That keeps them relevant.
+ mLastUpdatedTime = std::max(lastPresentTime, systemTime());
+ mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+
+ const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
+ mLastPresentTime = lastPresentTime;
+ // Ignore time diff that are too high - those are stale values
+ if (timeDiff > TIME_EPSILON_NS.count()) return;
+ const nsecs_t refreshDuration = (timeDiff > 0) ? timeDiff : mMinRefreshDuration;
+ mRefreshRateHistory.insertRefreshRate(refreshDuration);
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
new file mode 100644
index 0000000..02b6aef
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -0,0 +1,178 @@
+/*
+ * 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 <cstdint>
+#include <deque>
+#include <mutex>
+#include <numeric>
+#include <string>
+
+#include <log/log.h>
+
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class represents information about individial layers.
+ */
+class LayerInfo {
+ /**
+ * Struct that keeps the information about the refresh rate for last
+ * HISTORY_SIZE frames. This is used to better determine the refresh rate
+ * for individual layers.
+ */
+ class RefreshRateHistory {
+ public:
+ explicit RefreshRateHistory(nsecs_t minRefreshDuration)
+ : mMinRefreshDuration(minRefreshDuration) {}
+ void insertRefreshRate(nsecs_t refreshRate) {
+ mElements.push_back(refreshRate);
+ if (mElements.size() > HISTORY_SIZE) {
+ mElements.pop_front();
+ }
+ }
+
+ float getRefreshRateAvg() const {
+ nsecs_t refreshDuration = mMinRefreshDuration;
+ if (mElements.size() == HISTORY_SIZE) {
+ refreshDuration = scheduler::calculate_mean(mElements);
+ }
+
+ return 1e9f / refreshDuration;
+ }
+ void clearHistory() { mElements.clear(); }
+
+ private:
+ std::deque<nsecs_t> mElements;
+ static constexpr size_t HISTORY_SIZE = 30;
+ const nsecs_t mMinRefreshDuration;
+ };
+
+ /**
+ * Struct that keeps the information about the present time for last
+ * HISTORY_SIZE frames. This is used to better determine whether the given layer
+ * is still relevant and it's refresh rate should be considered.
+ */
+ class PresentTimeHistory {
+ public:
+ void insertPresentTime(nsecs_t presentTime) {
+ mElements.push_back(presentTime);
+ if (mElements.size() > HISTORY_SIZE) {
+ mElements.pop_front();
+ }
+ }
+
+ // Checks whether the present time that was inserted HISTORY_SIZE ago is within a
+ // certain threshold: TIME_EPSILON_NS.
+ bool isRelevant() const {
+ const int64_t obsoleteEpsilon = systemTime() - scheduler::TIME_EPSILON_NS.count();
+ // The layer had to publish at least HISTORY_SIZE of updates, and the first
+ // update should not be older than TIME_EPSILON_NS nanoseconds.
+ if (mElements.size() == HISTORY_SIZE &&
+ mElements.at(HISTORY_SIZE - 1) > obsoleteEpsilon) {
+ return true;
+ }
+ return false;
+ }
+
+ void clearHistory() { mElements.clear(); }
+
+ private:
+ std::deque<nsecs_t> mElements;
+ static constexpr size_t HISTORY_SIZE = 10;
+ };
+
+public:
+ LayerInfo(const std::string name, float maxRefreshRate);
+ ~LayerInfo();
+
+ LayerInfo(const LayerInfo&) = delete;
+ LayerInfo& operator=(const LayerInfo&) = delete;
+
+ // Records the last requested oresent time. It also stores information about when
+ // the layer was last updated. If the present time is farther in the future than the
+ // updated time, the updated time is the present time.
+ void setLastPresentTime(nsecs_t lastPresentTime);
+
+ void setHDRContent(bool isHdr) {
+ std::lock_guard lock(mLock);
+ mIsHDR = isHdr;
+ }
+
+ void setVisibility(bool visible) {
+ std::lock_guard lock(mLock);
+ mIsVisible = visible;
+ }
+
+ // Checks the present time history to see whether the layer is relevant.
+ bool isRecentlyActive() const {
+ std::lock_guard lock(mLock);
+ return mPresentTimeHistory.isRelevant();
+ }
+
+ // Calculate the average refresh rate.
+ float getDesiredRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return mRefreshRateHistory.getRefreshRateAvg();
+ }
+
+ bool getHDRContent() {
+ std::lock_guard lock(mLock);
+ return mIsHDR;
+ }
+
+ bool isVisible() {
+ std::lock_guard lock(mLock);
+ return mIsVisible;
+ }
+
+ // Return the last updated time. If the present time is farther in the future than the
+ // updated time, the updated time is the present time.
+ nsecs_t getLastUpdatedTime() {
+ std::lock_guard lock(mLock);
+ return mLastUpdatedTime;
+ }
+
+ std::string getName() const { return mName; }
+
+ void clearHistory() {
+ std::lock_guard lock(mLock);
+ mRefreshRateHistory.clearHistory();
+ mPresentTimeHistory.clearHistory();
+ }
+
+private:
+ const std::string mName;
+ const nsecs_t mMinRefreshDuration;
+ mutable std::mutex mLock;
+ nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
+ nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
+ RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
+ PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
+ bool mIsHDR GUARDED_BY(mLock) = false;
+ bool mIsVisible GUARDED_BY(mLock) = false;
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
similarity index 90%
rename from services/surfaceflinger/MessageQueue.cpp
rename to services/surfaceflinger/Scheduler/MessageQueue.cpp
index 056d381..baf900d 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,18 @@
}
mEventThread = eventThread;
- mEvents = eventThread->createEventConnection();
+ mEvents = eventThread->createEventConnection(std::move(resyncCallback));
+ 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);
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
similarity index 87%
rename from services/surfaceflinger/MessageQueue.h
rename to services/surfaceflinger/Scheduler/MessageQueue.h
index 90d1c72..0b2206d 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,7 +85,9 @@
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;
@@ -114,7 +116,7 @@
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
android::EventThread* mEventThread;
- sp<IDisplayEventConnection> mEvents;
+ sp<EventThreadConnection> mEvents;
gui::BitTube mEventTube;
sp<Handler> mHandler;
@@ -124,13 +126,15 @@
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 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..276bce1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -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.
+ */
+
+#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);
+
+ // Below defines the threshold when an offset is considered to be negative, i.e. targeting
+ // for the N+2 vsync instead of N+1. This means that:
+ // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
+ // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
+ property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1");
+ const int phaseOffsetThresholdForNextVsyncNs = 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};
+
+ mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1
+ ? phaseOffsetThresholdForNextVsyncNs
+ : std::numeric_limits<nsecs_t>::max();
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
+ android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
+ switch (refreshRateType) {
+ 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..dc71e6e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.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 <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 getOffsetsForRefreshRate(
+ RefreshRateConfigs::RefreshRateType refreshRateType) const = 0;
+ virtual Offsets getCurrentOffsets() const = 0;
+ virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
+ virtual nsecs_t getOffsetThresholdForNextVsync() const = 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 for a given refresh rate.
+ Offsets getOffsetsForRefreshRate(
+ RefreshRateConfigs::RefreshRateType refreshRateType) const override;
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ Offsets getCurrentOffsets() const override {
+ return getOffsetsForRefreshRate(mRefreshRateType);
+ }
+
+ // 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;
+ }
+
+ nsecs_t getOffsetThresholdForNextVsync() const override { return mOffsetThresholdForNextVsync; }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+private:
+ Offsets getDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
+ Offsets getHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
+
+ std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
+ RefreshRateConfigs::RefreshRateType::DEFAULT;
+
+ Offsets mDefaultRefreshRateOffsets;
+ Offsets mHighRefreshRateOffsets;
+ nsecs_t mOffsetThresholdForNextVsync;
+};
+} // 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..d730058
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.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 <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;
+ // config Id (returned from HWC2::Display::Config::getId())
+ hwc2_config_t id;
+ };
+
+ // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+ // baking them in.
+ const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
+ return mRefreshRates;
+ }
+ std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
+ const auto& refreshRate = mRefreshRates.find(type);
+ if (refreshRate != mRefreshRates.end()) {
+ return refreshRate->second;
+ }
+ return nullptr;
+ }
+
+ RefreshRateType getRefreshRateType(hwc2_config_t id) const {
+ for (const auto& [type, refreshRate] : mRefreshRates) {
+ if (refreshRate->id == id) {
+ return type;
+ }
+ }
+
+ return RefreshRateType::DEFAULT;
+ }
+
+ void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ mRefreshRates.clear();
+
+ // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
+ mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
+ std::make_shared<RefreshRate>(
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
+ HWC2_SCREEN_OFF_CONFIG_ID}));
+
+ 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;
+ const int configId = configIdToVsyncPeriod[0].first;
+ mRefreshRates.emplace(RefreshRateType::DEFAULT,
+ std::make_shared<RefreshRate>(
+ RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps),
+ configs.at(configId)->getId()}));
+ }
+
+ 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;
+ const int configId = configIdToVsyncPeriod[1].first;
+ mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
+ std::make_shared<RefreshRate>(
+ RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps),
+ configs.at(configId)->getId()}));
+ }
+ }
+
+private:
+ std::map<RefreshRateType, std::shared_ptr<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..7e7c630
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -0,0 +1,161 @@
+/*
+ * 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:
+ RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
+ : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}
+
+ // 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 (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
+ int64_t totalTimeForConfig = 0;
+ if (!config) {
+ continue;
+ }
+ 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() const {
+ std::ostringstream stream;
+ stream << "+ Refresh rate: running time in seconds\n";
+ for (const auto& [name, time] : const_cast<RefreshRateStats*>(this)->getTotalTimes()) {
+ stream << name << ": " << getDateFormatFromMs(time) << '\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) {
+ continue;
+ }
+ 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.
+ const RefreshRateConfigs& mRefreshRateConfigs;
+
+ // Aggregate refresh rate statistics for telemetry.
+ TimeStats& mTimeStats;
+
+ int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
+ int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+
+ std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+
+ nsecs_t mPreviousRecordedTime = systemTime();
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
new file mode 100644
index 0000000..513436a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -0,0 +1,553 @@
+/*
+ * 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 <input/InputWindow.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 "LayerInfo.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,
+ const scheduler::RefreshRateConfigs& refreshRateConfig)
+ : mHasSyncFramework(running_without_sync_framework(true)),
+ mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
+ mPrimaryHWVsyncEnabled(false),
+ mHWVsyncAvailable(false),
+ mRefreshRateConfigs(refreshRateConfig) {
+ // 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);
+ mSupportKernelTimer = support_kernel_idle_timer(false);
+
+ mSetTouchTimerMs = set_touch_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) {
+ if (mSupportKernelTimer) {
+ mIdleTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetIdleTimerMs),
+ [this] { resetKernelTimerCallback(); },
+ [this] {
+ expiredKernelTimerCallback();
+ });
+ } else {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetIdleTimerMs),
+ [this] { resetTimerCallback(); },
+ [this] { expiredTimerCallback(); });
+ }
+ mIdleTimer->start();
+ }
+
+ if (mSetTouchTimerMs > 0) {
+ // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
+ mTouchTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetTouchTimerMs),
+ [this] { resetTouchTimerCallback(); },
+ [this] { expiredTouchTimerCallback(); });
+ mTouchTimer->start();
+ }
+}
+
+Scheduler::~Scheduler() {
+ // Ensure the IdleTimer thread is joined before we start destroying state.
+ mTouchTimer.reset();
+ 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));
+}
+
+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::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->setPeriod(period);
+
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync->beginResync();
+ mEventControlThread->setVsyncEnabled(true);
+ mPrimaryHWVsyncEnabled = true;
+ }
+}
+
+void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
+ bool needsHwVsync = false;
+ *periodChanged = false;
+ { // Scope for the lock
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ if (mPrimaryHWVsyncEnabled) {
+ needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
+ }
+ }
+
+ 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);
+}
+
+std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
+ std::string const& name, int windowType) {
+ RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE;
+
+ const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
+ const uint32_t fps = (refreshRate) ? refreshRate->fps : 0;
+ return mLayerHistory.createLayer(name, fps);
+}
+
+void Scheduler::addLayerPresentTimeAndHDR(
+ const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
+ nsecs_t presentTime, bool isHDR) {
+ mLayerHistory.insert(layerHandle, presentTime, isHDR);
+}
+
+void Scheduler::setLayerVisibility(
+ const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
+ mLayerHistory.setVisibility(layerHandle, visible);
+}
+
+void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
+ fn(*mPrimaryDispSync);
+}
+
+void Scheduler::updateFpsBasedOnContent() {
+ auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
+ const uint32_t refreshRateRound = std::round(refreshRate);
+ RefreshRateType newRefreshRateType;
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
+ return;
+ }
+ mContentRefreshRate = refreshRateRound;
+ ATRACE_INT("ContentFPS", mContentRefreshRate);
+
+ mIsHDRContent = isHDR;
+ ATRACE_INT("ContentHDR", mIsHDRContent);
+
+ mCurrentContentFeatureState = refreshRateRound > 0
+ ? ContentFeatureState::CONTENT_DETECTION_ON
+ : ContentFeatureState::CONTENT_DETECTION_OFF;
+ newRefreshRateType = calculateRefreshRateType();
+ if (mRefreshRateType == newRefreshRateType) {
+ return;
+ }
+ mRefreshRateType = newRefreshRateType;
+ }
+ changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
+}
+
+void Scheduler::setChangeRefreshRateCallback(
+ const ChangeRefreshRateCallback& changeRefreshRateCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mChangeRefreshRateCallback = changeRefreshRateCallback;
+}
+
+void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mGetVsyncPeriod = getVsyncPeriod;
+}
+
+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::notifyTouchEvent() {
+ if (mTouchTimer) {
+ mTouchTimer->reset();
+ }
+
+ if (mSupportKernelTimer) {
+ resetIdleTimer();
+ }
+}
+
+void Scheduler::resetTimerCallback() {
+ timerChangeRefreshRate(IdleTimerState::RESET);
+ ATRACE_INT("ExpiredIdleTimer", 0);
+}
+
+void Scheduler::resetKernelTimerCallback() {
+ ATRACE_INT("ExpiredKernelIdleTimer", 0);
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mGetVsyncPeriod) {
+ resyncToHardwareVsync(false, mGetVsyncPeriod());
+ }
+}
+
+void Scheduler::expiredTimerCallback() {
+ timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ ATRACE_INT("ExpiredIdleTimer", 1);
+}
+
+void Scheduler::resetTouchTimerCallback() {
+ // We do not notify the applications about config changes when idle timer is reset.
+ touchChangeRefreshRate(TouchState::ACTIVE);
+ ATRACE_INT("TouchState", 1);
+}
+
+void Scheduler::expiredTouchTimerCallback() {
+ // We do not notify the applications about config changes when idle timer expires.
+ touchChangeRefreshRate(TouchState::INACTIVE);
+ ATRACE_INT("TouchState", 0);
+}
+
+void Scheduler::expiredKernelTimerCallback() {
+ ATRACE_INT("ExpiredKernelIdleTimer", 1);
+ // Disable HW Vsync if the timer expired, as we don't need it
+ // enabled if we're not pushing frames.
+ disableHardwareVsync(false);
+}
+
+std::string Scheduler::doDump() {
+ std::ostringstream stream;
+ stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
+ stream << "+ Touch timer interval: " << mSetTouchTimerMs << " ms" << std::endl;
+ return stream.str();
+}
+
+void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
+ RefreshRateType newRefreshRateType;
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ if (mCurrentIdleTimerState == idleTimerState) {
+ return;
+ }
+ mCurrentIdleTimerState = idleTimerState;
+ newRefreshRateType = calculateRefreshRateType();
+ if (mRefreshRateType == newRefreshRateType) {
+ return;
+ }
+ mRefreshRateType = newRefreshRateType;
+ }
+ changeRefreshRate(newRefreshRateType, ConfigEvent::None);
+}
+
+void Scheduler::touchChangeRefreshRate(TouchState touchState) {
+ ConfigEvent event = ConfigEvent::None;
+ RefreshRateType newRefreshRateType;
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ if (mCurrentTouchState == touchState) {
+ return;
+ }
+ mCurrentTouchState = touchState;
+ newRefreshRateType = calculateRefreshRateType();
+ if (mRefreshRateType == newRefreshRateType) {
+ return;
+ }
+ mRefreshRateType = newRefreshRateType;
+ // Send an event in case that content detection is on as touch has a higher priority
+ if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+ event = ConfigEvent::Changed;
+ }
+ }
+ changeRefreshRate(newRefreshRateType, event);
+}
+
+Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+ // HDR content is not supported on PERFORMANCE mode
+ if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
+ return RefreshRateType::DEFAULT;
+ }
+
+ // As long as touch is active we want to be in performance mode
+ if (mCurrentTouchState == TouchState::ACTIVE) {
+ return RefreshRateType::PERFORMANCE;
+ }
+
+ // If timer has expired as it means there is no new content on the screen
+ if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
+ return RefreshRateType::DEFAULT;
+ }
+
+ // If content detection is off we choose performance as we don't know the content fps
+ if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
+ return RefreshRateType::PERFORMANCE;
+ }
+
+ // Content detection is on, find the appropriate refresh rate
+ // Start with the smallest refresh rate which is within a margin of the content
+ RefreshRateType currRefreshRateType = RefreshRateType::PERFORMANCE;
+ constexpr float MARGIN = 0.05f;
+ auto iter = mRefreshRateConfigs.getRefreshRates().cbegin();
+ while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
+ if (iter->second->fps >= mContentRefreshRate * (1 - MARGIN)) {
+ currRefreshRateType = iter->first;
+ break;
+ }
+ ++iter;
+ }
+
+ // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+ // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+ // align well with both
+ float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
+ float(mContentRefreshRate);
+ if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+ while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
+ ratio = iter->second->fps / float(mContentRefreshRate);
+
+ if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+ currRefreshRateType = iter->first;
+ break;
+ }
+ ++iter;
+ }
+ }
+
+ return currRefreshRateType;
+}
+
+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..96d4bd5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -0,0 +1,298 @@
+/*
+ * 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,
+ const scheduler::RefreshRateConfigs& refreshRateConfig);
+
+ 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);
+
+ 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);
+ // Passes a vsync sample to DispSync. periodChange will be true if DipSync
+ // detected that the vsync period changed, and false otherwise.
+ void addResyncSample(const nsecs_t timestamp, bool* periodChanged);
+ void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+ void setIgnorePresentFences(bool ignore);
+ nsecs_t expectedPresentTime();
+ // Registers the layer in the scheduler, and returns the handle for future references.
+ std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
+ int windowType);
+
+ // Stores present time for a layer.
+ void addLayerPresentTimeAndHDR(
+ const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
+ nsecs_t presentTime, bool isHDR);
+ // Stores visibility for a layer.
+ void setLayerVisibility(
+ const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible);
+ // Updates FPS based on the most content presented.
+ void updateFpsBasedOnContent();
+ // Callback that gets invoked when Scheduler wants to change the refresh rate.
+ void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+ void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
+
+ // Returns whether idle timer is enabled or not
+ bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
+
+ // Function that resets the idle timer.
+ void resetIdleTimer();
+
+ // Function that resets the touch timer.
+ void notifyTouchEvent();
+
+ // 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 ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
+ enum class IdleTimerState { EXPIRED, RESET };
+ enum class TouchState { INACTIVE, ACTIVE };
+
+ // 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 is called when the timer resets.
+ void resetTimerCallback();
+ // Function that is called when the timer expires.
+ void expiredTimerCallback();
+ // Function that is called when the timer resets when paired with a display
+ // driver timeout in the kernel. This enables hardware vsync when we move
+ // out from idle.
+ void resetKernelTimerCallback();
+ // Function that is called when the timer expires when paired with a display
+ // driver timeout in the kernel. This disables hardware vsync when we move
+ // into idle.
+ void expiredKernelTimerCallback();
+ // Function that is called when the touch timer resets.
+ void resetTouchTimerCallback();
+ // Function that is called when the touch timer expires.
+ void expiredTouchTimerCallback();
+ // Sets vsync period.
+ void setVsyncPeriod(const nsecs_t period);
+ // Idle timer feature's function to change the refresh rate.
+ void timerChangeRefreshRate(IdleTimerState idleTimerState);
+ // Touch timer feature's function to change the refresh rate.
+ void touchChangeRefreshRate(TouchState touchState);
+ // Calculate the new refresh rate type
+ RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
+ void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
+
+ // Helper function to calculate error frames
+ float getErrorFrames(float contentFps, float configFps);
+
+ // 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;
+
+ // Historical information about individual layers. Used for predicting the refresh rate.
+ scheduler::LayerHistory mLayerHistory;
+
+ // 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;
+ // Enables whether to use idle timer callbacks that support the kernel
+ // timer.
+ bool mSupportKernelTimer;
+
+ // Timer used to monitor touch events.
+ int64_t mSetTouchTimerMs = 0;
+ std::unique_ptr<scheduler::IdleTimer> mTouchTimer;
+
+ std::mutex mCallbackLock;
+ ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+ GetVsyncPeriod mGetVsyncPeriod 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;
+ ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) =
+ ContentFeatureState::CONTENT_DETECTION_OFF;
+ IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
+ TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
+ uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
+ RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
+ bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
+
+ const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
+
+ // Global config to force HDR content to work on DEFAULT refreshRate
+ static constexpr bool mForceHDRContentToDefaultRefreshRate = true;
+};
+
+} // 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..3bf3922
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -0,0 +1,81 @@
+/*
+ * 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 <cinttypes>
+#include <numeric>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+using namespace std::chrono_literals;
+
+// 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;
+static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
+
+// This number is used when we try to determine how long does a given layer stay relevant.
+// Currently it is set to 100ms, because that would indicate 10Hz rendering.
+static constexpr std::chrono::nanoseconds TIME_EPSILON_NS = 100ms;
+
+// This number is used when we try to determine how long do we keep layer information around
+// before we remove it. Currently it is set to 100ms.
+static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 100ms;
+
+// 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(), static_cast<V>(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/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
new file mode 100644
index 0000000..81a7864
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -0,0 +1,203 @@
+/*
+ * 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 <utils/Errors.h>
+
+#include <cinttypes>
+#include <mutex>
+
+#include "Scheduler.h"
+
+namespace android {
+
+/*
+ * Modulates the vsync-offsets depending on current SurfaceFlinger state.
+ */
+class VSyncModulator {
+private:
+ // Number of frames we'll keep the early phase offsets once they are activated for a
+ // transaction. 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_TRANSACTION = 2;
+
+public:
+ struct Offsets {
+ nsecs_t sf;
+ nsecs_t app;
+ };
+
+ // Sets the phase offsets
+ //
+ // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
+ // as early. May be the same as late, in which case we don't shift offsets.
+ // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
+ // and the transaction was marked as early, we'll use sfEarly.
+ // sfLate: The regular SF vsync phase offset.
+ // appEarly: Like sfEarly, but for the app-vsync
+ // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
+ // appLate: The regular app vsync phase offset.
+ void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) {
+ 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 getEarlyGlOffsets() const { return mEarlyGlOffsets; }
+
+ void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
+ mSfEventThread = sfEventThread;
+ mAppEventThread = appEventThread;
+ }
+
+ void setSchedulerAndHandles(Scheduler* scheduler,
+ Scheduler::ConnectionHandle* appConnectionHandle,
+ Scheduler::ConnectionHandle* sfConnectionHandle) {
+ mScheduler = scheduler;
+ mAppConnectionHandle = appConnectionHandle;
+ mSfConnectionHandle = sfConnectionHandle;
+ }
+
+ void setTransactionStart(Scheduler::TransactionStart transactionStart) {
+ if (transactionStart == Scheduler::TransactionStart::EARLY) {
+ mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ }
+
+ // An early transaction stays an early transaction.
+ if (transactionStart == mTransactionStart ||
+ mTransactionStart == Scheduler::TransactionStart::EARLY) {
+ return;
+ }
+ mTransactionStart = transactionStart;
+ updateOffsets();
+ }
+
+ void onTransactionHandled() {
+ if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
+ mTransactionStart = Scheduler::TransactionStart::NORMAL;
+ updateOffsets();
+ }
+
+ // Called when we send a refresh rate change to hardware composer, so that
+ // we can move into early offsets.
+ void onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) {
+ return;
+ }
+ mRefreshRateChangePending = true;
+ updateOffsets();
+ }
+
+ // Called when we detect from vsync signals that the refresh rate changed.
+ // This way we can move out of early offsets if no longer necessary.
+ void onRefreshRateChangeDetected() {
+ if (!mRefreshRateChangePending) {
+ return;
+ }
+ mRefreshRateChangePending = false;
+ updateOffsets();
+ }
+
+ void onRefreshed(bool usedRenderEngine) {
+ bool updateOffsetsNeeded = false;
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updateOffsetsNeeded = true;
+ }
+ if (usedRenderEngine != mLastFrameUsedRenderEngine) {
+ mLastFrameUsedRenderEngine = usedRenderEngine;
+ updateOffsetsNeeded = true;
+ }
+ if (updateOffsetsNeeded) {
+ updateOffsets();
+ }
+ }
+
+ Offsets getOffsets() {
+ // Early offsets are used if we're in the middle of a refresh rate
+ // change, or if we recently begin a transaction.
+ if (mTransactionStart == Scheduler::TransactionStart::EARLY ||
+ mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
+ return mEarlyOffsets;
+ } else if (mLastFrameUsedRenderEngine) {
+ return mEarlyGlOffsets;
+ } else {
+ return mLateOffsets;
+ }
+ }
+
+private:
+ void updateOffsets() {
+ const Offsets desired = getOffsets();
+ const Offsets current = mOffsets;
+
+ bool changed = false;
+ if (desired.sf != current.sf) {
+ if (mSfConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
+ } else {
+ mSfEventThread->setPhaseOffset(desired.sf);
+ }
+ changed = true;
+ }
+ if (desired.app != current.app) {
+ if (mAppConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
+ } else {
+ mAppEventThread->setPhaseOffset(desired.app);
+ }
+ changed = true;
+ }
+
+ if (changed) {
+ mOffsets = desired;
+ }
+ }
+
+ Offsets mLateOffsets;
+ Offsets mEarlyOffsets;
+ Offsets mEarlyGlOffsets;
+
+ EventThread* mSfEventThread = nullptr;
+ EventThread* mAppEventThread = nullptr;
+
+ Scheduler* mScheduler = nullptr;
+ Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
+ Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
+
+ std::atomic<Offsets> mOffsets;
+
+ std::atomic<Scheduler::TransactionStart> mTransactionStart =
+ Scheduler::TransactionStart::NORMAL;
+ std::atomic<bool> mLastFrameUsedRenderEngine = false;
+ std::atomic<bool> mRefreshRateChangePending = false;
+ std::atomic<int> mRemainingEarlyFrameCount = 0;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5324470..9c8251f 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,80 +37,99 @@
#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 "RefreshRateOverlay.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 "RegionSamplingThread.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/PhaseOffsets.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 <android/hardware/power/1.0/IPower.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 android::hardware::power::V1_0::PowerHint;
+using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
+using ui::DisplayPrimaries;
using ui::Hdr;
using ui::RenderIntent;
@@ -117,19 +138,63 @@
#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 +211,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 +227,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,116 +270,59 @@
}
}
-NativeWindowSurface::~NativeWindowSurface() = default;
+SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {}
-namespace impl {
+SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
+ : mFactory(factory),
+ mPhaseOffsets(mFactory.createPhaseOffsets()),
+ mInterceptor(mFactory.createSurfaceInterceptor(this)),
+ mTimeStats(mFactory.createTimeStats()),
+ mEventQueue(mFactory.createMessageQueue()),
+ mCompositionEngine(mFactory.createCompositionEngine()) {}
-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)
- : BnSurfaceComposer(),
- mTransactionFlags(0),
- mTransactionPending(false),
- mAnimTransactionPending(false),
- mLayersRemoved(false),
- mLayersAdded(false),
- mRepaintEverything(0),
- mBootTime(systemTime()),
- mBuiltinDisplays(),
- mVisibleRegionsDirty(false),
- mGeometryInvalid(false),
- mAnimCompositionPending(false),
- mBootStage(BootStage::BOOTLOADER),
- mDebugRegion(0),
- mDebugDisableHWC(0),
- mDebugDisableTransformHint(0),
- mDebugInSwapBuffers(0),
- mLastSwapBufferTime(0),
- mDebugInTransaction(0),
- mLastTransactionTime(0),
- mForceFullDamage(false),
- mPrimaryDispSync("PrimaryDispSync"),
- mPrimaryHWVsyncEnabled(false),
- mHWVsyncAvailable(false),
- mHasPoweredOff(false),
- mNumLayers(0),
- mVrFlingerRequestsDisplay(false),
- mMainThreadId(std::this_thread::get_id()),
- mCreateBufferQueue(&BufferQueue::createBufferQueue),
- mCreateNativeWindowSurface(&impl::NativeWindowSurface::create) {}
-
-SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) {
+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(
+ hasWideColorDisplay ? Dataspace::DISPLAY_P3 : 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 +331,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 +355,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 +365,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
@@ -389,9 +399,7 @@
mEventQueue->init(this);
}
-SurfaceFlinger::~SurfaceFlinger()
-{
-}
+SurfaceFlinger::~SurfaceFlinger() = default;
void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
{
@@ -416,19 +424,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 +444,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 +533,13 @@
if (mWindowManager != 0) {
mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
+ sp<IBinder> input(defaultServiceManager()->getService(
+ String16("inputflinger")));
+ if (input == nullptr) {
+ ALOGE("Failed to link to input service");
+ } else {
+ mInputFlinger = interface_cast<IInputFlinger>(input);
+ }
if (mVrFlinger) {
mVrFlinger->OnBootFinished();
@@ -512,11 +554,20 @@
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& performanceRefreshRate =
+ mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE);
+
+ if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) {
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
+ } else {
+ setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+ }
+ }));
}
uint32_t SurfaceFlinger::getNewTexture() {
@@ -541,268 +592,127 @@
}
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));
+ std::lock_guard lock(mTexturePoolMutex);
+ // We don't change the pool size, so the fix-up logic in postComposition will decide whether
+ // to actually delete this or not based on mTexturePoolSize
+ mTexturePool.push_back(texture);
+ ATRACE_INT("TexturePoolSize", mTexturePool.size());
}
-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); },
+ mRefreshRateConfigs);
+ 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);
+ renderEngineFeature |=
+ (enable_protected_contents(false) ? renderengine::RenderEngine::ENABLE_PROTECTED_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);
+ mScheduler->setChangeRefreshRateCallback(
+ [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(type, event);
+ });
+ mScheduler->setGetVsyncPeriodCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ return getVsyncPeriod();
+ });
- // 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;
- }
+ mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
+ mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
ALOGV("Done initializing");
}
@@ -819,6 +729,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 +745,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 +787,51 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
- Vector<DisplayInfo>* configs) {
- if (configs == nullptr || display.get() == nullptr) {
+status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
+ Vector<DisplayInfo>* configs) {
+ if (!displayToken || !configs) {
return BAD_VALUE;
}
- if (!display.get())
+ Mutex::Autolock lock(mStateLock);
+
+ 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 +848,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 +864,12 @@
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;
+ const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
+ const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
+ info.appVsyncOffset = offset.late.app;
// 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 +883,12 @@
//
// 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() - offset.late.sf + 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,209 +899,229 @@
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 ActiveConfigInfo& info) {
+ ATRACE_CALL();
- sp<const DisplayDevice> device(getDisplayDevice(display));
- if (device != nullptr) {
- return device->getActiveConfig();
- }
+ // 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 prevConfig = mDesiredActiveConfig.event;
+ mDesiredActiveConfig = info;
+ mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
- return BAD_VALUE;
+ if (!mDesiredActiveConfigChanged) {
+ // This will trigger HWC refresh without resetting the idle timer.
+ repaintEverythingForHWC();
+ // Start receiving vsync samples now, so that we can detect a period
+ // switch.
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mPhaseOffsets->setRefreshRateType(info.type);
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.onRefreshRateChangeInitiated();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
+ }
+ mDesiredActiveConfigChanged = true;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+ }
}
-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();
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_CALL();
- if (mode == currentMode) {
- ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
+ std::vector<int32_t> allowedConfig;
+ allowedConfig.push_back(mode);
+
+ return setAllowedDisplayConfigs(displayToken, allowedConfig);
+}
+
+void SurfaceFlinger::setActiveConfigInternal() {
+ ATRACE_CALL();
+
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display) {
return;
}
- if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- ALOGW("Trying to set config for virtual display");
- return;
- }
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId);
- hw->setActiveConfig(mode);
- getHwComposer().setActiveConfig(type, mode);
+ display->setActiveConfig(mUpcomingActiveConfig.configId);
+
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ 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);
+ }
}
-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);
- }
+bool SurfaceFlinger::performSetActiveConfig() {
+ ATRACE_CALL();
+ if (mCheckPendingFence) {
+ if (previousFrameMissed()) {
+ // fence has not signaled yet. wait for the next invalidate
+ mEventQueue->invalidate();
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 = getDefaultDisplayDeviceLocked();
+ 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);
+ mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
+ 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 (!isDisplayConfigAllowed(desiredActiveConfig.configId)) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
+ mDesiredActiveConfig.configId = display->getActiveConfig();
+ mDesiredActiveConfigChanged = false;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ 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;
+ mEventQueue->invalidate();
+ return false;
+}
+
+status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
+ Vector<ColorMode>* outColorModes) {
+ if (!displayToken || !outColorModes) {
+ return BAD_VALUE;
}
std::vector<ColorMode> modes;
+ bool isInternalDisplay = false;
{
- 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);
+ isInternalDisplay = displayId == getInternalDisplayIdLocked();
}
outColorModes->clear();
- std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
+
+ // If it's built-in display and the configuration claims it's not wide color capable,
+ // filter out all wide color modes. The typical reason why this happens is that the
+ // hardware is not good enough to support GPU composition of wide color, and thus the
+ // OEMs choose to disable this capability.
+ if (isInternalDisplay && !hasWideColorDisplay) {
+ std::remove_copy_if(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes),
+ isWideColorMode);
+ } else {
+ std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
+ }
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 +1137,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 +1159,109 @@
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;
+ }
+
+ // Use hasWideColorDisplay to override built-in display.
+ const auto displayId = display->getId();
+ if (displayId && displayId == getInternalDisplayIdLocked()) {
+ *outIsWideColorDisplay = hasWideColorDisplay;
+ return NO_ERROR;
+ }
+ *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 +1281,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 +1298,84 @@
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);
+}
+
+status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
+ PowerHint powerHint = static_cast<PowerHint>(hintId);
+
+ if (powerHint == PowerHint::INTERACTION) {
+ mScheduler->notifyTouchEvent();
+ }
+
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
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));
}
// ----------------------------------------------------------------------------
@@ -1314,10 +1385,12 @@
}
void SurfaceFlinger::signalTransaction() {
+ mScheduler->resetIdleTimer();
mEventQueue->invalidate();
}
void SurfaceFlinger::signalLayerUpdate() {
+ mScheduler->resetIdleTimer();
mEventQueue->invalidate();
}
@@ -1346,92 +1419,39 @@
} 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);
+ bool periodChanged = false;
+ mScheduler->addResyncSample(timestamp, &periodChanged);
+ if (periodChanged) {
+ mVsyncModulator.onRefreshRateChangeDetected();
}
}
@@ -1440,9 +1460,37 @@
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) {
+ return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
+}
+
+void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display || mBootStage != BootStage::FINISHED) {
+ return;
+ }
+ ATRACE_CALL();
+
+ // Don't do any updating if the current fps is the same as the new one.
+ const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate);
+ if (!refreshRateConfig) {
+ ALOGV("Skipping refresh rate change request for unsupported rate.");
+ return;
+ }
+
+ const int desiredConfigId = refreshRateConfig->configId;
+
+ if (!isDisplayConfigAllowed(desiredConfigId)) {
+ ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
+ return;
+ }
+
+ setDesiredActiveConfig({refreshRate, 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 +1504,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 +1514,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,60 +1561,117 @@
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);
}
-void SurfaceFlinger::onMessageReceived(int32_t what) {
+bool SurfaceFlinger::previousFrameMissed() NO_THREAD_SAFETY_ANALYSIS {
+ // We are storing the last 2 present fences. If sf's phase offset is to be
+ // woken up before the actual vsync but targeting the next vsync, we need to check
+ // fence N-2
+ const sp<Fence>& fence =
+ mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
+ ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
+
+ return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
+}
+
+void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
- bool frameMissed = !mHadClientComposition &&
- mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getSignalTime() ==
- Fence::SIGNAL_TIME_PENDING);
+ bool frameMissed = previousFrameMissed();
+ 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->updateFpsBasedOnContent();
+ }
+
+ if (performSetActiveConfig()) {
+ break;
+ }
+
+ // For now, only propagate backpressure when missing a hwc frame.
+ if (hwcFrameMissed && !gpuFrameMissed) {
if (mPropagateBackpressure) {
signalLayerUpdate();
break;
@@ -1579,6 +1685,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 +1706,25 @@
}
bool SurfaceFlinger::handleMessageTransaction() {
- uint32_t transactionFlags = peekTransactionFlags();
- if (transactionFlags) {
- handleTransaction(transactionFlags);
- return true;
- }
- return false;
-}
-
-bool SurfaceFlinger::handleMessageInvalidate() {
ATRACE_CALL();
- return handlePageFlip();
+ uint32_t transactionFlags = peekTransactionFlags();
+
+ bool flushedATransaction = flushTransactionQueues();
+
+ bool runHandleTransaction = transactionFlags &&
+ ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+
+ if (runHandleTransaction) {
+ handleTransaction(eTransactionMask);
+ } else {
+ getTransactionFlags(eTransactionFlushNeeded);
+ }
+
+ if (transactionFlushNeeded()) {
+ setTransactionFlags(eTransactionFlushNeeded);
+ }
+
+ return runHandleTransaction;
}
void SurfaceFlinger::handleMessageRefresh() {
@@ -1614,111 +1732,213 @@
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);
+ }
+
+ 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 +1948,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 +1971,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 +2016,35 @@
}
// |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);
+ mPreviousPresentFences[1] = mPreviousPresentFences[0];
+ mPreviousPresentFences[0] = displayDevice
+ ? getHwComposer().getPresentFence(*displayDevice->getId())
+ : Fence::NO_FENCE;
+ auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
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 +2052,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 +2062,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 +2078,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 +2105,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 {
@@ -1896,12 +2117,53 @@
{
std::lock_guard lock(mTexturePoolMutex);
- const size_t refillCount = mTexturePoolSize - mTexturePool.size();
- if (refillCount > 0) {
+ if (mTexturePool.size() < mTexturePoolSize) {
+ const size_t refillCount = mTexturePoolSize - mTexturePool.size();
const size_t offset = mTexturePool.size();
mTexturePool.resize(mTexturePoolSize);
getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset);
ATRACE_INT("TexturePoolSize", mTexturePool.size());
+ } else if (mTexturePool.size() > mTexturePoolSize) {
+ const size_t deleteCount = mTexturePool.size() - mTexturePoolSize;
+ const size_t offset = mTexturePoolSize;
+ getRenderEngine().deleteTextures(deleteCount, mTexturePool.data() + offset);
+ mTexturePool.resize(mTexturePoolSize);
+ ATRACE_INT("TexturePoolSize", mTexturePool.size());
+ }
+ }
+
+ mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
+
+ // Lock the mStateLock in case SurfaceFlinger is in the middle of applying a transaction.
+ // If we do not lock here, a callback could be sent without all of its SurfaceControls and
+ // metrics.
+ {
+ Mutex::Autolock _l(mStateLock);
+ mTransactionCompletedThread.sendCallbacks();
+ }
+
+ if (mLumaSampling && mRegionSamplingThread) {
+ mRegionSamplingThread->notifyNewContent();
+ }
+
+ // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
+ // side-effect of getTotalSize(), so we check that again here
+ if (ATRACE_ENABLED()) {
+ ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
+ }
+}
+
+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());
}
}
}
@@ -1916,59 +2178,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 +2258,39 @@
// 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<DisplayDevice>& display,
+ Dataspace* outHdrDataSpace,
+ bool* outIsHdrClientComposition) 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;
+ *outIsHdrClientComposition = layer->getForceClientComposition(display);
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 +2306,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 +2316,25 @@
}
Dataspace hdrDataSpace;
- Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace);
+ bool isHdrClientComposition = false;
+ Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace, &isHdrClientComposition);
+
+ 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);
+ !profile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition;
if (isHdr) {
bestDataSpace = hdrDataSpace;
}
@@ -2048,177 +2353,112 @@
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;
+ 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 +2466,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);
}
}
}
@@ -2272,8 +2503,7 @@
State drawingState(mDrawingState);
Mutex::Autolock _l(mStateLock);
- const nsecs_t now = systemTime();
- mDebugInTransaction = now;
+ mDebugInTransaction = systemTime();
// Here we're guaranteed that some transaction flags are set
// so we can call handleTransactionLocked() unconditionally.
@@ -2285,74 +2515,41 @@
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
- mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
invalidateHwcGeometry();
// 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 +2558,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 +2622,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 +2659,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 +2682,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 +2714,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 +2739,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 +2759,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);
}
}
}
@@ -2601,7 +2794,7 @@
* (perform the transaction for each of them if needed)
*/
- if (transactionFlags & eTraversalNeeded) {
+ if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2609,7 +2802,12 @@
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
mVisibleRegionsDirty = true;
+
+ if (flags & Layer::eInputInfoChanged) {
+ mInputInfoChanged = true;
+ }
});
+ mTraversalNeededMainThread = false;
}
/*
@@ -2641,7 +2839,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 +2852,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 +2906,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 = 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 +2997,18 @@
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);
+ }
+
+ // If the layer has been removed and has no parent, then it will not be reachable
+ // when traversing layers on screen. Add the layer to the offscreenLayers set to
+ // ensure we can copy its current to drawing state.
+ if (!l->getParent()) {
+ mOffscreenLayers.emplace(l.get());
+ }
}
mLayersPendingRemoval.clear();
}
@@ -2752,24 +3017,68 @@
// 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();
+
+ // If the layer can be reached when traversing mDrawingState, then the layer is no
+ // longer offscreen. Remove the layer from the offscreenLayer set.
+ if (mOffscreenLayers.count(layer)) {
+ mOffscreenLayers.erase(layer);
+ }
+ });
+
+ commitOffscreenLayers();
});
+
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::commitOffscreenLayers() {
+ for (Layer* offscreenLayer : mOffscreenLayers) {
+ offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+ uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
+ if (!trFlags) return;
+
+ layer->doTransaction(0);
+ layer->commitChildList();
+ });
+ }
+}
+
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 +3090,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 +3127,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 +3147,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,16 +3214,17 @@
}
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);
}
}
}
bool SurfaceFlinger::handlePageFlip()
{
+ ATRACE_CALL();
ALOGV("handlePageFlip");
nsecs_t latchTime = systemTime();
@@ -2930,11 +3243,14 @@
// 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 {
+ ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
}
} else {
@@ -2942,12 +3258,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 +3298,104 @@
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 = 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);
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);
+ }
+ if (needsProtected != display->getRenderSurface()->isProtected() &&
+ needsProtected == renderEngine.isProtected()) {
+ 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();
+ clientCompositionDisplay.orientation = displayState.orientation;
- // 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 = displayState.colorTransformMat;
}
}
@@ -3098,32 +3404,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,42 +3459,79 @@
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(), /*useFramebufferCache=*/true, std::move(fd),
+ readyFence);
+ } else if (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)
-{
+status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
+ const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
+ const sp<IBinder>& parentHandle,
+ const sp<Layer>& parentLayer, bool addToCurrentState) {
// add this layer to the current state list
{
Mutex::Autolock _l(mStateLock);
+ sp<Layer> parent;
+ if (parentHandle != nullptr) {
+ parent = fromHandle(parentHandle);
+ if (parent == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+ } else {
+ parent = parentLayer;
+ }
+
if (mNumLayers >= MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers,
MAX_LAYERS);
return NO_MEMORY;
}
- if (parent == nullptr) {
+
+ mLayersByLocalBinderToken.emplace(handle->localBinder(), lbc);
+
+ 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 +3544,6 @@
mMaxGraphicBufferProducerListSize, mNumLayers);
}
mLayersAdded = true;
- mNumLayers++;
}
// attach this layer to the client
@@ -3192,75 +3552,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 +3574,51 @@
return old;
}
+bool SurfaceFlinger::flushTransactionQueues() {
+ // to prevent onHandleDestroyed from being called while the lock is held,
+ // we must keep a copy of the transactions (specifically the composer
+ // states) around outside the scope of the lock
+ std::vector<const TransactionState> transactions;
+ bool flushedATransaction = false;
+ {
+ Mutex::Autolock _l(mStateLock);
+
+ auto it = mTransactionQueues.begin();
+ while (it != mTransactionQueues.end()) {
+ auto& [applyToken, transactionQueue] = *it;
+
+ while (!transactionQueue.empty()) {
+ const auto& transaction = transactionQueue.front();
+ if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime,
+ transaction.states)) {
+ setTransactionFlags(eTransactionFlushNeeded);
+ break;
+ }
+ transactions.push_back(transaction);
+ applyTransactionState(transaction.states, transaction.displays, transaction.flags,
+ mPendingInputWindowCommands, transaction.desiredPresentTime,
+ transaction.buffer, transaction.callback,
+ transaction.postTime, transaction.privileged,
+ /*isMainThread*/ true);
+ transactionQueue.pop();
+ flushedATransaction = true;
+ }
+
+ if (transactionQueue.empty()) {
+ it = mTransactionQueues.erase(it);
+ mTransactionCV.broadcast();
+ } else {
+ it = std::next(it, 1);
+ }
+ }
+ }
+ return flushedATransaction;
+}
+
+bool SurfaceFlinger::transactionFlushNeeded() {
+ 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,23 +3641,90 @@
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,
+ const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) {
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
+ auto itr = mTransactionQueues.find(applyToken);
+ // if this is an animation frame, wait until prior animation frame has
+ // been applied by SF
+ if (flags & eAnimation) {
+ while (itr != mTransactionQueues.end()) {
+ status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ ALOGW_IF(err == TIMED_OUT,
+ "setTransactionState timed out "
+ "waiting for animation frame to apply");
+ break;
+ }
+ itr = mTransactionQueues.find(applyToken);
+ }
+ }
+ if (itr != mTransactionQueues.end() ||
+ !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+ mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
+ uncacheBuffer, listenerCallbacks, postTime,
+ privileged);
+ setTransactionFlags(eTransactionFlushNeeded);
+ return;
+ }
+
+ applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
+ uncacheBuffer, listenerCallbacks, postTime, privileged);
+}
+
+void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands,
+ const int64_t desiredPresentTime,
+ const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ const int64_t postTime, bool privileged,
+ bool isMainThread) {
+ 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.
- while (mAnimTransactionPending) {
+ while (!isMainThread && mAnimTransactionPending) {
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
@@ -3323,16 +3741,32 @@
transactionFlags |= setDisplayStateLocked(display);
}
- for (const ComposerState& state : states) {
- transactionFlags |= setClientStateLocked(state);
+ // In case the client has sent a Transaction that should receive callbacks but without any
+ // SurfaceControls that should be included in the callback, send the listener and callbackIds
+ // to the callback thread so it can send an empty callback
+ if (!listenerCallbacks.empty()) {
+ mTransactionCompletedThread.run();
+ }
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ mTransactionCompletedThread.addCallback(listener, callbackIds);
}
- // 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).
+ uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- setDestroyStateLocked(state);
+ clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
+ postTime, privileged);
+ }
+
+ // If the state doesn't require a traversal and there are callbacks, send them now
+ if (!(clientStateFlags & eTraversalNeeded) && !listenerCallbacks.empty()) {
+ mTransactionCompletedThread.sendCallbacks();
+ }
+ transactionFlags |= clientStateFlags;
+
+ transactionFlags |= addInputWindowCommands(inputWindowCommands);
+
+ if (uncacheBuffer.isValid()) {
+ ClientCache::getInstance().erase(uncacheBuffer);
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -3344,15 +3778,21 @@
transactionFlags = eTransactionNeeded;
}
+ // If we are on the main thread, we are about to preform a traversal. Clear the traversal bit
+ // so we don't have to wake up again next frame to preform an uneeded traversal.
+ if (isMainThread && (transactionFlags & eTraversalNeeded)) {
+ transactionFlags = transactionFlags & (~eTraversalNeeded);
+ mTraversalNeededMainThread = true;
+ }
+
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
}
// 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 +3803,91 @@
if (flags & eAnimation) {
mAnimTransactionPending = true;
}
- while (mTransactionPending) {
+ if (mPendingInputWindowCommands.syncInputWindows) {
+ mPendingSyncInputWindows = true;
+ }
+
+ // applyTransactionState can be called by either the main SF thread or by
+ // another process through setTransactionState. While a given process may wish
+ // to wait on synchronous transactions, the main SF thread should never
+ // be blocked. Therefore, we only wait if isMainThread is false.
+ while (!isMainThread && (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,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
+ bool privileged) {
const layer_state_t& s = composerState.state;
sp<Client> client(static_cast<Client*>(composerState.client.get()));
@@ -3447,20 +3896,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 +3962,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 +3988,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 +3999,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 +4026,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,63 +4065,159 @@
// 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::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
+ }
+ }
+ bool bufferChanged = what & layer_state_t::eBufferChanged;
+ bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
+ sp<GraphicBuffer> buffer;
+ if (bufferChanged && cacheIdChanged) {
+ ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+ buffer = s.buffer;
+ } else if (cacheIdChanged) {
+ buffer = ClientCache::getInstance().get(s.cachedBuffer);
+ } else if (bufferChanged) {
+ buffer = s.buffer;
+ }
+ if (buffer) {
+ if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ 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,
+ const sp<IBinder>& parentHandle,
+ const sp<Layer>& parentLayer) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
return BAD_VALUE;
}
+ ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
+ "Expected only one of parentLayer or parentHandle to be non-null. "
+ "Programmer error?");
+
status_t result = NO_ERROR;
sp<Layer> layer;
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, InputWindowInfo::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 +4228,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, parentHandle, parentLayer,
+ addToCurrentState);
if (result != NO_ERROR) {
return result;
}
@@ -3719,15 +4266,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 +4289,97 @@
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);
+
+ auto it = mLayersByLocalBinderToken.begin();
+ while (it != mLayersByLocalBinderToken.end()) {
+ if (it->second == layer) {
+ it = mLayersByLocalBinderToken.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ 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 +4387,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 +4452,61 @@
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);
+ 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 +4514,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 +4523,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 +4567,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 +4609,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 +4623,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 +4683,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 +4693,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,93 +4719,132 @@
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 built-in 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");
}
-LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet) const {
+LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet,
+ uint32_t traceFlags) const {
LayersProto layersProto;
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const State& state = useDrawing ? mDrawingState : mCurrentState;
state.traverseInZOrder([&](Layer* layer) {
LayerProto* layerProto = layersProto.add_layers();
- layer->writeToProto(layerProto, stateSet);
+ layer->writeToProto(layerProto, stateSet, traceFlags);
});
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 +4859,9 @@
appendGuiConfigString(result);
result.append("\n");
+ result.append("\nDisplay identification data:\n");
+ dumpDisplayIdentificationData(result);
+
result.append("\nWide-Color information:\n");
dumpWideColorInfo(result);
@@ -4344,63 +4869,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 +4930,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 +4965,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 +4986,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 +5000,33 @@
*/
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");
+
+ result.append(mTimeStats->miniDump());
+ result.append("\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 +5054,78 @@
}
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:
+ case NOTIFY_POWER_HINT: {
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 +5134,45 @@
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;
+ }
+ case CAPTURE_SCREEN_BY_ID: {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int uid = ipc->getCallingUid();
+ if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
+ return OK;
+ }
+ 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 1034 are currently used for backdoors. The code
+ // in onTransact verifies that the user is root, and has access to use SF.
+ if (code >= 1000 && code <= 1035) {
+ 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;
@@ -4653,13 +5219,13 @@
}
case 1008: // toggle use of hw composer
n = data.readInt32();
- mDebugDisableHWC = n ? 1 : 0;
+ mDebugDisableHWC = n != 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
case 1009: // toggle use of transform hint
n = data.readInt32();
- mDebugDisableTransformHint = n ? 1 : 0;
+ mDebugDisableTransformHint = n != 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -4671,8 +5237,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,22 +5301,23 @@
// 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: {
n = data.readInt32();
- mForceFullDamage = static_cast<bool>(n);
+ mForceFullDamage = n != 0;
return NO_ERROR;
}
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 +5350,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 +5385,138 @@
}
// 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;
+ }
+ // Set trace flags
+ case 1033: {
+ n = data.readUint32();
+ ALOGD("Updating trace flags to 0x%x", n);
+ mTracing.setTraceFlags(n);
+ reply->writeInt32(NO_ERROR);
+ return NO_ERROR;
+ }
+ case 1034: {
+ // TODO(b/129297325): expose this via developer menu option
+ n = data.readInt32();
+ if (n && !mRefreshRateOverlay) {
+ RefreshRateType type;
+ {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ type = mDesiredActiveConfig.type;
+ }
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
+ mRefreshRateOverlay->changeRefreshRate(type);
+ } else if (!n) {
+ mRefreshRateOverlay.reset();
+ }
+ return NO_ERROR;
+ }
+ case 1035: {
+ n = data.readInt32();
+ mDebugDisplayConfigSetByBackdoor = false;
+ if (n >= 0) {
+ const auto displayToken = getInternalDisplayToken();
+ status_t result = setAllowedDisplayConfigs(displayToken, {n});
+ if (result != NO_ERROR) {
+ return result;
+ }
+ mDebugDisplayConfigSetByBackdoor = true;
+ }
+ return NO_ERROR;
+ }
}
}
return err;
}
void SurfaceFlinger::repaintEverything() {
- android_atomic_or(1, &mRepaintEverything);
+ mRepaintEverything = true;
signalTransaction();
}
+void SurfaceFlinger::repaintEverythingForHWC() {
+ mRepaintEverything = true;
+ mEventQueue->invalidate();
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -4848,66 +5530,141 @@
const int mApi;
};
-status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- bool& outCapturedSecureLayers, Rect sourceCrop,
- uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ,
- int32_t maxLayerZ, bool useIdentityTransform,
+status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
+ sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
+ const Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight,
+ bool useIdentityTransform,
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,
- captureSecureLayers);
+ 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,
- outCapturedSecureLayers);
+ auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
+ std::placeholders::_1);
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
+ useIdentityTransform, outCapturedSecureLayers);
}
-status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
- sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
- float frameScale, bool childrenOnly) {
+static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ case ColorMode::DISPLAY_BT2020:
+ return Dataspace::DISPLAY_P3;
+ default:
+ return Dataspace::V0_SRGB;
+ }
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
+ const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
+ if (displayToken) {
+ return getDisplayDeviceLocked(displayToken);
+ }
+ // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
+ // may not have a displayId.
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getLayerStack() == displayOrLayerStack) {
+ return display;
+ }
+ }
+ return nullptr;
+}
+
+status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
+ sp<GraphicBuffer>* outBuffer) {
+ sp<DisplayDevice> display;
+ uint32_t width;
+ uint32_t height;
+ ui::Transform::orientation_flags captureOrientation;
+ {
+ Mutex::Autolock _l(mStateLock);
+ display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ if (!display) {
+ return BAD_VALUE;
+ }
+
+ width = uint32_t(display->getViewport().width());
+ height = uint32_t(display->getViewport().height());
+
+ captureOrientation = fromSurfaceComposerRotation(
+ static_cast<ISurfaceComposer::Rotation>(display->getOrientation()));
+ if (captureOrientation == ui::Transform::orientation_flags::ROT_90) {
+ captureOrientation = ui::Transform::orientation_flags::ROT_270;
+ } else if (captureOrientation == ui::Transform::orientation_flags::ROT_270) {
+ captureOrientation = ui::Transform::orientation_flags::ROT_90;
+ }
+ *outDataspace =
+ pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
+ }
+
+ DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
+ false /* captureSecureLayers */);
+
+ auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
+ std::placeholders::_1);
+ bool ignored = false;
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
+ false /* useIdentityTransform */,
+ ignored /* outCapturedSecureLayers */);
+}
+
+status_t SurfaceFlinger::captureLayers(
+ const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
+ const Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
+ 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();
@@ -4920,8 +5677,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); }
@@ -4938,11 +5698,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();
}
}
@@ -4954,41 +5715,57 @@
// 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;
const bool mChildrenOnly;
};
- auto layerHandle = reinterpret_cast<Layer::Handle*>(layerHandleBinder.get());
- auto parent = layerHandle->owner.promote();
-
- if (parent == nullptr || parent->isPendingRemoval()) {
- ALOGE("captureLayers called with a removed parent");
- return NAME_NOT_FOUND;
- }
-
- const int uid = IPCThreadState::self()->getCallingUid();
- const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
- ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
- return PERMISSION_DENIED;
- }
-
+ int reqWidth = 0;
+ int reqHeight = 0;
+ sp<Layer> parent;
Rect crop(sourceCrop);
- if (sourceCrop.width() <= 0) {
- crop.left = 0;
- crop.right = parent->getCurrentState().active.w;
- }
+ std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
- if (sourceCrop.height() <= 0) {
- crop.top = 0;
- crop.bottom = parent->getCurrentState().active.h;
- }
+ {
+ Mutex::Autolock _l(mStateLock);
- int32_t reqWidth = crop.width() * frameScale;
- int32_t reqHeight = crop.height() * frameScale;
+ parent = fromHandle(layerHandleBinder);
+ if (parent == nullptr || parent->isRemovedFromCurrentState()) {
+ ALOGE("captureLayers called with an invalid or removed parent");
+ return NAME_NOT_FOUND;
+ }
+
+ const int uid = IPCThreadState::self()->getCallingUid();
+ const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+ if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
+ ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
+ return PERMISSION_DENIED;
+ }
+
+ if (sourceCrop.width() <= 0) {
+ crop.left = 0;
+ crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+ }
+
+ if (sourceCrop.height() <= 0) {
+ crop.top = 0;
+ crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+ }
+ reqWidth = crop.width() * frameScale;
+ reqHeight = crop.height() * frameScale;
+
+ for (const auto& handle : excludeHandles) {
+ sp<Layer> excludeLayer = fromHandle(handle);
+ if (excludeLayer != nullptr) {
+ excludeLayers.emplace(excludeLayer);
+ } else {
+ ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
+ return NAME_NOT_FOUND;
+ }
+ }
+ } // mStateLock
// really small crop or frameScale
if (reqWidth <= 0) {
@@ -4998,35 +5775,58 @@
reqHeight = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly);
-
- auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) {
+ LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+ auto traverseLayers = [parent, childrenOnly,
+ &excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
} else if (childrenOnly && layer == parent.get()) {
return;
}
+
+ sp<Layer> p = layer;
+ while (p != nullptr) {
+ if (excludeLayers.count(p) != 0) {
+ return;
+ }
+ p = p->getParent();
+ }
+
visitor(layer);
});
};
+
bool outCapturedSecureLayers = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, false,
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
outCapturedSecureLayers);
}
status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
sp<GraphicBuffer>* outBuffer,
+ const ui::PixelFormat reqPixelFormat,
bool useIdentityTransform,
bool& outCapturedSecureLayers) {
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,
+ outCapturedSecureLayers);
+}
+
+status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+ TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer,
+ bool useIdentityTransform,
+ bool& outCapturedSecureLayers) {
// This mutex protects syncFd and captureResult for communication of the return values from the
// main thread back to this Binder thread
std::mutex captureMutex;
@@ -5038,7 +5838,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) {
@@ -5053,8 +5853,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,
outCapturedSecureLayers);
});
@@ -5070,14 +5870,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;
}
@@ -5091,39 +5891,106 @@
}
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();
+ const auto transform = renderArea.getTransform();
+ const auto sourceCrop = renderArea.getSourceCrop();
- // 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);
+ clientCompositionDisplay.clip = sourceCrop;
+ clientCompositionDisplay.globalTransform = transform.asMatrix4();
- // set-up our viewport
- engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap,
- rotation);
- engine.disableTexturing();
+ // Now take into account the rotation flag. We append a transform that
+ // rotates the layer stack about the origin, then translate by buffer
+ // boundaries to be in the right quadrant.
+ mat4 rotMatrix;
+ 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 = renderArea.getBounds().getHeight();
+ break;
+ case ui::Transform::ROT_180:
+ rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
+ displacementY = renderArea.getBounds().getWidth();
+ displacementX = renderArea.getBounds().getHeight();
+ break;
+ case ui::Transform::ROT_270:
+ rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
+ displacementY = renderArea.getBounds().getWidth();
+ 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]);
+
+ // 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);
+
+ mat4 clipTransform = displacementMat * rotMatrix;
+ clientCompositionDisplay.globalTransform =
+ clipTransform * clientCompositionDisplay.globalTransform;
+
+ 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,
+ /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+
+ *outSyncFd = drawFence.release();
}
status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
@@ -5145,64 +6012,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();
}
// ---------------------------------------------------------------------------
@@ -5215,22 +6033,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()) {
@@ -5241,8 +6054,94 @@
}
}
-}; // namespace android
+void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
+ const std::vector<int32_t>& allowedConfigs) {
+ if (!display->isPrimary()) {
+ return;
+ }
+ ALOGV("Updating allowed configs");
+ mAllowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(), allowedConfigs.end());
+
+ // Set the highest allowed config by iterating backwards on available refresh rates
+ const auto& refreshRates = mRefreshRateConfigs.getRefreshRates();
+ for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
+ if (iter->second && isDisplayConfigAllowed(iter->second->configId)) {
+ ALOGV("switching to config %d", iter->second->configId);
+ setDesiredActiveConfig(
+ {iter->first, iter->second->configId, Scheduler::ConfigEvent::Changed});
+ break;
+ }
+ }
+}
+
+status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) {
+ ATRACE_CALL();
+
+ if (!displayToken || allowedConfigs.empty()) {
+ return BAD_VALUE;
+ }
+
+ if (mDebugDisplayConfigSetByBackdoor) {
+ // ignore this request as config is overridden by backdoor
+ return NO_ERROR;
+ }
+
+ postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set allowed display configs for invalid display token %p",
+ displayToken.get());
+ } else if (display->isVirtual()) {
+ ALOGW("Attempt to set allowed display configs for virtual display");
+ } else {
+ setAllowedDisplayConfigsInternal(display, allowedConfigs);
+ }
+ }));
+
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ std::vector<int32_t>* outAllowedConfigs) {
+ ATRACE_CALL();
+
+ if (!displayToken || !outAllowedConfigs) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ if (display->isPrimary()) {
+ outAllowedConfigs->assign(mAllowedDisplayConfigs.begin(), mAllowedDisplayConfigs.end());
+ }
+
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
+ mFlinger->setInputWindowsFinished();
+}
+
+sp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
+ BBinder *b = handle->localBinder();
+ if (b == nullptr) {
+ return nullptr;
+ }
+ auto it = mLayersByLocalBinderToken.find(b);
+ if (it != mLayersByLocalBinderToken.end()) {
+ return it->second.promote();
+ }
+ return nullptr;
+}
+
+} // namespace android
#if defined(__gl_h_)
#error "don't include gl/gl.h in this file"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fce877c..c5c4103 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -14,114 +14,104 @@
* limitations under the License.
*/
-#ifndef ANDROID_SURFACE_FLINGER_H
-#define ANDROID_SURFACE_FLINGER_H
+#pragma once
-#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 "Barrier.h"
+#include "ClientCache.h"
#include "DisplayDevice.h"
-#include "DispSync.h"
-#include "EventThread.h"
+#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/PowerAdvisor.h"
+#include "Effects/Daltonizer.h"
#include "FrameTracker.h"
#include "LayerStats.h"
#include "LayerVector.h"
-#include "MessageQueue.h"
-#include "SurfaceInterceptor.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateStats.h"
+#include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncModulator.h"
+#include "SurfaceFlingerFactory.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 <unordered_set>
#include <utility>
-#include "RenderArea.h"
-
-#include <layerproto/LayerProtoHeader.h>
using namespace android::surfaceflinger;
namespace android {
-// ---------------------------------------------------------------------------
-
class Client;
-class ColorLayer;
-class DisplayEventConnection;
-class EventControlThread;
class EventThread;
-class IGraphicBufferConsumer;
+class HWComposer;
class IGraphicBufferProducer;
+class IInputFlinger;
class InjectVSyncSource;
class Layer;
-class Surface;
-class SurfaceFlingerBE;
-class VSyncSource;
+class MessageBase;
+class RefreshRateOverlay;
+class RegionSamplingThread;
+class TimeStats;
-namespace impl {
-class EventThread;
-} // namespace impl
+namespace compositionengine {
+class DisplaySurface;
+} // namespace compositionengine
-namespace RE {
+namespace renderengine {
class RenderEngine;
-}
-
-typedef std::function<void(const LayerVector::Visitor&)> TraverseLayersFunction;
+} // namespace renderengine
namespace dvr {
class VrFlinger;
} // namespace dvr
-// ---------------------------------------------------------------------------
-
enum {
- eTransactionNeeded = 0x01,
- eTraversalNeeded = 0x02,
+ eTransactionNeeded = 0x01,
+ eTraversalNeeded = 0x02,
eDisplayTransactionNeeded = 0x04,
eDisplayLayerStackChanged = 0x08,
- eTransactionMask = 0x0f,
+ eTransactionFlushNeeded = 0x10,
+ eTransactionMask = 0x1f,
};
enum class DisplayColorSetting : int32_t {
@@ -130,53 +120,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;
@@ -186,35 +136,28 @@
// Only accessed from the main thread.
struct CompositePresentTime {
- nsecs_t composite { -1 };
- std::shared_ptr<FenceTime> display { FenceTime::NO_FENCE };
+ nsecs_t composite = -1;
+ std::shared_ptr<FenceTime> display = FenceTime::NO_FENCE;
};
std::queue<CompositePresentTime> mCompositePresentTimes;
static const size_t NUM_BUCKETS = 8; // < 1-7, 7+
- nsecs_t mFrameBuckets[NUM_BUCKETS];
- nsecs_t mTotalTime;
- std::atomic<nsecs_t> mLastSwapTime;
+ nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
+ nsecs_t mTotalTime = 0;
+ std::atomic<nsecs_t> mLastSwapTime = 0;
// Double- vs. triple-buffering stats
struct BufferingStats {
- BufferingStats()
- : numSegments(0),
- totalTime(0),
- twoBufferTime(0),
- doubleBufferedTime(0),
- tripleBufferedTime(0) {}
-
- size_t numSegments;
- nsecs_t totalTime;
+ size_t numSegments = 0;
+ nsecs_t totalTime = 0;
// "Two buffer" means that a third buffer was never used, whereas
// "double-buffered" means that on average the segment only used two
// buffers (though it may have used a third for some part of the
// segment)
- nsecs_t twoBufferTime;
- nsecs_t doubleBufferedTime;
- nsecs_t tripleBufferedTime;
+ nsecs_t twoBufferTime = 0;
+ nsecs_t doubleBufferedTime = 0;
+ nsecs_t tripleBufferedTime = 0;
};
mutable Mutex mBufferingStatsMutex;
std::unordered_map<std::string, BufferingStats> mBufferingStats;
@@ -222,10 +165,9 @@
// The composer sequence id is a monotonically increasing integer that we
// use to differentiate callbacks from different hardware composer
// instances. Each hardware composer instance gets a different sequence id.
- int32_t mComposerSequenceId;
+ int32_t mComposerSequenceId = 0;
};
-
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
private IBinder::DeathRecipient,
@@ -279,23 +221,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 +259,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 +268,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 +291,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 +300,35 @@
// 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(Layer* layer) {
+ mNumLayers--;
+ mOffscreenLayers.erase(layer);
+ }
+
+ TransactionCompletedThread& getTransactionCompletedThread() {
+ return mTransactionCompletedThread;
+ }
+
+ sp<Layer> fromHandle(const sp<IBinder>& handle) REQUIRES(mStateLock);
+
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 Client;
+ friend class Layer;
friend class MonitoredProducer;
+ friend class RefreshRateOverlay;
+ friend class RegionSamplingThread;
+ friend class SurfaceTracing;
// For unit tests
friend class TestableSurfaceFlinger;
@@ -363,6 +338,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,73 +376,110 @@
/* ------------------------------------------------------------------------
* 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,
- bool& outCapturedSecureLayers,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
- ISurfaceComposer::Rotation rotation, bool captureSecureLayers);
- 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, const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks) 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,
+ bool& outCapturedSecureLayers, 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 captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
+ sp<GraphicBuffer>* outBuffer) override;
+ status_t captureLayers(
+ const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
+ const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+ const Rect& sourceCrop,
+ const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& exclude,
+ 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;
+ 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;
+ status_t notifyPowerHint(int32_t hintId) 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,19 +491,37 @@
void signalLayerUpdate();
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);
- // called on the main thread in response to setPowerMode()
- void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode,
- bool stateLockHeld);
+ using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
- // Called on the main thread in response to setActiveColorMode()
- void setActiveColorModeInternal(const sp<DisplayDevice>& hw,
- ui::ColorMode colorMode,
- ui::Dataspace dataSpace,
- ui::RenderIntent renderIntent);
+ struct ActiveConfigInfo {
+ RefreshRateType type;
+ int configId;
+ Scheduler::ConfigEvent event;
+
+ bool operator!=(const ActiveConfigInfo& other) const {
+ return type != other.type || configId != other.configId || event != other.event;
+ }
+ };
+
+ // called on the main thread in response to initializeDisplays()
+ void onInitializeDisplays() REQUIRES(mStateLock);
+ // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
+ void setDesiredActiveConfig(const ActiveConfigInfo& info) 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() REQUIRES(mStateLock);
+ // called on the main thread in response to setPowerMode()
+ void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
+
+ // called on the main thread in response to setAllowedDisplayConfigs()
+ void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
+ const std::vector<int32_t>& allowedConfigs)
+ REQUIRES(mStateLock);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -501,8 +532,13 @@
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 setInputWindowsFinished();
void updateCursorAsync();
/* handlePageFlip - latch a new buffer if available and compute the dirty
@@ -514,59 +550,77 @@
/* ------------------------------------------------------------------------
* Transactions
*/
+ void applyTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands,
+ const int64_t desiredPresentTime,
+ const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ const int64_t postTime, bool privileged, bool isMainThread = false)
+ REQUIRES(mStateLock);
+ // Returns true if at least one transaction was flushed
+ bool flushTransactionQueues();
+ // Returns true if there is at least one transaction that needs to be flushed
+ bool transactionFlushNeeded();
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);
+ void commitOffscreenLayers();
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,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ 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,
+ const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr);
- 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);
+ status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
+ const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
+ const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
+ bool addToCurrentState);
+
+ // Traverse through all the layers and compute and cache its bounds.
+ void computeLayerBounds();
/* ------------------------------------------------------------------------
* Boot animation, on/off animations and screen capture
@@ -574,19 +628,26 @@
void startBootAnim();
+ using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+
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, bool& outCapturedSecureLayers);
+ status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
+ bool& outCapturedSecureLayers);
+ const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
bool forSystem, int* outSyncFd, bool& outCapturedSecureLayers);
- 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
@@ -605,38 +666,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
@@ -647,112 +706,199 @@
* 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* outHdrDataSpace) const;
+ ui::Dataspace getBestDataspace(const sp<DisplayDevice>& display, ui::Dataspace* outHdrDataSpace,
+ bool* outIsHdrClientComposition) 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(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock);
- /* ------------------------------------------------------------------------
+ bool isDisplayConfigAllowed(int32_t configId) REQUIRES(mStateLock);
+
+ /*
+ * 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;
+ }
+
+ bool previousFrameMissed();
+
+ /*
* 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;
- LayersProto dumpProtoInfo(LayerVector::StateSet stateSet) const;
- LayersProto dumpVisibleLayersProtoInfo(int32_t hwcId) const;
+ void dumpBufferingStats(std::string& result) const;
+ void dumpDisplayIdentificationData(std::string& result) const;
+ void dumpWideColorInfo(std::string& result) const;
+ LayersProto dumpProtoInfo(LayerVector::StateSet stateSet,
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL) 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
@@ -768,14 +914,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;
+ bool mTransactionPending = false;
+ bool mAnimTransactionPending = false;
+ SortedVector<sp<Layer>> mLayersPendingRemoval;
+ bool mTraversalNeededMainThread = false;
+
+ // guards access to the mDrawing state if tracing is enabled.
+ mutable std::mutex mDrawingStateLock;
// global color transform states
Daltonizer mDaltonizer;
@@ -787,45 +939,50 @@
size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
// protected by mStateLock (but we could use another lock)
- bool mLayersRemoved;
- bool mLayersAdded;
+ bool mLayersRemoved = false;
+ bool mLayersAdded = false;
- // 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;
+ const nsecs_t mBootTime = systemTime();
+ bool mGpuToCpuSupported = false;
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.
+ const 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;
- bool mGeometryInvalid;
- bool mAnimCompositionPending;
+ bool mVisibleRegionsDirty = false;
+ // Set during transaction commit stage to track if the input info for a layer has changed.
+ bool mInputInfoChanged = false;
+ bool mGeometryInvalid = false;
+ bool mAnimCompositionPending = false;
std::vector<sp<Layer>> mLayersWithQueuedFrames;
- sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+ // Tracks layers that need to update a display's dirty region.
+ std::vector<sp<Layer>> mLayersPendingRefresh;
+ std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, 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,
BOOTANIMATION,
FINISHED,
};
- BootStage mBootStage;
+ BootStage mBootStage = BootStage::BOOTLOADER;
struct HotplugEvent {
- hwc2_display_t display;
+ hwc2_display_t hwcDisplayId;
HWC2::Connection connection = HWC2::Connection::Invalid;
};
// protected by mStateLock
@@ -833,43 +990,46 @@
// 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;
+
+ // protected by mStateLock
+ std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken;
// don't use a lock for these, we don't care
- int mDebugRegion;
- int mDebugDisableHWC;
- int mDebugDisableTransformHint;
- volatile nsecs_t mDebugInSwapBuffers;
- nsecs_t mLastSwapBufferTime;
- volatile nsecs_t mDebugInTransaction;
- nsecs_t mLastTransactionTime;
- bool mForceFullDamage;
+ int mDebugRegion = 0;
+ bool mDebugDisableHWC = false;
+ bool mDebugDisableTransformHint = false;
+ volatile nsecs_t mDebugInTransaction = 0;
+ bool mForceFullDamage = false;
bool mPropagateBackpressure = true;
- std::unique_ptr<SurfaceInterceptor> mInterceptor =
- std::make_unique<impl::SurfaceInterceptor>(this);
- SurfaceTracing mTracing;
+ std::unique_ptr<SurfaceInterceptor> mInterceptor;
+ SurfaceTracing mTracing{*this};
+ bool mTracingEnabled = false;
+ bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false;
LayerStats mLayerStats;
- TimeStats& mTimeStats = TimeStats::getInstance();
+ const 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>()};
+ std::unique_ptr<MessageQueue> mEventQueue;
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 = 0;
- std::atomic<bool> mRefreshPending{false};
+ std::atomic<bool> mRefreshPending = false;
// We maintain a pool of pre-generated texture names to hand out to avoid
// layer creation needing to run on the main thread (which it would
@@ -878,16 +1038,47 @@
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, const client_cache_t& uncacheBuffer,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
+ bool privileged)
+ : states(composerStates),
+ displays(displayStates),
+ flags(transactionFlags),
+ desiredPresentTime(desiredPresentTime),
+ buffer(uncacheBuffer),
+ callback(listenerCallbacks),
+ postTime(postTime),
+ privileged(privileged) {}
+
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags;
+ const int64_t desiredPresentTime;
+ client_cache_t buffer;
+ std::vector<ListenerCallbacks> callback;
+ const int64_t postTime;
+ bool privileged;
+ };
+ std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+
/* ------------------------------------------------------------------------
* Feature prototyping
*/
- bool mInjectVSyncs;
+ bool mInjectVSyncs = false;
// Static screen stats
- bool mHasPoweredOff;
+ bool mHasPoweredOff = false;
- size_t mNumLayers;
+ size_t mNumLayers = 0;
// Verify that transaction is being called by an approved process:
// either AID_GRAPHICS or AID_SYSTEM.
@@ -897,26 +1088,87 @@
sp<IBinder> mWindowManager;
std::unique_ptr<dvr::VrFlinger> mVrFlinger;
- std::atomic<bool> mVrFlingerRequestsDisplay;
+ std::atomic<bool> mVrFlingerRequestsDisplay = false;
static bool useVrFlinger;
- std::thread::id mMainThreadId;
+ std::thread::id mMainThreadId = std::this_thread::get_id();
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;
-};
-}; // namespace android
+ std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
-#endif // ANDROID_SURFACE_FLINGER_H
+ /* ------------------------------------------------------------------------
+ * Scheduler
+ */
+ bool mUseSmart90ForVideo = false;
+ std::unique_ptr<Scheduler> mScheduler;
+ sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
+ sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+
+ scheduler::RefreshRateConfigs mRefreshRateConfigs;
+ scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
+
+ // All configs are allowed if the set is empty.
+ using DisplayConfigs = std::set<int32_t>;
+ DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
+
+ 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;
+ ui::DisplayPrimaries mInternalDisplayPrimaries;
+
+ sp<IInputFlinger> mInputFlinger;
+ InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
+ // Should only be accessed by the main thread.
+ InputWindowCommands mInputWindowCommands;
+
+ struct SetInputWindowsListener : BnSetInputWindowsListener {
+ explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger)
+ : mFlinger(std::move(flinger)) {}
+
+ void onSetInputWindowsFinished() override;
+
+ const sp<SurfaceFlinger> mFlinger;
+ };
+
+ const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
+
+ bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+ Hwc2::impl::PowerAdvisor mPowerAdvisor;
+
+ std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+
+ // Flag used to set override allowed display configs from backdoor
+ bool mDebugDisplayConfigSetByBackdoor = false;
+
+ // A set of layers that have no parent so they are not drawn on screen.
+ // Should only be accessed by the main thread.
+ // The Layer pointer is removed from the set when the destructor is called so there shouldn't
+ // be any issues with a raw pointer referencing an invalid object.
+ std::unordered_set<Layer*> mOffscreenLayers;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
new file mode 100644
index 0000000..e425b2a
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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,
+ const scheduler::RefreshRateConfigs& refreshRateConfig) override {
+ return std::make_unique<Scheduler>(callback, refreshRateConfig);
+ }
+
+ 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..c2bc808
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -0,0 +1,107 @@
+/*
+ * 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,
+ const scheduler::RefreshRateConfigs& refreshRateConfig) = 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..2b33ba1
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -0,0 +1,304 @@
+/*
+ * 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/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 tmpHasHDRDisplayVal = has_HDR_display(defaultValue);
+ auto tmpHasWideColorDisplayVal = has_wide_color_display(defaultValue);
+
+ auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement :
+ 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;
+}
+
+int32_t set_touch_timer_ms(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::set_touch_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;
+}
+
+bool enable_protected_contents(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::enable_protected_contents();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
+bool support_kernel_idle_timer(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::support_kernel_idle_timer();
+ 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..1964ccd
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -0,0 +1,86 @@
+/*
+ * 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 SURFACEFLINGERPROPERTIES_H_
+#define SURFACEFLINGERPROPERTIES_H_
+
+#include <SurfaceFlingerProperties.sysprop.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/graphics/common/1.2/types.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);
+
+int32_t set_touch_timer_ms(int32_t defaultValue);
+
+bool use_smart_90_for_video(bool defaultValue);
+
+bool enable_protected_contents(bool defaultValue);
+
+bool support_kernel_idle_timer(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..c4ab066 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -18,90 +18,186 @@
#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 {
+SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger)
+ : mFlinger(flinger), mSfLock(flinger.mDrawingStateLock) {}
+
+void SurfaceTracing::mainLoop() {
+ addFirstEntry();
+ bool enabled = true;
+ while (enabled) {
+ LayersTraceProto entry = traceWhenNotified();
+ enabled = addTraceToBuffer(entry);
+ }
+}
+
+void SurfaceTracing::addFirstEntry() {
+ LayersTraceProto entry;
+ {
+ std::scoped_lock lock(mSfLock);
+ entry = traceLayersLocked("tracing.enable");
+ }
+ addTraceToBuffer(entry);
+}
+
+LayersTraceProto SurfaceTracing::traceWhenNotified() {
+ std::unique_lock<std::mutex> lock(mSfLock);
+ mCanStartTrace.wait(lock);
+ android::base::ScopedLockAssertion assumeLock(mSfLock);
+ LayersTraceProto entry = traceLayersLocked(mWhere);
+ lock.unlock();
+ return entry;
+}
+
+bool SurfaceTracing::addTraceToBuffer(LayersTraceProto& entry) {
+ std::scoped_lock lock(mTraceLock);
+ mBuffer.emplace(std::move(entry));
+ if (mWriteToFile) {
+ writeProtoFileLocked();
+ mWriteToFile = false;
+ }
+ return mEnabled;
+}
+
+void SurfaceTracing::notify(const char* where) {
+ std::scoped_lock lock(mSfLock);
+ mWhere = where;
+ mCanStartTrace.notify_one();
+}
+
+void SurfaceTracing::writeToFileAsync() {
+ std::scoped_lock lock(mTraceLock);
+ mWriteToFile = true;
+ mCanStartTrace.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::scoped_lock lock(mTraceLock);
if (mEnabled) {
return;
}
+ mBuffer.reset(mBufferSize);
mEnabled = true;
-
- mTrace = std::make_unique<LayersTraceFileProto>();
- mTrace->set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
- LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+ 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::scoped_lock lock(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;
+ mCanStartTrace.notify_all();
+ return true;
}
bool SurfaceTracing::isEnabled() const {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::scoped_lock lock(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::scoped_lock lock(mTraceLock);
+ mBufferSize = bufferSizeInByte;
+ mBuffer.setSize(bufferSizeInByte);
}
-status_t SurfaceTracing::writeProtoFileLocked() {
+void SurfaceTracing::setTraceFlags(uint32_t flags) {
+ std::scoped_lock lock(mSfLock);
+ mTraceFlags = flags;
+}
+
+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, mTraceFlags));
+ 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::scoped_lock lock(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..4773307 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -18,36 +18,92 @@
#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:
+ explicit SurfaceTracing(SurfaceFlinger& 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;
+
+ enum : uint32_t {
+ TRACE_CRITICAL = 1 << 0,
+ TRACE_INPUT = 1 << 1,
+ TRACE_EXTRA = 1 << 2,
+ TRACE_ALL = 0xffffffff
+ };
+ void setTraceFlags(uint32_t flags);
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 addFirstEntry();
+ LayersTraceProto traceWhenNotified();
+ LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+
+ // Returns true if trace is enabled.
+ bool addTraceToBuffer(LayersTraceProto& entry);
+ void writeProtoFileLocked() REQUIRES(mTraceLock);
+
+ const SurfaceFlinger& mFlinger;
+ status_t mLastErr = NO_ERROR;
+ std::thread mThread;
+ std::condition_variable mCanStartTrace;
+
+ std::mutex& mSfLock;
+ uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_ALL;
+ const char* mWhere GUARDED_BY(mSfLock) = "";
+
+ 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 9c34aa7..c97a19b 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")) {
@@ -79,13 +67,23 @@
}
}
+std::string TimeStats::miniDump() {
+ ATRACE_CALL();
+
+ std::string result = "TimeStats miniDump:\n";
+ std::lock_guard<std::mutex> lock(mMutex);
+ android::base::StringAppendF(&result, "Number of tracked layers is %zu\n",
+ mTimeStatsTracker.size());
+ return result;
+}
+
void TimeStats::incrementTotalFrames() {
if (!mEnabled.load()) return;
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.totalFrames++;
+ mTimeStats.totalFrames++;
}
void TimeStats::incrementMissedFrames() {
@@ -94,7 +92,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.missedFrames++;
+ mTimeStats.missedFrames++;
}
void TimeStats::incrementClientCompositionFrames() {
@@ -103,13 +101,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 +116,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 +129,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 +146,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 +162,130 @@
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) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
+ 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,178 +293,261 @@
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];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
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];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
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];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
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];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
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];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
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];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
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() {
@@ -447,9 +556,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() {
@@ -458,47 +568,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..2bcb568 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,135 @@
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 std::string miniDump() = 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;
+ std::string miniDump() 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;
+
+ 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;
+
+ static const size_t MAX_NUM_LAYER_RECORDS = 200;
};
+} // 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..5cf8eb1
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -0,0 +1,298 @@
+/*
+ * 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 {
+
+// Returns 0 if they are equal
+// <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
+// >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
+//
+// See CallbackIdsHash for a explaniation of why this works
+static int compareCallbackIds(const std::vector<CallbackId>& c1,
+ const std::vector<CallbackId>& c2) {
+ if (c1.empty()) {
+ return !c2.empty();
+ }
+ return c1.front() - c2.front();
+}
+
+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, transactionStats] : mCompletedTransactions) {
+ IInterface::asBinder(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);
+}
+
+status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add callback because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ if (mCompletedTransactions.count(listener) == 0) {
+ status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+ return err;
+ }
+ }
+
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+ transactionStatsDeque.emplace_back(callbackIds);
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::registerPendingCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot register callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // addCallback before trying to register a pending callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ ALOGE("cannot find transaction stats");
+ return err;
+ }
+
+ mPendingTransactions[handle->listener][handle->callbackIds]++;
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addPresentedCallbackHandles(
+ const std::deque<sp<CallbackHandle>>& handles) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add presented callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ for (const auto& handle : handles) {
+ auto listener = mPendingTransactions.find(handle->listener);
+ if (listener != mPendingTransactions.end()) {
+ 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 {
+ ALOGW("there are more latched callbacks than there were registered callbacks");
+ }
+ if (listener->second.size() == 0) {
+ mPendingTransactions.erase(listener);
+ }
+ } else {
+ ALOGW("cannot find listener in mPendingTransactions");
+ }
+
+ status_t err = addCallbackHandle(handle);
+ if (err != NO_ERROR) {
+ ALOGE("could not add callback handle");
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addUnpresentedCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add unpresented callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ return addCallbackHandle(handle);
+}
+
+status_t TransactionCompletedThread::findTransactionStats(
+ const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) {
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+
+ // Search back to front because the most recent transactions are at the back of the deque
+ auto itr = transactionStatsDeque.rbegin();
+ for (; itr != transactionStatsDeque.rend(); itr++) {
+ if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
+ *outTransactionStats = &(*itr);
+ return NO_ERROR;
+ }
+ }
+
+ ALOGE("could not find transaction stats");
+ return BAD_VALUE;
+}
+
+status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // addCallback before trying to add a presnted callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transactionStats->latchTime = handle->latchTime;
+ transactionStats->surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+ handle->previousReleaseFence);
+ return NO_ERROR;
+}
+
+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);
+ std::vector<ListenerStats> completedListenerStats;
+
+ // For each listener
+ auto completedTransactionsItr = mCompletedTransactions.begin();
+ while (completedTransactionsItr != mCompletedTransactions.end()) {
+ auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
+ ListenerStats listenerStats;
+ listenerStats.listener = listener;
+
+ // For each transaction
+ auto transactionStatsItr = transactionStatsDeque.begin();
+ while (transactionStatsItr != transactionStatsDeque.end()) {
+ auto& transactionStats = *transactionStatsItr;
+
+ // If we are still waiting on the callback handles for this transaction, stop
+ // here because all transaction callbacks for the same listener must come in order
+ auto pendingTransactions = mPendingTransactions.find(listener);
+ if (pendingTransactions != mPendingTransactions.end() &&
+ pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
+ break;
+ }
+
+ // If the transaction has been latched
+ if (transactionStats.latchTime >= 0) {
+ if (!mPresentFence) {
+ break;
+ }
+ transactionStats.presentFence = mPresentFence;
+ }
+
+ // Remove the transaction from completed to the callback
+ listenerStats.transactionStats.push_back(std::move(transactionStats));
+ transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
+ }
+ // If the listener has completed transactions
+ if (!listenerStats.transactionStats.empty()) {
+ // If the listener is still alive
+ if (IInterface::asBinder(listener)->isBinderAlive()) {
+ // Send callback
+ listenerStats.listener->onTransactionCompleted(listenerStats);
+ IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
+ }
+ completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
+ } else {
+ completedTransactionsItr++;
+ }
+
+ completedListenerStats.push_back(std::move(listenerStats));
+ }
+
+ if (mPresentFence) {
+ mPresentFence.clear();
+ }
+
+ // If everyone else has dropped their reference to a layer and its listener is dead,
+ // we are about to cause the layer to be deleted. If this happens at the wrong time and
+ // we are holding mMutex, we will cause a deadlock.
+ //
+ // The deadlock happens because this thread is holding on to mMutex and when we delete
+ // the layer, it grabs SF's mStateLock. A different SF binder thread grabs mStateLock,
+ // then call's TransactionCompletedThread::run() which tries to grab mMutex.
+ //
+ // To avoid this deadlock, we need to unlock mMutex when dropping our last reference to
+ // to the layer.
+ mMutex.unlock();
+ completedListenerStats.clear();
+ mMutex.lock();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+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..21e2678
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -0,0 +1,139 @@
+/*
+ * 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 {
+
+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.empty()) ? 0 : callbackIds.front());
+ }
+};
+
+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();
+
+ // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
+ // to be included in the callback. This functions should be call before attempting to add any
+ // callback handles.
+ status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener,
+ const std::vector<CallbackId>& callbackIds);
+
+ // 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.
+ status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+ // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
+ status_t 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.
+ status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+
+ void addPresentFence(const sp<Fence>& presentFence);
+
+ void sendCallbacks();
+
+private:
+ void threadMain();
+
+ status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds,
+ TransactionStats** outTransactionStats) REQUIRES(mMutex);
+
+ status_t 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 ITransactionCompletedListenerHash {
+ std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const {
+ return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get()
+ : nullptr);
+ }
+ };
+
+ // 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<ITransactionCompletedListener>,
+ std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
+ ITransactionCompletedListenerHash>
+ mPendingTransactions GUARDED_BY(mMutex);
+ std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>,
+ ITransactionCompletedListenerHash>
+ mCompletedTransactions 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/VSyncModulator.h b/services/surfaceflinger/VSyncModulator.h
deleted file mode 100644
index e071a59..0000000
--- a/services/surfaceflinger/VSyncModulator.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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 <utils/Errors.h>
-
-#include <mutex>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-/*
- * Modulates the vsync-offsets depending on current SurfaceFlinger state.
- */
-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
- // as early. May be the same as late, in which case we don't shift offsets.
- // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
- // and the transaction was marked as early, we'll use sfEarly.
- // sfLate: The regular SF vsync phase offset.
- // appEarly: Like sfEarly, but for the app-vsync
- // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
- // appLate: The regular app vsync phase offset.
- void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) {
- mEarlyOffsets = early;
- mEarlyGlOffsets = earlyGl;
- mLateOffsets = late;
- mOffsets = late;
- }
-
- Offsets getEarlyOffsets() const {
- return mEarlyOffsets;
- }
-
- Offsets getEarlyGlOffsets() const {
- return mEarlyGlOffsets;
- }
-
- void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
- mSfEventThread = sfEventThread;
- mAppEventThread = appEventThread;
- }
-
- void setTransactionStart(TransactionStart transactionStart) {
-
- if (transactionStart == TransactionStart::EARLY) {
- mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT;
- }
-
- // An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) {
- return;
- }
- mTransactionStart = transactionStart;
- updateOffsets();
- }
-
- void onTransactionHandled() {
- if (mTransactionStart == TransactionStart::NORMAL) return;
- mTransactionStart = TransactionStart::NORMAL;
- updateOffsets();
- }
-
- void onRefreshed(bool usedRenderEngine) {
- bool updateOffsetsNeeded = false;
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
- }
- if (usedRenderEngine != mLastFrameUsedRenderEngine) {
- mLastFrameUsedRenderEngine = usedRenderEngine;
- updateOffsetsNeeded = true;
- }
- if (updateOffsetsNeeded) {
- updateOffsets();
- }
- }
-
-private:
-
- void updateOffsets() {
- const Offsets desired = getOffsets();
- const Offsets current = mOffsets;
-
- bool changed = false;
- if (desired.sf != current.sf) {
- mSfEventThread->setPhaseOffset(desired.sf);
- changed = true;
- }
- if (desired.app != current.app) {
- mAppEventThread->setPhaseOffset(desired.app);
- changed = true;
- }
-
- if (changed) {
- mOffsets = desired;
- }
- }
-
- Offsets getOffsets() {
- if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) {
- return mEarlyOffsets;
- } else if (mLastFrameUsedRenderEngine) {
- return mEarlyGlOffsets;
- } else {
- return mLateOffsets;
- }
- }
-
- Offsets mLateOffsets;
- Offsets mEarlyOffsets;
- Offsets mEarlyGlOffsets;
-
- EventThread* mSfEventThread = nullptr;
- EventThread* mAppEventThread = nullptr;
-
- std::atomic<Offsets> mOffsets;
-
- std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
- std::atomic<bool> mLastFrameUsedRenderEngine = false;
- std::atomic<int> mRemainingEarlyFrameCount = 0;
-};
-
-} // namespace android
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 172f5ac..d03cb7b 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..d3381e5 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,9 @@
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, "isProtected=%1d, ", isProtected);
StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
@@ -312,8 +312,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..b097505 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -1,144 +1,180 @@
// 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;
+
+ InputWindowInfoProto input_window_info = 47;
}
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;
+}
+
+message InputWindowInfoProto {
+ uint32 layout_params_flags = 1;
+ uint32 layout_params_type = 2;
+ RectProto frame = 3;
+ RegionProto touchable_region = 4;
+
+ uint32 surface_inset = 5;
+ bool visible = 6;
+ bool can_receive_keys = 7;
+ bool has_focus = 8;
+ bool has_wallpaper = 9;
+
+ float global_scale_factor = 10;
+ float window_x_scale = 11;
+ float window_y_scale = 12;
+
+ uint32 crop_layer_id = 13;
+ bool replace_touchable_region_with_crop = 14;
+ RectProto touchable_region_crop = 15;
}
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/Android.bp b/services/surfaceflinger/sysprop/Android.bp
new file mode 100644
index 0000000..7721d7d2
--- /dev/null
+++ b/services/surfaceflinger/sysprop/Android.bp
@@ -0,0 +1,6 @@
+sysprop_library {
+ name: "SurfaceFlingerProperties",
+ srcs: ["*.sysprop"],
+ api_packages: ["android.sysprop"],
+ property_owner: "Platform",
+}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
new file mode 100644
index 0000000..decabd5
--- /dev/null
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -0,0 +1,338 @@
+# 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: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns"
+}
+
+prop {
+ api_name: "vsync_sf_event_phase_offset_ns"
+ type: Long
+ scope: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ 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: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_red"
+}
+
+prop {
+ api_name: "display_primary_green"
+ type: DoubleList
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_green"
+}
+
+prop {
+ api_name: "display_primary_blue"
+ type: DoubleList
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.display_primary_blue"
+}
+
+prop {
+ api_name: "display_primary_white"
+ type: DoubleList
+ scope: System
+ 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: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_idle_timer_ms"
+}
+
+# setTouchTimerMs indicates what is considered a timeout in milliseconds for Scheduler.
+# This value is used by the Scheduler to trigger touch 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_touch_timer_ms"
+ type: Integer
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_touch_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: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_smart_90_for_video"
+}
+
+prop {
+ api_name: "enable_protected_contents"
+ type: Boolean
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.protected_contents"
+}
+
+# Indicates whether Scheduler's idle timer should support a display driver timeout in the kernel.
+# The value of set_idle_timer_ms should be shorter in time than the timeout duration in the kernel.
+prop {
+ api_name: "support_kernel_idle_timer"
+ type: Boolean
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.support_kernel_idle_timer"
+}
diff --git a/services/surfaceflinger/sysprop/api/current.txt b/services/surfaceflinger/sysprop/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/removed.txt b/services/surfaceflinger/sysprop/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
new file mode 100644
index 0000000..6ae3ac1
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -0,0 +1,43 @@
+// Signature format: 2.0
+package android.sysprop {
+
+ public final class SurfaceFlingerProperties {
+ method public static java.util.Optional<java.lang.Long> default_composition_dataspace();
+ method public static java.util.Optional<java.lang.Integer> default_composition_pixel_format();
+ method public static java.util.List<java.lang.Double> display_primary_blue();
+ method public static java.util.List<java.lang.Double> display_primary_green();
+ method public static java.util.List<java.lang.Double> display_primary_red();
+ method public static java.util.List<java.lang.Double> display_primary_white();
+ method public static java.util.Optional<java.lang.Boolean> enable_protected_contents();
+ method public static java.util.Optional<java.lang.Boolean> force_hwc_copy_for_virtual_displays();
+ method public static java.util.Optional<java.lang.Boolean> has_HDR_display();
+ method public static java.util.Optional<java.lang.Boolean> has_wide_color_display();
+ method public static java.util.Optional<java.lang.Long> max_frame_buffer_acquired_buffers();
+ method public static java.util.Optional<java.lang.Long> max_virtual_display_dimension();
+ method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns();
+ method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation();
+ method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
+ method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
+ method public static java.util.Optional<java.lang.Integer> set_touch_timer_ms();
+ method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service();
+ method public static java.util.Optional<java.lang.Boolean> support_kernel_idle_timer();
+ method public static java.util.Optional<java.lang.Boolean> use_color_management();
+ method public static java.util.Optional<java.lang.Boolean> use_context_priority();
+ method public static java.util.Optional<java.lang.Boolean> use_smart_90_for_video();
+ method public static java.util.Optional<java.lang.Boolean> use_vr_flinger();
+ method public static java.util.Optional<java.lang.Long> vsync_event_phase_offset_ns();
+ method public static java.util.Optional<java.lang.Long> vsync_sf_event_phase_offset_ns();
+ method public static java.util.Optional<java.lang.Long> wcg_composition_dataspace();
+ method public static java.util.Optional<java.lang.Integer> wcg_composition_pixel_format();
+ }
+
+ public enum SurfaceFlingerProperties.primary_display_orientation_values {
+ method public String getPropValue();
+ enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_0;
+ enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_180;
+ enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_270;
+ enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_90;
+ }
+
+}
+
diff --git a/services/surfaceflinger/sysprop/api/system-removed.txt b/services/surfaceflinger/sysprop/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/test-current.txt b/services/surfaceflinger/sysprop/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/sysprop/api/test-removed.txt b/services/surfaceflinger/sysprop/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/surfaceflinger/sysprop/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index c511c5e..53a3611 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -17,9 +17,13 @@
defaults: ["surfaceflinger_defaults"],
test_suites: ["device-tests"],
srcs: [
+ "BufferGenerator.cpp",
+ "Credentials_test.cpp",
+ "InvalidHandles_test.cpp",
"Stress_test.cpp",
"SurfaceInterceptor_test.cpp",
"Transaction_test.cpp",
+ "VirtualDisplay_test.cpp",
],
data: ["SurfaceFlinger_test.filter"],
static_libs: [
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/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
new file mode 100644
index 0000000..42d1f5a
--- /dev/null
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 <binder/Binder.h>
+
+#include <gtest/gtest.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace {
+
+class NotALayer : public BBinder {};
+
+/**
+ * For all of these tests we make a SurfaceControl with an invalid layer handle
+ * and verify we aren't able to trick SurfaceFlinger.
+ */
+class InvalidHandleTest : public ::testing::Test {
+protected:
+ sp<SurfaceComposerClient> mScc;
+ sp<SurfaceControl> mNotSc;
+ void SetUp() override {
+ mScc = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mScc->initCheck());
+ mNotSc = makeNotSurfaceControl();
+ }
+
+ sp<SurfaceControl> makeNotSurfaceControl() {
+ return new SurfaceControl(mScc, new NotALayer(), nullptr, true);
+ }
+};
+
+TEST_F(InvalidHandleTest, createSurfaceInvalidHandle) {
+ auto notSc = makeNotSurfaceControl();
+ ASSERT_EQ(nullptr,
+ mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
+ notSc.get())
+ .get());
+}
+
+TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+
+ ASSERT_EQ(NAME_NOT_FOUND,
+ sf->captureLayers(mNotSc->getHandle(), &outBuffer, Rect::EMPTY_RECT, 1.0f));
+}
+
+} // namespace
+} // 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..6b4634a 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.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*"
}
}
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 e04e1e9..d5f6534 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -15,22 +15,28 @@
*/
#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 <private/gui/ComposerService.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>
@@ -40,6 +46,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include "BufferGenerator.h"
+
namespace android {
namespace {
@@ -65,6 +73,9 @@
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;
@@ -95,32 +106,22 @@
}
// 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 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;
}
- 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;
+
+ 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;
@@ -128,6 +129,7 @@
dst += 4;
}
}
+ buffer->unlock();
}
// Check if a region has the specified color.
@@ -196,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,
@@ -229,6 +231,20 @@
*sc = std::make_unique<ScreenCapture>(outBuffer);
}
+ static void captureChildLayersExcluding(
+ std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+ std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ SurfaceComposerClient::Transaction().apply(true);
+
+ sp<GraphicBuffer> outBuffer;
+ ASSERT_EQ(NO_ERROR,
+ sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
+ 1.0f, true));
+ *sc = std::make_unique<ScreenCapture>(outBuffer);
+ }
+
void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
@@ -326,18 +342,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();
@@ -346,6 +371,32 @@
return 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);
+ }
+
+ sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
+ SurfaceControl* parent = nullptr) {
+ auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, parent);
+ asTransaction([&](Transaction& t) {
+ t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
+ t.setAlpha(colorLayer, color.a / 255.0f);
+ });
+ return colorLayer;
+ }
+
ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
// wait for previous transactions (such as setSize) to complete
Transaction().apply(true);
@@ -356,23 +407,6 @@
return buffer;
}
- ANativeWindow_Buffer getLayerBuffer(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));
-
- return buffer;
- }
-
- void postLayerBuffer(const sp<SurfaceControl>& layer) {
- ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
-
- // wait for the newly posted buffer to be latched
- waitForLayerBuffers();
- }
-
void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
@@ -388,56 +422,133 @@
postBufferQueueLayerBuffer(layer);
}
- void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color) {
- ANativeWindow_Buffer buffer;
- ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer));
- fillBufferColor(buffer, Rect(0, 0, buffer.width, buffer.height), color);
- postLayerBuffer(layer);
+ 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 fillLayerQuadrant(const sp<SurfaceControl>& layer, const Color& topLeft,
+ 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
@@ -445,42 +556,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
@@ -489,113 +769,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()
@@ -606,116 +887,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);
}
@@ -723,53 +1025,183 @@
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) {
+TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor,
+ parent.get()));
+ Transaction()
+ .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+ .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+ .show(childLayer)
+ .show(parent)
+ .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+ .apply();
+
+ Transaction()
+ .setRelativeLayer(childLayer, parent->getHandle(), -1)
+ .setLayer(childLayer, 1)
+ .apply();
+
+ {
+ SCOPED_TRACE("setLayer above");
+ // Set layer should get applied and place the child above.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+ }
+
+ Transaction()
+ .setLayer(childLayer, 1)
+ .setRelativeLayer(childLayer, parent->getHandle(), -1)
+ .apply();
+
+ {
+ SCOPED_TRACE("setRelative below");
+ // Set relative layer should get applied and place the child below.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+ }
+}
+
+TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
+ sp<SurfaceControl> parent =
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor);
+ sp<SurfaceControl> relativeParent =
+ LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
+ 0 /* buffer height */, ISurfaceComposerClient::eFXSurfaceColor);
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor,
+ parent.get()));
+ Transaction()
+ .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+ .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+ .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
+ .show(childLayer)
+ .show(parent)
+ .show(relativeParent)
+ .setLayer(parent, mLayerZBase - 1)
+ .setLayer(relativeParent, mLayerZBase)
+ .apply();
+
+ Transaction()
+ .setRelativeLayer(childLayer, relativeParent->getHandle(), 1)
+ .apply();
+
+ {
+ SCOPED_TRACE("setLayer above");
+ // Set layer should get applied and place the child above.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+ }
+
+ Transaction()
+ .hide(relativeParent)
+ .apply();
+
+ {
+ SCOPED_TRACE("hide relative parent");
+ // The relative should no longer be visible.
+ std::unique_ptr<ScreenCapture> screenshot;
+ ScreenCapture::captureScreen(&screenshot);
+ screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+ }
+}
+
+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);
@@ -779,7 +1211,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);
@@ -789,7 +1221,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);
@@ -800,7 +1232,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);
}
@@ -810,57 +1242,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)
@@ -868,21 +1308,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;
@@ -890,13 +1330,11 @@
.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));
}
/** RAII Wrapper around get/seteuid */
@@ -922,16 +1360,15 @@
Transaction()
.setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
.apply(true);
-
ASSERT_EQ(PERMISSION_DENIED,
- composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, 0, INT_MAX, false));
+ 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, 0, INT_MAX, false));
+ composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
{
SCOPED_TRACE("as system");
@@ -941,31 +1378,32 @@
// 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.
- bool outCapturedSecureLayers = false;
+ bool outCapturedSecureLayers;
ASSERT_EQ(NO_ERROR,
composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
- Rect(), 0, 0, 0, INT_MAX, false, ISurfaceComposer::eRotateNone, true));
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
+ 0, false, ISurfaceComposer::eRotateNone, true));
ASSERT_EQ(true, outCapturedSecureLayers);
ScreenCapture sc(outBuffer);
sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
}
-TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) {
+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);
}
@@ -973,24 +1411,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));
@@ -998,32 +1487,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});
@@ -1034,37 +1557,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);
@@ -1075,26 +1663,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;
@@ -1107,20 +1886,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);
@@ -1133,91 +1913,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;
@@ -1230,31 +2056,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()
@@ -1262,14 +2090,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
@@ -1280,363 +2108,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());
@@ -1658,11 +4203,10 @@
}
virtual void TearDown() {
- mComposerClient->dispose();
+ LayerTransactionTest::TearDown();
mBGSurfaceControl = 0;
mFGSurfaceControl = 0;
mSyncSurfaceControl = 0;
- mComposerClient = 0;
}
void waitForPostedBuffers() {
@@ -1675,13 +4219,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;
@@ -1691,10 +4229,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();
@@ -1752,13 +4290,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 {
@@ -1782,45 +4319,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);
@@ -1832,14 +4332,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);
});
{
@@ -1875,26 +4375,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);
@@ -1903,7 +4404,7 @@
}
TEST_F(LayerUpdateTest, MergingTransactions) {
- sp<ScreenCapture> sc;
+ std::unique_ptr<ScreenCapture> sc;
{
SCOPED_TRACE("before move");
ScreenCapture::captureScreen(&sc);
@@ -1930,13 +4431,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);
}
}
@@ -1946,7 +4447,7 @@
}
sp<SurfaceControl> mChild;
- sp<ScreenCapture> mCapture;
+ std::unique_ptr<ScreenCapture> mCapture;
};
TEST_F(ChildLayerTest, ChildLayerPositioning) {
@@ -1957,7 +4458,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
@@ -1969,7 +4470,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
@@ -1984,27 +4485,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);
@@ -2019,7 +4504,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectFGColor(0, 0);
// Last pixel in foreground should now be the child.
mCapture->expectChildColor(63, 63);
@@ -2034,7 +4519,7 @@
// Find the boundary between the parent and child
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectChildColor(9, 9);
mCapture->expectFGColor(10, 10);
}
@@ -2044,7 +4529,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);
@@ -2052,6 +4537,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);
@@ -2065,7 +4576,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
// Unblended child color
mCapture->checkPixel(0, 0, 0, 254, 0);
}
@@ -2073,7 +4584,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);
}
@@ -2081,7 +4592,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);
}
@@ -2095,7 +4606,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
@@ -2109,7 +4620,7 @@
});
{
- ScreenCapture::captureScreen(&mCapture);
+ mCapture = screenshot();
mCapture->expectFGColor(64, 64);
// In reparenting we should have exposed the entire foreground surface.
mCapture->expectFGColor(74, 74);
@@ -2120,6 +4631,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);
@@ -2128,7 +4669,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
@@ -2137,6 +4678,7 @@
mCapture->expectFGColor(84, 84);
}
+
asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
asTransaction([&](Transaction& t) { t.hide(mChild); });
@@ -2144,7 +4686,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);
@@ -2154,10 +4696,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);
@@ -2170,7 +4711,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
@@ -2185,13 +4726,110 @@
// 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, DetachChildrenWithDeferredTransaction) {
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ ASSERT_TRUE(childNewClient != nullptr);
+ ASSERT_TRUE(childNewClient->isValid());
+
+ fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+ Transaction()
+ .hide(mChild)
+ .show(childNewClient)
+ .setPosition(childNewClient, 10, 10)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ Rect rect = Rect(74, 74, 84, 84);
+ mCapture->expectBorder(rect, Color{195, 63, 63, 255});
+ mCapture->expectColor(rect, Color{200, 200, 200, 255});
+ }
+
+ Transaction()
+ .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
+ mFGSurfaceControl->getSurface()->getNextFrameNumber())
+ .apply();
+ Transaction().detachChildren(mFGSurfaceControl).apply();
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
+
+ // BufferLayer can still dequeue buffers even though there's a detached layer with a
+ // deferred transaction.
+ {
+ SCOPED_TRACE("new buffer");
+ mCapture = screenshot();
+ Rect rect = Rect(74, 74, 84, 84);
+ mCapture->expectBorder(rect, Color::RED);
+ mCapture->expectColor(rect, Color{200, 200, 200, 255});
+ }
+}
+
TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
asTransaction([&](Transaction& t) {
t.show(mChild);
@@ -2200,11 +4838,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) {
@@ -2214,13 +4852,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);
}
}
@@ -2233,11 +4871,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.
@@ -2252,25 +4891,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);
});
@@ -2297,7 +5044,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
@@ -2309,7 +5056,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);
@@ -2328,7 +5075,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
@@ -2338,17 +5085,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());
@@ -2362,7 +5108,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
@@ -2372,7 +5118,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);
@@ -2382,13 +5128,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);
@@ -2396,8 +5141,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;
@@ -2409,13 +5153,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;
@@ -2432,9 +5377,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);
@@ -2448,9 +5392,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);
@@ -2461,10 +5404,60 @@
mCapture->expectChildColor(0, 0);
}
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+ sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child2, 200, 0, 200);
+ sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+ fillSurfaceRGBA8(child2, 200, 0, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .show(child2)
+ .show(child3)
+ .setLayer(child, 1)
+ .setLayer(child2, 2)
+ .apply(true);
+
+ // Child2 would be visible but its excluded, so we should see child1 color instead.
+ ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+ mCapture->checkPixel(10, 10, 0, 0, 0);
+ mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
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);
@@ -2482,11 +5475,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);
@@ -2506,12 +5498,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);
@@ -2540,71 +5530,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()
@@ -2621,9 +5615,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();
@@ -2636,15 +5629,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()
@@ -2662,14 +5653,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)
@@ -2687,7 +5676,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.
@@ -2696,14 +5685,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)
@@ -2732,13 +5719,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;
@@ -2754,9 +5740,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");
@@ -2793,4 +5779,284 @@
}
}
+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());
+}
+
+class RelativeZTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ // Back layer
+ mBackgroundLayer = createColorLayer("Background layer", Color::RED);
+
+ // Front layer
+ mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
+
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+ t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
+ t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mBackgroundLayer = 0;
+ mForegroundLayer = 0;
+ }
+
+ sp<SurfaceControl> mBackgroundLayer;
+ sp<SurfaceControl> mForegroundLayer;
+};
+
+// When a layer is reparented offscreen, remove relative z order if the relative parent
+// is still onscreen so that the layer is not drawn.
+TEST_F(RelativeZTest, LayerRemoved) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ // Background layer (RED)
+ // Child layer (WHITE) (relative to foregroud layer)
+ // Foregroud layer (GREEN)
+ sp<SurfaceControl> childLayer =
+ createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
+
+ Transaction{}
+ .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
+ .show(childLayer)
+ .apply();
+
+ {
+ // The childLayer should be in front of the FG control.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+ }
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLayer, nullptr).apply();
+
+ // Background layer (RED)
+ // Child layer (WHITE)
+ // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+
+ {
+ // The relative z info for child layer should be reset, leaving FG control on top.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+ }
+}
+
+// When a layer is reparented offscreen, preseve relative z order if the relative parent
+// is also offscreen. Regression test b/132613412
+TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
+ std::unique_ptr<ScreenCapture> sc;
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1 (WHITE)
+ // child level 2a (BLUE)
+ // child level 3 (GREEN) (relative to child level 2b)
+ // child level 2b (BLACK)
+ sp<SurfaceControl> childLevel1 =
+ createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
+ sp<SurfaceControl> childLevel2a =
+ createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
+ sp<SurfaceControl> childLevel2b =
+ createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
+ sp<SurfaceControl> childLevel3 =
+ createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
+
+ Transaction{}
+ .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+ .show(childLevel2a)
+ .show(childLevel2b)
+ .show(childLevel3)
+ .apply();
+
+ {
+ // The childLevel3 should be in front of childLevel2b.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+ }
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ Transaction{}.reparent(childLevel1, nullptr).apply();
+
+ // Background layer (RED)
+ // Foregroud layer (GREEN)
+ // child level 1 (WHITE)
+ // child level 2 back (BLUE)
+ // child level 3 (GREEN) (relative to child level 2b)
+ // child level 2 front (BLACK)
+ Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+
+ {
+ // Nothing should change at this point since relative z info was preserved.
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
new file mode 100644
index 0000000..9fd2227
--- /dev/null
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 <binder/Binder.h>
+
+#include <gtest/gtest.h>
+#include <gui/GLConsumer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+namespace android {
+namespace {
+
+class VirtualDisplayTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ sp<IGraphicBufferConsumer> consumer;
+
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(100, 100);
+
+ mGLConsumer = new GLConsumer(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ }
+
+ sp<IGraphicBufferProducer> mProducer;
+ sp<GLConsumer> mGLConsumer;
+};
+
+TEST_F(VirtualDisplayTest, VirtualDisplayDestroyedSurfaceReuse) {
+ sp<IBinder> virtualDisplay =
+ SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(virtualDisplay, mProducer);
+ t.apply(true);
+
+ SurfaceComposerClient::destroyDisplay(virtualDisplay);
+ virtualDisplay.clear();
+ // Sync here to ensure the display was completely destroyed in SF
+ t.apply(true);
+
+ sp<Surface> surface = new Surface(mProducer);
+ sp<ANativeWindow> window(surface);
+
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_EGL));
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
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 28cac2e..a892a2a 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -146,7 +146,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;
@@ -243,7 +243,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()) {
@@ -251,11 +251,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;
}
}
@@ -295,13 +296,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);
@@ -329,14 +331,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);
@@ -365,11 +368,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);
@@ -403,11 +407,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);
@@ -474,10 +479,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;
@@ -625,7 +631,7 @@
{
TransactionScope ts(*sFakeComposer);
Rect cropRect(16, 16, 32, 32);
- ts.setCrop(mFGSurfaceControl, cropRect);
+ ts.setCrop_legacy(mFGSurfaceControl, cropRect);
}
ASSERT_EQ(2, sFakeComposer->getFrameCount());
@@ -635,40 +641,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);
@@ -848,18 +820,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()));
@@ -982,7 +952,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
@@ -995,22 +965,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);
@@ -1096,7 +1050,7 @@
EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
}
-TEST_F(ChildLayerTest, DetachChildren) {
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
{
TransactionScope ts(*sFakeComposer);
ts.show(mChild);
@@ -1112,14 +1066,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.
@@ -1194,8 +1193,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);
}
@@ -1218,6 +1217,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); }
@@ -1236,8 +1311,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));
}
};
@@ -1281,7 +1355,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;
@@ -1295,7 +1369,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()));
@@ -1308,111 +1382,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/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9949bfa..f842d61 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,60 @@
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",
+ "CachingTest.cpp",
+ "CompositionTest.cpp",
+ "DispSyncSourceTest.cpp",
+ "DisplayIdentificationTest.cpp",
"DisplayTransactionTest.cpp",
"EventControlThreadTest.cpp",
"EventThreadTest.cpp",
+ "IdleTimerTest.cpp",
+ "LayerHistoryTest.cpp",
+ "LayerMetadataTest.cpp",
+ "SchedulerTest.cpp",
+ "SchedulerUtilsTest.cpp",
+ "RefreshRateConfigsTest.cpp",
+ "RefreshRateStatsTest.cpp",
+ "RegionSamplingTest.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/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
new file mode 100644
index 0000000..74ce540
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "CachingTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/BufferQueue.h>
+#include "BufferStateLayer.h"
+
+namespace android {
+
+class SlotGenerationTest : public testing::Test {
+protected:
+ BufferStateLayer::HwcSlotGenerator mHwcSlotGenerator;
+ 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)};
+ sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+};
+
+TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) {
+ sp<IBinder> binder = new BBinder();
+ // test getting invalid client_cache_id
+ client_cache_t id;
+ uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
+}
+
+TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) {
+ sp<IBinder> binder = new BBinder();
+ client_cache_t id;
+ id.token = binder;
+ id.id = 0;
+ uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
+
+ client_cache_t idB;
+ idB.token = binder;
+ idB.id = 1;
+ slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
+
+ slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
+
+ slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
+}
+
+TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) {
+ sp<IBinder> binder = new BBinder();
+ std::vector<client_cache_t> ids;
+ uint32_t cacheId = 0;
+ // fill up cache
+ for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ client_cache_t id;
+ id.token = binder;
+ id.id = cacheId;
+ ids.push_back(id);
+
+ uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
+ cacheId++;
+ }
+ for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(ids[i]);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
+ }
+
+ for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ client_cache_t id;
+ id.token = binder;
+ id.id = cacheId;
+ uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+ EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
+ cacheId++;
+ }
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
new file mode 100644
index 0000000..4f8ed1a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -0,0 +1,1320 @@
+/*
+ * 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(mFlinger.mutableRefreshRateConfigs());
+ 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();
+
+ 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,
+ setPowerMode(HWC_DISPLAY,
+ static_cast<Hwc2::IComposerClient::PowerMode>(
+ Derived::INIT_POWER_MODE)))
+ .WillOnce(Return(Error::NONE));
+
+ FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical,
+ true /* isPrimary */)
+ .setCapabilities(&test->mDefaultCapabilities)
+ .setPowerMode(Derived::INIT_POWER_MODE)
+ .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 setupPreconditionCallExpectations(CompositionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
+
+ 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>&, ANativeWindowBuffer*,
+ const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 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>&, ANativeWindowBuffer*,
+ const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 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 setupPreconditionCallExpectations(CompositionTest*) {}
+
+ 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*, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 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*, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 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*, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ 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(1);
+ 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::template setupPreconditionCallExpectations<ThisCase>(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..2e705da
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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());
+ }
+}
+
+} // 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..5f58e7d 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(mFlinger.mutableRefreshRateConfigs());
+ 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,32 @@
// 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));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+
return injector;
}
@@ -266,19 +368,19 @@
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));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
}
static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -297,7 +399,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;
@@ -307,6 +410,7 @@
// The HWC active configuration id
static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
+ static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
static void injectPendingHotplugEvent(DisplayTransactionTest* test,
HWC2::Connection connection) {
@@ -315,15 +419,31 @@
}
// 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)
.setActiveConfig(HWC_ACTIVE_CONFIG_ID)
+ .setPowerMode(INIT_POWER_MODE)
.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)));
+ EXPECT_CALL(*test->mComposer,
+ setPowerMode(HWC_DISPLAY_ID,
+ static_cast<Hwc2::IComposerClient::PowerMode>(INIT_POWER_MODE)))
+ .WillOnce(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 +473,16 @@
getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
IComposerClient::Attribute::DPI_Y, _))
.WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), 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 +492,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 +567,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 +597,7 @@
static void injectConfigChange(DisplayTransactionTest* test) {
test->mFlinger.mutableHasWideColorDisplay() = false;
+ test->mFlinger.mutableUseColorManagement() = false;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
}
@@ -471,11 +616,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 +647,7 @@
static constexpr bool WIDE_COLOR_SUPPORTED = false;
static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
test->mFlinger.mutableHasWideColorDisplay() = true;
}
@@ -512,6 +661,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 +670,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 +705,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 +720,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 +735,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 +748,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 +756,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 +765,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 +785,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 +841,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 +865,7 @@
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
HdrNotSupportedVariant<PrimaryDisplayVariant>,
Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant,
- NonHwcDisplayHdrSupportVariant,
- NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>;
+
/* ------------------------------------------------------------------------
*
* SurfaceFlinger::onHotplugReceived
@@ -694,8 +873,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 +897,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 +909,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 +1025,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 +1056,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 +1131,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 +1144,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 +1156,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 +1170,194 @@
}
/* ------------------------------------------------------------------------
+ * 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);
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).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 +1370,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 +1400,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 +1445,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 +1492,9 @@
template <typename Case>
void setupCommonPreconditions();
+ template <typename Case, bool connected>
+ static void expectHotplugReceived(mock::EventThread*);
+
template <typename Case>
void setupCommonCallExpectationsForConnectProcessing();
@@ -1146,6 +1532,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 +1557,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 +1575,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 +1669,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 +1691,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 +1705,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 +1740,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 +1804,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 +1848,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 +1880,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
@@ -1491,7 +1906,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 +1931,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 +1951,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 +1974,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 +1984,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 +2133,20 @@
// 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);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
display.inject();
// There is a change to the viewport state
@@ -1726,11 +2158,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 +2178,20 @@
// 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);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
display.inject();
// There is a change to the viewport state
@@ -1767,11 +2203,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 +2243,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 +2705,8 @@
// processing.
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+
// --------------------------------------------------------------------
// Invocation
@@ -2358,14 +2758,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 +2782,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 +2835,23 @@
}
};
+struct DispSyncIsSupportedVariant {
+ static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
+ 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 +2873,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 +2903,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 +2939,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 +2958,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 +2968,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 +2991,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 +3008,7 @@
}
static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
- test->mFlinger.mutablePrimaryHWVsyncEnabled() = enabled;
+ test->mScheduler->mutablePrimaryHWVsyncEnabled() = enabled;
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
@@ -2605,16 +3043,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 +3077,7 @@
// --------------------------------------------------------------------
// Preconditions
+ Case::Doze::setupComposerCallExpectations(this);
auto display =
Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
Case::setInitialPrimaryHWVsyncEnabled(this,
@@ -2671,7 +3113,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 +3127,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..ea908a9 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,27 @@
namespace android {
namespace {
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222;
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL;
+
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)
+ : EventThreadConnection(eventThread, std::move(resyncCallback)) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -71,7 +77,10 @@
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;
@@ -81,6 +90,7 @@
ConnectionEventRecorder mConnectionEventCallRecorder{0};
MockVSyncSource mVSyncSource;
+ VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<android::impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
};
@@ -101,25 +111,36 @@
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());
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -169,16 +190,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 {
/* ------------------------------------------------------------------------
@@ -194,6 +225,17 @@
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);
+
+ // 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);
@@ -201,22 +243,19 @@
// EventThread should immediately request a resync.
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 +280,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 +296,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 +319,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 +346,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 +366,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 +390,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 +414,39 @@
expectVSyncSetPhaseOffsetCallReceived(321);
}
-TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false);
- expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false);
+TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
}
-TEST_F(EventThreadTest, postHotplugPrimaryConnect) {
- mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true);
- expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true);
+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);
+}
+
+TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
+ mThread->onConfigChanged(DISPLAY_ID_64BIT, 7);
+ expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
new file mode 100644
index 0000000..96121bb
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.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 <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; }
+
+ PhaseOffsets::Offsets getOffsetsForRefreshRate(
+ RefreshRateConfigs::RefreshRateType /*refreshRateType*/) const override {
+ return getCurrentOffsets();
+ }
+
+ // 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 {}
+
+ nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; }
+
+ // 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..eff22b6
--- /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_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).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..2b1dfa8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -0,0 +1,131 @@
+#undef LOG_TAG
+#define LOG_TAG "LayerHistoryUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <mutex>
+#include <thread>
+
+#include "Scheduler/LayerHistory.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android {
+namespace scheduler {
+
+class LayerHistoryTest : public testing::Test {
+public:
+ LayerHistoryTest();
+ ~LayerHistoryTest() override;
+
+protected:
+ std::unique_ptr<LayerHistory> mLayerHistory;
+
+ static constexpr float MAX_REFRESH_RATE = 90.f;
+};
+
+LayerHistoryTest::LayerHistoryTest() {
+ mLayerHistory = std::make_unique<LayerHistory>();
+}
+LayerHistoryTest::~LayerHistoryTest() {}
+
+namespace {
+TEST_F(LayerHistoryTest, oneLayer) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ mLayerHistory->setVisibility(testLayer, true);
+
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ // This is still 0, because the layer is not considered recently active if it
+ // has been present in less than 10 frames.
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ // This should be MAX_REFRESH_RATE as we have more than 10 samples
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+}
+
+TEST_F(LayerHistoryTest, oneHDRLayer) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestHDRLayer", MAX_REFRESH_RATE);
+ mLayerHistory->setVisibility(testLayer, true);
+
+ mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
+ EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ EXPECT_EQ(true, mLayerHistory->getDesiredRefreshRateAndHDR().second);
+
+ mLayerHistory->setVisibility(testLayer, false);
+ EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ EXPECT_EQ(false, mLayerHistory->getDesiredRefreshRateAndHDR().second);
+}
+
+TEST_F(LayerHistoryTest, explicitTimestamp) {
+ std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
+ mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ mLayerHistory->setVisibility(test30FpsLayer, true);
+
+ nsecs_t startTime = systemTime();
+ for (int i = 0; i < 31; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ }
+
+ EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+}
+
+TEST_F(LayerHistoryTest, multipleLayers) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ mLayerHistory->setVisibility(testLayer, true);
+ std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
+ mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ mLayerHistory->setVisibility(test30FpsLayer, true);
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
+ mLayerHistory->createLayer("TestLayer2", MAX_REFRESH_RATE);
+ mLayerHistory->setVisibility(testLayer2, true);
+
+ nsecs_t startTime = systemTime();
+ for (int i = 0; i < 10; i++) {
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+
+ startTime = systemTime();
+ for (int i = 0; i < 10; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+
+ for (int i = 10; i < 30; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+
+ // This frame is only around for 9 occurrences, so it doesn't throw
+ // anything off.
+ for (int i = 0; i < 9; i++) {
+ mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ // After 100 ms frames become obsolete.
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ // Insert the 31st frame.
+ mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333), false /*isHDR*/);
+ EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+}
+
+} // namespace
+} // namespace scheduler
+} // 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/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
new file mode 100644
index 0000000..5067fe8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 "DisplayHardware/HWC2.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+
+namespace android {
+namespace scheduler {
+
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+class RefreshRateConfigsTest : public testing::Test {
+protected:
+ static constexpr int CONFIG_ID_60 = 0;
+ static constexpr hwc2_config_t HWC2_CONFIG_ID_60 = 0;
+ static constexpr int CONFIG_ID_90 = 1;
+ static constexpr hwc2_config_t HWC2_CONFIG_ID_90 = 1;
+ static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr int64_t VSYNC_90 = 11111111;
+
+ RefreshRateConfigsTest();
+ ~RefreshRateConfigsTest();
+
+ void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
+ ASSERT_EQ(left.configId, right.configId);
+ ASSERT_EQ(left.name, right.name);
+ ASSERT_EQ(left.fps, right.fps);
+ }
+
+ RefreshRateConfigs mConfigs;
+};
+
+RefreshRateConfigsTest::RefreshRateConfigsTest() {
+ 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());
+}
+
+RefreshRateConfigsTest::~RefreshRateConfigsTest() {
+ 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(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ mConfigs.populate(displayConfigs);
+
+ // We always store a configuration for screen off.
+ const auto& rates = mConfigs.getRefreshRates();
+ ASSERT_EQ(1, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
+
+ RefreshRate expectedConfig =
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
+ assertRatesEqual(expectedConfig, *powerSavingRate->second);
+
+ ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(1, mConfigs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ mConfigs.populate(displayConfigs);
+
+ const auto& rates = mConfigs.getRefreshRates();
+ ASSERT_EQ(2, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+
+ RefreshRate expectedPowerSavingConfig =
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+
+ ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(2, mConfigs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config90.setVsyncPeriod(VSYNC_90);
+ displayConfigs.push_back(config90.build());
+ mConfigs.populate(displayConfigs);
+
+ const auto& rates = mConfigs.getRefreshRates();
+ ASSERT_EQ(3, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_NE(rates.end(), performanceRate);
+
+ RefreshRate expectedPowerSavingConfig =
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+ RefreshRate expectedPerformanceConfig =
+ RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
+ assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+
+ ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ assertRatesEqual(expectedPerformanceConfig,
+ *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
new file mode 100644
index 0000000..411ec61
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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();
+
+ mock::TimeStats mTimeStats;
+ RefreshRateConfigs mRefreshRateConfigs;
+ RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats};
+};
+
+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());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+ mRefreshRateConfigs.populate(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());
+
+ mRefreshRateConfigs.populate(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.setConfigMode(CONFIG_ID_90);
+ 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());
+
+ mRefreshRateConfigs.populate(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.setConfigMode(CONFIG_ID_90);
+ 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/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
new file mode 100644
index 0000000..160f041
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 "RegionSamplingTest"
+
+#include <ui/Transform.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <array>
+#include <limits>
+
+#include "RegionSamplingThread.h"
+
+namespace android {
+
+struct RegionSamplingTest : testing::Test {
+public:
+ static uint32_t constexpr kBlack = 0;
+ static uint32_t constexpr kWhite = std::numeric_limits<uint32_t>::max();
+ static int constexpr kWidth = 98;
+ static int constexpr kStride = 100;
+ static int constexpr kHeight = 29;
+ static int constexpr kOrientation = ui::Transform::ROT_0;
+ std::array<uint32_t, kHeight * kStride> buffer;
+ Rect const whole_area{0, 0, kWidth, kHeight};
+};
+
+TEST_F(RegionSamplingTest, calculate_mean_white) {
+ std::fill(buffer.begin(), buffer.end(), kWhite);
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
+ testing::FloatEq(1.0f));
+}
+
+TEST_F(RegionSamplingTest, calculate_mean_black) {
+ std::fill(buffer.begin(), buffer.end(), kBlack);
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
+ testing::FloatEq(0.0f));
+}
+
+TEST_F(RegionSamplingTest, calculate_mean_partial_region) {
+ auto const halfway_down = kHeight >> 1;
+ auto const half = halfway_down * kStride;
+ Rect const partial_region = {whole_area.left, whole_area.top, whole_area.right,
+ whole_area.top + halfway_down};
+ std::fill(buffer.begin(), buffer.begin() + half, 0);
+ std::fill(buffer.begin() + half, buffer.end(), kWhite);
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, partial_region),
+ testing::FloatEq(0.0f));
+}
+
+TEST_F(RegionSamplingTest, calculate_mean_mixed_values) {
+ std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable {
+ uint32_t const pixel = (n % std::numeric_limits<uint8_t>::max()) << ((n % 3) * CHAR_BIT);
+ n++;
+ return pixel;
+ });
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
+ testing::FloatNear(0.083f, 0.01f));
+}
+
+TEST_F(RegionSamplingTest, bimodal_tiebreaker) {
+ std::generate(buffer.begin(), buffer.end(),
+ [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; });
+ // presently there's no tiebreaking strategy in place, accept either of the means
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
+ testing::AnyOf(testing::FloatEq(1.0), testing::FloatEq(0.0f)));
+}
+
+TEST_F(RegionSamplingTest, bounds_checking) {
+ std::generate(buffer.begin(), buffer.end(),
+ [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; });
+
+ Rect invalid_region{0, 0, 4, kHeight + 1};
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region),
+ testing::Eq(0.0));
+
+ invalid_region = Rect{0, 0, -4, kHeight};
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region),
+ testing::Eq(0.0));
+
+ invalid_region = Rect{3, 0, 2, 0};
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region),
+ testing::Eq(0.0));
+
+ invalid_region = Rect{0, 3, 0, 2};
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region),
+ testing::Eq(0.0));
+}
+
+// workaround for b/133849373
+TEST_F(RegionSamplingTest, orientation_90) {
+ std::generate(buffer.begin(), buffer.end(),
+ [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; });
+
+ Rect tl_region{0, 0, 4, 4};
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
+ tl_region),
+ testing::Eq(1.0));
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
+ tl_region),
+ testing::Eq(1.0));
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
+ tl_region),
+ testing::Eq(0.0));
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
+ tl_region),
+ testing::Eq(0.0));
+
+ Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight};
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
+ br_region),
+ testing::Eq(0.0));
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
+ br_region),
+ testing::Eq(0.0));
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
+ br_region),
+ testing::Eq(1.0));
+ EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
+ br_region),
+ testing::Eq(1.0));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
new file mode 100644
index 0000000..1f8b111
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -0,0 +1,184 @@
+#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()) {}
+ ~MockEventThreadConnection() = default;
+
+ MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
+ MOCK_METHOD1(setVsyncRate, status_t(uint32_t count));
+ MOCK_METHOD0(requestNextVsync, void());
+ };
+
+ scheduler::RefreshRateConfigs mRefreshRateConfigs;
+
+ /**
+ * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
+ * the same.
+ */
+ class MockScheduler : public android::Scheduler {
+ public:
+ MockScheduler(const scheduler::RefreshRateConfigs& refreshRateConfigs,
+ std::unique_ptr<EventThread> eventThread)
+ : Scheduler([](bool) {}, refreshRateConfigs), 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>(mRefreshRateConfigs, 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..c3d2b8d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.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 <gmock/gmock.h>
+
+#include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/Scheduler.h"
+
+namespace android {
+
+class TestableScheduler : public Scheduler {
+public:
+ TestableScheduler(const scheduler::RefreshRateConfigs& refreshRateConfig)
+ : Scheduler([](bool) {}, refreshRateConfig) {}
+
+ // 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());
+ 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..64d34ee 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -16,46 +16,216 @@
#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 "Scheduler/MessageQueue.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)>,
+ const scheduler::RefreshRateConfigs&) 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 +234,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 +259,41 @@
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, SurfaceFlinger::TraverseLayersFunction traverseLayers,
+ ANativeWindowBuffer* buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd) {
+ bool ignored;
+ return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
+ useIdentityTransform, forSystem, outSyncFd,
+ ignored);
+ }
+
+ 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 +302,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 +316,32 @@
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; }
+ auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
@@ -137,30 +349,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 +375,7 @@
auto& mutableIsConnected() { return this->mIsConnected; }
auto& mutableConfigs() { return this->mConfigs; }
+ auto& mutableLayers() { return this->mLayers; }
};
class FakeHwcDisplayInjector {
@@ -178,9 +386,11 @@
static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
+ static constexpr int32_t DEFAULT_POWER_MODE = 2;
- 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 +432,12 @@
return *this;
}
- auto& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
- mPowerAdvisor = powerAdvisor;
+ auto& setPowerMode(int mode) {
+ mPowerMode = mode;
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 +445,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);
@@ -248,18 +456,24 @@
config.setDpiY(mDpiY);
display->mutableConfigs().emplace(mActiveConfig, config.build());
display->mutableIsConnected() = true;
+ display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
- 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;
@@ -267,15 +481,19 @@
int32_t mDpiX = DEFAULT_DPI;
int32_t mDpiY = DEFAULT_DPI;
int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
+ int32_t mPowerMode = DEFAULT_POWER_MODE;
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 +513,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 +568,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..3c7e1da 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,16 @@
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));
};
} // 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%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy 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..12a349d
--- /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_METHOD2(addResyncSample, bool(nsecs_t, bool*));
+ 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..5b5f8e7 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,18 @@
EventThread();
~EventThread() override;
- MOCK_CONST_METHOD0(createEventConnection, sp<BnDisplayEventConnection>());
+ MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback));
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_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
+ 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..1b1c1a7 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,7 +30,8 @@
~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());
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%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
rename 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..b1634a8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.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 "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(miniDump, std::string());
+ 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 1519618..0000000
--- a/services/thermalservice/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-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.bp b/services/thermalservice/aidl/Android.bp
deleted file mode 100644
index f1ec656..0000000
--- a/services/thermalservice/aidl/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-cc_library {
- name: "libthermalservice",
-
- srcs: [
- "android/os/IThermalEventListener.aidl",
- "android/os/IThermalService.aidl",
- "android/os/Temperature.cpp",
- ],
- aidl: {
- local_include_dirs: ["."],
- export_aidl_headers: true,
- },
- export_include_dirs: ["."],
-
- shared_libs: [
- "libbinder",
- "libutils",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wunused",
- "-Wunreachable-code",
- ],
-}
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..afb3004 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -12,45 +12,54 @@
// 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",
+ ],
+}
+
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 dddbc76..1604775 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",
@@ -87,6 +92,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/api/vulkan.api b/vulkan/api/vulkan.api
index 7604c95..76503c8 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -96,7 +96,7 @@
@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME "VK_KHR_win32_surface"
// 11
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 7
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME "VK_ANDROID_native_buffer"
// 12
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index d3e5f0f..23006fa 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -37,7 +37,17 @@
* backwards-compatibility support is temporary, and will likely be removed in
* (along with all gralloc0 support) in a future release.
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 7
+/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+ *
+ * This version of the extension doesn't introduce new types or structs, but is
+ * to accommodate the new struct VkBindImageMemorySwapchainInfoKHR added in
+ * VK_KHR_swapchain spec version 69. When VkBindImageMemorySwapchainInfoKHR is
+ * chained in the pNext chain of VkBindImageMemoryInfo, a VkNativeBufferANDROID
+ * that holds the correct gralloc handle according to the imageIndex specified
+ * in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
+ * pNext chain of VkBindImageMemoryInfo and passed down to the driver.
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
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/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index f04eb03..bdd3573 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -703,6 +703,7 @@
{{Macro "driver.InterceptedExtensions"}}
VK_KHR_get_physical_device_properties2
VK_ANDROID_external_memory_android_hardware_buffer
+VK_KHR_bind_memory2
{{end}}
@@ -750,6 +751,9 @@
{{else if eq $.Name "vkGetInstanceProcAddr"}}true
{{else if eq $.Name "vkGetDeviceProcAddr"}}true
+ {{/* VK_KHR_swapchain v69 requirement */}}
+ {{else if eq $.Name "vkBindImageMemory2"}}true
+ {{else if eq $.Name "vkBindImageMemory2KHR"}}true
{{end}}
{{$ext := GetAnnotation $ "extension"}}
@@ -985,6 +989,10 @@
{{else if eq $.Name "vkGetPhysicalDeviceProperties"}}true
{{else if eq $.Name "vkGetPhysicalDeviceProperties2"}}true
{{else if eq $.Name "vkGetPhysicalDeviceProperties2KHR"}}true
+
+ {{/* VK_KHR_swapchain v69 requirement */}}
+ {{else if eq $.Name "vkBindImageMemory2"}}true
+ {{else if eq $.Name "vkBindImageMemory2KHR"}}true
{{end}}
{{$ext := GetAnnotation $ "extension"}}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index b494bca..23506ba 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) {
@@ -505,6 +537,7 @@
// Extensions we don't need to do anything about at this level
break;
+ case ProcHook::KHR_bind_memory2:
case ProcHook::KHR_incremental_present:
case ProcHook::KHR_shared_presentable_image:
case ProcHook::KHR_swapchain:
@@ -545,6 +578,7 @@
// return now as these extensions do not require HAL support
return;
case ProcHook::EXT_hdr_metadata:
+ case ProcHook::KHR_bind_memory2:
hook_extensions_.set(ext_bit);
break;
case ProcHook::ANDROID_external_memory_android_hardware_buffer:
@@ -821,8 +855,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 +967,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
@@ -945,9 +983,12 @@
memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
- // b/130182551 VK_KHR_SWAPCHAIN_SPEC_VERSION > 68 has structs the
- // loader doesn't handle properly. So drop the spec version to 68.
- prop.specVersion = 68;
+
+ if (prop.specVersion >= 8) {
+ prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+ } else {
+ prop.specVersion = 68;
+ }
}
}
@@ -970,12 +1011,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)
@@ -986,12 +1030,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);
@@ -1002,12 +1048,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;
@@ -1068,8 +1117,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;
@@ -1077,9 +1128,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;
@@ -1116,8 +1169,15 @@
}
VkPhysicalDeviceProperties properties;
+ ATRACE_BEGIN("driver.GetPhysicalDeviceProperties");
instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
&properties);
+ ATRACE_END();
+
+ if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
+ // Log that the app is hitting software Vulkan implementation
+ android::GraphicsEnv::getInstance().setCpuVulkanInUse();
+ }
data->driver_device = dev;
data->driver_version = properties.driverVersion;
@@ -1143,6 +1203,8 @@
VkResult EnumeratePhysicalDevices(VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices) {
+ ATRACE_CALL();
+
const auto& data = GetData(instance);
VkResult result = data.driver.EnumeratePhysicalDevices(
@@ -1159,6 +1221,8 @@
VkInstance instance,
uint32_t* pPhysicalDeviceGroupCount,
VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+ ATRACE_CALL();
+
VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
@@ -1219,6 +1283,8 @@
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue* pQueue) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
data.driver.GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
@@ -1228,6 +1294,8 @@
void GetDeviceQueue2(VkDevice device,
const VkDeviceQueueInfo2* pQueueInfo,
VkQueue* pQueue) {
+ ATRACE_CALL();
+
const auto& data = GetData(device);
data.driver.GetDeviceQueue2(device, pQueueInfo, pQueue);
@@ -1238,6 +1306,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/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index ec98b9f..574c327 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -137,6 +137,15 @@
}
}
+VKAPI_ATTR VkResult checkedBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos) {
+ if (GetData(device).hook_extensions[ProcHook::KHR_bind_memory2]) {
+ return BindImageMemory2KHR(device, bindInfoCount, pBindInfos);
+ } else {
+ Logger(device).Err(device, "VK_KHR_bind_memory2 not enabled. vkBindImageMemory2KHR not executed.");
+ return VK_SUCCESS;
+ }
+}
+
// clang-format on
const ProcHook g_proc_hooks[] = {
@@ -170,6 +179,20 @@
nullptr,
},
{
+ "vkBindImageMemory2",
+ ProcHook::DEVICE,
+ ProcHook::EXTENSION_CORE,
+ reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2),
+ nullptr,
+ },
+ {
+ "vkBindImageMemory2KHR",
+ ProcHook::DEVICE,
+ ProcHook::KHR_bind_memory2,
+ reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2KHR),
+ reinterpret_cast<PFN_vkVoidFunction>(checkedBindImageMemory2KHR),
+ },
+ {
"vkCreateAndroidSurfaceKHR",
ProcHook::INSTANCE,
ProcHook::KHR_android_surface,
@@ -458,6 +481,7 @@
if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
+ if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
// clang-format on
return ProcHook::EXTENSION_UNKNOWN;
}
@@ -517,11 +541,13 @@
INIT_PROC(true, dev, CreateImage);
INIT_PROC(true, dev, DestroyImage);
INIT_PROC(true, dev, AllocateCommandBuffers);
+ INIT_PROC(false, dev, BindImageMemory2);
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
+ INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR);
// clang-format on
return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 14c3aba..3faf6c0 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -46,6 +46,7 @@
KHR_get_surface_capabilities2,
KHR_get_physical_device_properties2,
ANDROID_external_memory_android_hardware_buffer,
+ KHR_bind_memory2,
EXTENSION_CORE, // valid bit
EXTENSION_COUNT,
@@ -85,11 +86,13 @@
PFN_vkCreateImage CreateImage;
PFN_vkDestroyImage DestroyImage;
PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
+ PFN_vkBindImageMemory2 BindImageMemory2;
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
+ PFN_vkBindImageMemory2KHR BindImageMemory2KHR;
// clang-format on
};
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index a7b7a6f..5679412 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
@@ -425,6 +428,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) {
@@ -474,6 +479,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..a8949d3 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-#include <algorithm>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android/hardware/graphics/common/1.0/types.h>
#include <grallocusage/GrallocUsageConversion.h>
#include <log/log.h>
-#include <ui/BufferQueueDefs.h>
#include <sync/sync.h>
-#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
#include <system/window.h>
-#include <android/hardware/graphics/common/1.0/types.h>
+#include <ui/BufferQueueDefs.h>
+#include <utils/StrongPointer.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
#include "driver.h"
@@ -50,6 +55,22 @@
// VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
+int TranslateVulkanToNativeTransform(VkSurfaceTransformFlagBitsKHR transform) {
+ switch (transform) {
+ // TODO: See TODO in TranslateNativeToVulkanTransform
+ case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+ return NATIVE_WINDOW_TRANSFORM_ROT_90;
+ case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+ return NATIVE_WINDOW_TRANSFORM_ROT_180;
+ case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+ return NATIVE_WINDOW_TRANSFORM_ROT_270;
+ case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+ case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
+ default:
+ return 0;
+ }
+}
+
VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) {
// Native and Vulkan transforms are isomorphic, but are represented
// differently. Vulkan transforms are built up of an optional horizontal
@@ -205,10 +226,12 @@
struct Swapchain {
Swapchain(Surface& surface_,
uint32_t num_images_,
- VkPresentModeKHR present_mode)
+ VkPresentModeKHR present_mode,
+ int pre_transform_)
: surface(surface_),
num_images(num_images_),
mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
+ pre_transform(pre_transform_),
frame_timestamps_enabled(false),
shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
@@ -217,10 +240,20 @@
window,
&refresh_duration);
}
+ uint64_t get_refresh_duration()
+ {
+ ANativeWindow* window = surface.window.get();
+ native_window_get_refresh_cycle_duration(
+ window,
+ &refresh_duration);
+ return static_cast<uint64_t>(refresh_duration);
+
+ }
Surface& surface;
uint32_t num_images;
bool mailbox_mode;
+ int pre_transform;
bool frame_timestamps_enabled;
int64_t refresh_duration;
bool shared;
@@ -335,15 +368,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 +449,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 +519,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 +563,8 @@
void DestroySurfaceKHR(VkInstance instance,
VkSurfaceKHR surface_handle,
const VkAllocationCallbacks* allocator) {
+ ATRACE_CALL();
+
Surface* surface = SurfaceFromHandle(surface_handle);
if (!surface)
return;
@@ -548,6 +585,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 +608,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 +629,8 @@
VkPhysicalDevice /*pdev*/,
VkSurfaceKHR surface,
VkSurfaceCapabilitiesKHR* capabilities) {
+ ATRACE_CALL();
+
int err;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -666,6 +704,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 +718,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 +742,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 +782,8 @@
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
VkSurfaceCapabilities2KHR* pSurfaceCapabilities) {
+ ATRACE_CALL();
+
VkResult result = GetPhysicalDeviceSurfaceCapabilitiesKHR(
physicalDevice, pSurfaceInfo->surface,
&pSurfaceCapabilities->surfaceCapabilities);
@@ -769,6 +819,8 @@
const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormat2KHR* pSurfaceFormats) {
+ ATRACE_CALL();
+
if (!pSurfaceFormats) {
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
@@ -800,6 +852,8 @@
VkSurfaceKHR surface,
uint32_t* count,
VkPresentModeKHR* modes) {
+ ATRACE_CALL();
+
int err;
int query_value;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -851,6 +905,8 @@
VkResult GetDeviceGroupPresentCapabilitiesKHR(
VkDevice,
VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
+ ATRACE_CALL();
+
ALOGV_IF(pDeviceGroupPresentCapabilities->sType !=
VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR,
"vkGetDeviceGroupPresentCapabilitiesKHR: invalid "
@@ -873,6 +929,8 @@
VkDevice,
VkSurfaceKHR,
VkDeviceGroupPresentModeFlagsKHR* pModes) {
+ ATRACE_CALL();
+
*pModes = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR;
return VK_SUCCESS;
}
@@ -882,6 +940,8 @@
VkSurfaceKHR surface,
uint32_t* pRectCount,
VkRect2D* pRects) {
+ ATRACE_CALL();
+
if (!pRects) {
*pRectCount = 1;
} else {
@@ -923,6 +983,8 @@
const VkSwapchainCreateInfoKHR* create_info,
const VkAllocationCallbacks* allocator,
VkSwapchainKHR* swapchain_handle) {
+ ATRACE_CALL();
+
int err;
VkResult result = VK_SUCCESS;
@@ -1128,7 +1190,9 @@
}
uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
uint32_t num_images =
- (create_info->minImageCount - 1) + min_undequeued_buffers;
+ (swap_interval ? create_info->minImageCount
+ : std::max(3u, create_info->minImageCount)) -
+ 1 + min_undequeued_buffers;
// Lower layer insists that we have at least two buffers. This is wasteful
// and we'd like to relax it in the shared case, but not all the pieces are
@@ -1147,9 +1211,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 +1223,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;
@@ -1188,9 +1256,9 @@
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!mem)
return VK_ERROR_OUT_OF_HOST_MEMORY;
- Swapchain* swapchain =
- new (mem) Swapchain(surface, num_images, create_info->presentMode);
-
+ Swapchain* swapchain = new (mem)
+ Swapchain(surface, num_images, create_info->presentMode,
+ TranslateVulkanToNativeTransform(create_info->preTransform));
// -- Dequeue all buffers and create a VkImage for each --
// Any failures during or after this must cancel the dequeued buffers.
@@ -1254,8 +1322,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 +1349,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 +1372,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 +1399,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 +1429,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 +1511,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 +1542,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);
@@ -1649,6 +1732,19 @@
ReleaseSwapchainImage(device, window, fence, img);
OrphanSwapchain(device, &swapchain);
}
+ int window_transform_hint;
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+ &window_transform_hint);
+ if (err != 0) {
+ ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+ strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ }
+ if (swapchain.pre_transform != window_transform_hint) {
+ swapchain_result =
+ WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
+ }
} else {
ReleaseSwapchainImage(device, nullptr, fence, img);
swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
@@ -1672,11 +1768,12 @@
VkDevice,
VkSwapchainKHR swapchain_handle,
VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
VkResult result = VK_SUCCESS;
- pDisplayTimingProperties->refreshDuration =
- static_cast<uint64_t>(swapchain.refresh_duration);
+ pDisplayTimingProperties->refreshDuration = swapchain.get_refresh_duration();
return result;
}
@@ -1687,6 +1784,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 +1810,8 @@
VkResult GetSwapchainStatusKHR(
VkDevice,
VkSwapchainKHR swapchain_handle) {
+ ATRACE_CALL();
+
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
VkResult result = VK_SUCCESS;
@@ -1728,6 +1829,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]);
@@ -1760,5 +1862,106 @@
return;
}
+static void InterceptBindImageMemory2(
+ uint32_t bind_info_count,
+ const VkBindImageMemoryInfo* bind_infos,
+ std::vector<VkNativeBufferANDROID>* out_native_buffers,
+ std::vector<VkBindImageMemoryInfo>* out_bind_infos) {
+ out_native_buffers->clear();
+ out_bind_infos->clear();
+
+ if (!bind_info_count)
+ return;
+
+ std::unordered_set<uint32_t> intercepted_indexes;
+
+ for (uint32_t idx = 0; idx < bind_info_count; idx++) {
+ auto info = reinterpret_cast<const VkBindImageMemorySwapchainInfoKHR*>(
+ bind_infos[idx].pNext);
+ while (info &&
+ info->sType !=
+ VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR) {
+ info = reinterpret_cast<const VkBindImageMemorySwapchainInfoKHR*>(
+ info->pNext);
+ }
+
+ if (!info)
+ continue;
+
+ ALOG_ASSERT(info->swapchain != VK_NULL_HANDLE,
+ "swapchain handle must not be NULL");
+ const Swapchain* swapchain = SwapchainFromHandle(info->swapchain);
+ ALOG_ASSERT(
+ info->imageIndex < swapchain->num_images,
+ "imageIndex must be less than the number of images in swapchain");
+
+ ANativeWindowBuffer* buffer =
+ swapchain->images[info->imageIndex].buffer.get();
+ VkNativeBufferANDROID native_buffer = {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+ .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
+#pragma clang diagnostic pop
+ .pNext = bind_infos[idx].pNext,
+ .handle = buffer->handle,
+ .stride = buffer->stride,
+ .format = buffer->format,
+ .usage = int(buffer->usage),
+ };
+ // Reserve enough space to avoid letting re-allocation invalidate the
+ // addresses of the elements inside.
+ out_native_buffers->reserve(bind_info_count);
+ out_native_buffers->emplace_back(native_buffer);
+
+ // Reserve the space now since we know how much is needed now.
+ out_bind_infos->reserve(bind_info_count);
+ out_bind_infos->emplace_back(bind_infos[idx]);
+ out_bind_infos->back().pNext = &out_native_buffers->back();
+
+ intercepted_indexes.insert(idx);
+ }
+
+ if (intercepted_indexes.empty())
+ return;
+
+ for (uint32_t idx = 0; idx < bind_info_count; idx++) {
+ if (intercepted_indexes.count(idx))
+ continue;
+ out_bind_infos->emplace_back(bind_infos[idx]);
+ }
+}
+
+VKAPI_ATTR
+VkResult BindImageMemory2(VkDevice device,
+ uint32_t bindInfoCount,
+ const VkBindImageMemoryInfo* pBindInfos) {
+ ATRACE_CALL();
+
+ // out_native_buffers is for maintaining the lifecycle of the constructed
+ // VkNativeBufferANDROID objects inside InterceptBindImageMemory2.
+ std::vector<VkNativeBufferANDROID> out_native_buffers;
+ std::vector<VkBindImageMemoryInfo> out_bind_infos;
+ InterceptBindImageMemory2(bindInfoCount, pBindInfos, &out_native_buffers,
+ &out_bind_infos);
+ return GetData(device).driver.BindImageMemory2(
+ device, bindInfoCount,
+ out_bind_infos.empty() ? pBindInfos : out_bind_infos.data());
+}
+
+VKAPI_ATTR
+VkResult BindImageMemory2KHR(VkDevice device,
+ uint32_t bindInfoCount,
+ const VkBindImageMemoryInfo* pBindInfos) {
+ ATRACE_CALL();
+
+ std::vector<VkNativeBufferANDROID> out_native_buffers;
+ std::vector<VkBindImageMemoryInfo> out_bind_infos;
+ InterceptBindImageMemory2(bindInfoCount, pBindInfos, &out_native_buffers,
+ &out_bind_infos);
+ return GetData(device).driver.BindImageMemory2KHR(
+ device, bindInfoCount,
+ out_bind_infos.empty() ? pBindInfos : out_bind_infos.data());
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index ed5718c..4912ef1 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -44,6 +44,8 @@
VKAPI_ATTR void SetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pHdrMetadataEXTs);
VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities);
VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats);
+VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
+VKAPI_ATTR VkResult BindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
// clang-format on
} // namespace driver
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index ca93e65..a626e48 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: [